新增rss功能

This commit is contained in:
xingsu1021
2022-09-11 10:52:30 +08:00
parent 62cb674fbb
commit 62f87f62c0
65 changed files with 3903 additions and 114 deletions

View File

@@ -30,6 +30,15 @@ pip install --no-cache-dir -r requirements.txt -i https://pypi.mirrors.ustc.edu.
```
![](https://raw.githubusercontent.com/xingsu1021/pthelper/master/static/screenshot/update.png)
# v2.2.0 说明
```shell
1、新增RSS订阅功能。
2、新增RSS订阅后直接发送TR下载,(QB未测试但已集成)。
3、此版本不支持在线升级使用。
```
![](https://raw.githubusercontent.com/xingsu1021/pthelper/master/static/screenshot/rss.png)
# v2.1.0 说明
```shell

View File

@@ -1170,7 +1170,7 @@ def opencd(site_name, site_name_cn, site_url, site_cookie):
logger.info(data_ocr)
#验证码不足6未跳过
if len(data_ocr) < 6 :
if len(data_ocr) != 6 :
continue
data = {'imagehash': imagehash,
@@ -1246,7 +1246,7 @@ def hdsky(site_name, site_name_cn, site_url, site_cookie):
)
try:
session = requests.session()
#session = requests.session()
#验证码签到执行3次验证
for i in range(3):
@@ -1256,7 +1256,7 @@ def hdsky(site_name, site_name_cn, site_url, site_cookie):
#print('i--------->',i)
return False, "%s(%s) 错误:连续3次验证码失败" % (site_name,site_name_cn)
response = session.post(sign_url, headers=headers, data=data, timeout=10)
response = requests.post(sign_url, headers=headers, data=data, timeout=10)
logger.info(response.text)
if response.status_code == 200:
result = json.loads(response.text)
@@ -1264,7 +1264,7 @@ def hdsky(site_name, site_name_cn, site_url, site_cookie):
code = result['code']
#拼接验证码图片
hdsky_image_url = 'https://hdsky.me/image.php?action=regimage&imagehash=' + code
response = session.get(hdsky_image_url, headers=headers, timeout=10)
response = requests.get(hdsky_image_url, headers=headers, timeout=10)
with open(image_code_name, "wb") as fp:
fp.write(response.content)
@@ -1277,14 +1277,14 @@ def hdsky(site_name, site_name_cn, site_url, site_cookie):
logger.info(data_ocr)
#验证码不足6未跳过
if len(data_ocr) < 6 :
if len(data_ocr) != 6 :
continue
data = {'action': 'showup',
'imagehash': code,
'imagestring': data_ocr
}
response = session.post("https://hdsky.me/showup.php", headers=headers, data=data, timeout=10)
response = requests.post("https://hdsky.me/showup.php", headers=headers, data=data, timeout=10)
logger.info(response.text)
try:
result = json.loads(response.text)

View File

@@ -121,8 +121,6 @@ def timesince(dt, since='', default="just now"):
_STRPTIME_LOCK = threading.Lock()
_GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
_ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"
def now_date():
"""当前时间"""
@@ -155,6 +153,7 @@ def http_to_unixtime(time_string):
HTTP Date形如 `Sat, 05 Dec 2015 11:10:29 GMT` 。
"""
_GMT_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
return to_unixtime(time_string, _GMT_FORMAT)
def cst_to_unixtime(time_string):
@@ -166,6 +165,7 @@ def cst_to_unixtime(time_string):
def iso8601_to_unixtime(time_string):
"""把ISO8601时间字符串(形如,2012-02-24T06:07:48.000Z)转换为UNIX时间,精确到秒。"""
_ISO8601_FORMAT = "%Y-%m-%dT%H:%M:%S.000Z"
return to_unixtime(time_string, _ISO8601_FORMAT)
def str2datetime(st, s_format='%Y-%m-%d %H:%M:%S'):
@@ -249,10 +249,7 @@ def capacity_convert(size, expect='auto', rate=1000):
return expect_size, expect
def filesizeformat(value, baseMB=False):
"""Format the value like a 'human-readable' file size (i.e. 13 KB,
4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega,
giga etc.), if the second parameter is set to `True` the binary
prefixes are (mebi, gibi).
"""将字节转换为 1.00KB 1.00Mb 1.00gb
"""
try:
bytes = float(value)
@@ -271,16 +268,16 @@ def filesizeformat(value, baseMB=False):
if bytes < base:
ret = '%d Bytes' % (bytes)
elif bytes < base * base:
ret = '%d KB' % (bytes / base)
ret = '%.2f KB' % (bytes / base)
elif bytes < base * base * base:
ret = '%d MB' % (bytes / (base * base))
ret = '%.2f MB' % (bytes / (base * base))
elif bytes < base * base * base * base:
if bytes % (base * base * base) == 0:
ret = '%d GB' % (bytes / (base * base * base))
if bytes % (base * base * base) >= 0:
ret = '%.2f GB' % (bytes / (base * base * base))
else:
ret = "%d MB" % (bytes / (base * base))
ret = "%.2f MB" % (bytes / (base * base))
else:
ret = '%.1f TB' % (bytes / (base * base * base * base))
ret = '%.2f TB' % (bytes / (base * base * base * base))
return ret

View File

@@ -6,12 +6,18 @@ from urllib import parse
import os
import logging
from bs4 import BeautifulSoup
import feedparser
from thefuzz import process
from transmission_rpc import Client
import qbittorrentapi
from common.utils import send_email,send_telegram,send_iyuu, send_enwechat
from common.utils import send_email,send_telegram,send_iyuu, send_enwechat, parseUrl
from common.sites_sign import signIngress
from sites.models import SiteConfig, SiteInfo
from .models import Job, Log
from notify.models import NotifyConfig
from rss.models import Rule, SeedInfo
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from django_apscheduler.jobstores import DjangoJobStore, register_job
@@ -96,6 +102,9 @@ def my_scheduler(crontab_id=None, crontab_status=None, hour="*", minute="*", job
elif jobtype_id == 1003:
#重签
scheduler.add_job(re_fail_sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
elif jobtype_id == 1002:
#RSS订阅
scheduler.add_job(rss, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
else:
#禁用状态,直接删除任务
@@ -117,6 +126,8 @@ def my_scheduler(crontab_id=None, crontab_status=None, hour="*", minute="*", job
scheduler.add_job(sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
elif jobtype_id == 1003:
scheduler.add_job(re_fail_sign, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
elif jobtype_id == 1002:
scheduler.add_job(rss, 'cron', hour=hour, minute=minute, id=crontab_id, args=[crontab_id])
return True
@@ -338,4 +349,224 @@ def send_msg(crontab_id = None, send_data = []):
logger.info("-------------enwechat-------------------")
logger.info(msg)
return
#==========================================================================================================
def rss(crontab_id):
"""
执行订阅
参数:
crontab_id:执行任务的ID
"""
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36',
#'cookie': site_cookie,
}
#获取任务ID
job_id = Job.objects.get(crontab_id=crontab_id)
ormdata_rule = Rule.objects.get(job_id = job_id.id)
#订阅地址
rss_url = ormdata_rule.config_id.url
#站点信息
siteinfo_id = ormdata_rule.config_id.siteinfo_id
site_name = siteinfo_id.siteconfig_name
site_name_cn = siteinfo_id.siteconfig_name_cn
#关键字
keyword = [x for x in ormdata_rule.keyword.split(',')]
#码率
rate = ormdata_rule.rate
logger.info("-------------开始RSS订阅任务%s(%s)-------------------" % (site_name, site_name_cn))
d = feedparser.parse(rss_url, request_headers=headers)
#未获取返回状态
if 'status' not in d:
logger.error('请求RSS地址失败')
return
#匹配RSS过滤后发送
send_data = []
if d.status == 200:
#entries 电影内容
for i in d.entries:
#标题 [Movie][Back to 1942 2012 V2 2160p WEB-DL H265 AAC-LeagueWEB][一九四二/温故1942/1942 | 类型: 剧情/历史/战争/灾难 主演: 张国立/张默/徐帆 [国语/中英双语硬字] | *4K* *V2高码*][16.35 GB]
seed_name = i.title
#种子类型 Movie
seed_type = i.tags[0].term
#种子页地址 https://lemonhd.org/details_movie.php?id=327311
seed_link = i.link
#'links': [{'rel': 'alternate', 'type': 'text/html', 'href': 'https://lemonhd.org/details_tv.php?id=328435'},
# {'length': '2543569030', 'type': 'application/x-bittorrent', 'href': 'https://lemonhd.org/download.php?id=328435&passkey=c1c6b3d5291bf330ffa9ad2872d52144',
# 'rel': 'enclosure'}
# ]
#种子下载地址
seed_download_url = i.links[1].href
#种子大小 字节数
seed_length = i.links[1].length
#发布时间 Wed, 07 Sep 2022 14:27:09 +0800
seed_published_time = i.published
_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S %z"
#将字符串转换成datetime在格式化成需要的 2022-09-07 09:26:22
seed_published_time = datetime.datetime.strptime(seed_published_time, _TIME_FORMAT).strftime('%Y-%m-%d %H:%M:%S')
#种子ID afe783da83684e92f5ff6a4c42fd11da216b77a2
seed_hash_id = i.id
seedinfo = None
#确认无重复,则添加
num = SeedInfo.objects.filter(seed_hash_id=seed_hash_id).filter(siteinfo_id=siteinfo_id).count()
if num == 0:
if keyword[0] == '':
#未添加关键字则全部加入
if rate == 'all':
#码率为所有
seedinfo = SeedInfo.objects.create(seed_name = seed_name,
seed_type = seed_type,
seed_details_link = seed_link,
seed_donwload_link = seed_download_url,
seed_file_size = seed_length,
seed_hash_id = seed_hash_id,
seed_published_time = seed_published_time,
siteinfo_id = siteinfo_id,
rule_id = ormdata_rule
)
else:
if rate in seed_name:
seedinfo = SeedInfo.objects.create(seed_name = seed_name,
seed_type = seed_type,
seed_details_link = seed_link,
seed_donwload_link = seed_download_url,
seed_file_size = seed_length,
seed_hash_id = seed_hash_id,
seed_published_time = seed_published_time,
siteinfo_id = siteinfo_id,
rule_id = ormdata_rule
)
#发送下载器
seed_download(seedinfo.id)
#将电影名加入发送
send_data.append(seed_name)
else:
#通过匹配关键字添加
_name, num=process.extractOne(seed_name, keyword)
if num >= 50:
#匹配度大于50添加
if rate == 'all':
#码率为所有
seedinfo = SeedInfo.objects.create(seed_name = seed_name,
seed_type = seed_type,
seed_details_link = seed_link,
seed_donwload_link = seed_download_url,
seed_file_size = seed_length,
seed_hash_id = seed_hash_id,
seed_published_time = seed_published_time,
siteinfo_id = siteinfo_id,
rule_id = ormdata_rule
)
else:
if rate in seed_name:
seedinfo = SeedInfo.objects.create(seed_name = seed_name,
seed_type = seed_type,
seed_details_link = seed_link,
seed_donwload_link = seed_download_url,
seed_file_size = seed_length,
seed_hash_id = seed_hash_id,
seed_published_time = seed_published_time,
siteinfo_id = siteinfo_id,
rule_id = ormdata_rule
)
#发送下载器
seed_download(seedinfo.id)
#将电影名加入发送
send_data.append(seed_name)
#发送通知
send_msg(crontab_id,send_data)
return
def seed_download(seedinfo_id):
"""
下载种子
参数
seedinfo_id rss订阅后存入数据库的id
"""
ormdata_seedinfo = SeedInfo.objects.get(id = seedinfo_id)
#下载连接
seed_donwload_link = ormdata_seedinfo.seed_donwload_link
#下载器类型
download_type = ormdata_seedinfo.rule_id.tools_id.typed
download_username = ormdata_seedinfo.rule_id.tools_id.username
download_password = ormdata_seedinfo.rule_id.tools_id.password
download_url = ormdata_seedinfo.rule_id.tools_id.url
#下载目录
download_dirname = ormdata_seedinfo.rule_id.tools_id.dirname
#加入下载器后是否立刻下载
is_paused = ormdata_seedinfo.rule_id.is_paused
if download_dirname == "":
download_dirname = None
url_data = parseUrl(download_url)
#保存加入下载器后返回的ID
torrent_id = None
if download_type == 'tr':
try:
c = Client(protocol= url_data['protocol'],
host=url_data['host'],
port=url_data['port'],
username=download_username,
password=download_password,
timeout = 10
)
torrent = c.add_torrent(seed_donwload_link,
timeout = 10,
download_dir = download_dirname,
#paused = is_paused, #立刻下载,需要下载器开启默认下载才有效
)
#获取种子id
torrent_id = torrent.id
#如果启动则下载
if is_paused:
c.start_torrent(torrent_id)
ormdata_seedinfo.seed_torrent_id = torrent_id
ormdata_seedinfo.seed_status = True
ormdata_seedinfo.save()
except Exception as e:
print(e)
elif download_type == 'qb':
try:
c = qbittorrentapi.Client(
host=url_data['host'],
port=url_data['port'],
username=download_username,
password=download_password,
)
torrent = c.torrents_add(seed_donwload_link,
save_path=download_dirname,
#is_paused=is_paused, #立刻下载
)
#获取种子id
torrent_id = torrent.id
#如果启动则下载
if is_paused:
c.start_torrent(torrent_id)
ormdata_seedinfo.seed_torrent_id = torrent_id
ormdata_seedinfo.seed_status = True
ormdata_seedinfo.save()
except Exception as e:
print(e)
return

View File

@@ -545,14 +545,14 @@ class SignJobView(LoginRequiredMixin,TemplateView):
name = "签到"
#未配置消息通知不予通过
if len(notifys) == 0:
response_data={"code":0,"msg":"请配置消息通知!"}
return JsonResponse(response_data)
#if len(notifys) == 0:
#response_data={"code":0,"msg":"请配置消息通知!"}
#return JsonResponse(response_data)
data_count = Job.objects.filter(jobtype_id=1000).count()
if data_count == 0:
ormdata = Job.objects.create(name=name,
crontab_status=crontab_status,
crontab_status=int(crontab_status),
hour=hour,
minute=minute,
jobtype_id=jobtype_id,
@@ -563,7 +563,7 @@ class SignJobView(LoginRequiredMixin,TemplateView):
)
else:
ormdata = Job.objects.get(id=_id)
ormdata.crontab_status = crontab_status
ormdata.crontab_status = int(crontab_status)
ormdata.hour = hour
ormdata.minute = minute
ormdata.crontab_time = crontab_time

View File

3
apps/download/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps/download/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class DownloadConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'download'

View File

@@ -0,0 +1,26 @@
# Generated by Django 4.1 on 2022-09-08 14:02
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Tools',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=30, verbose_name='下载器名称')),
('typed', models.CharField(max_length=30, verbose_name='下载器类型tr,qb')),
('url', models.URLField(blank=True, verbose_name='下载器地址')),
('username', models.CharField(max_length=50, verbose_name='下载器用户名')),
('password', models.CharField(max_length=128, verbose_name='下载器密码')),
('dirname', models.CharField(max_length=150, null=True, verbose_name='默认下载目录')),
],
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1 on 2022-09-08 17:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('download', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='tools',
name='name',
field=models.CharField(max_length=30, unique=True, verbose_name='下载器名称'),
),
]

View File

12
apps/download/models.py Normal file
View File

@@ -0,0 +1,12 @@
from django.db import models
# Create your models here.
class Tools(models.Model):
"""下载器"""
name = models.CharField('下载器名称', max_length=30, unique=True)
typed = models.CharField('下载器类型tr,qb', max_length=30)
url = models.URLField('下载器地址', blank=True)
username = models.CharField('下载器用户名', max_length=50)
password = models.CharField('下载器密码', max_length=128)
dirname = models.CharField('默认下载目录', max_length=150, null=True)

View File

@@ -0,0 +1,235 @@
<style>
#toolsTable + .layui-table-view .layui-table-body tbody > tr > td {
padding: 0;
}
#toolsTable + .layui-table-view .layui-table-body tbody > tr > td > .layui-table-cell {
height: 48px;
line-height: 48px;
}
.tb-img-circle {
width: 40px;
height: 40px;
cursor: zoom-in;
border-radius: 50%;
border: 2px solid #dddddd;
}
</style>
<!-- 正文开始 -->
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline searchInput">
<input id="toolsSearchValue" class="layui-input" type="text" placeholder="输入关键字" />
</div>
<div class="layui-inline">
<button id="toolsBtnSearch" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>
<button id="toolsBtnAdd" class="layui-btn icon-btn"><i
class="layui-icon">&#xe654;</i>添加</button>
<button id="toolsBtnDel" class="layui-btn layui-btn-danger icon-btn"><i
class="layui-icon">&#xe640;</i>删除</button>
</button>
</div>
</div>
</div>
<table class="layui-table" id="toolsTable" lay-filter="toolsTable"></table>
</div>
</div>
</div>
<!-- 表格操作列 -->
<script type="text/html" id="toolsTableBar">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown', 'upload'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
var xmSelect = layui.xmSelect;
var upload = layui.upload;
form.render('select');
// 渲染表格
var insTb = tableX.render({
elem: '#toolsTable',
url: setter.base_server + 'download/tools/list',
method: 'GET',
cellMinWidth: 100,
page: true,
limit: 15,
limits: [15, 50, 100],
cols: [[
{ type: 'checkbox' },
{ field: 'id', title: 'ID', hide: true },
{ field: 'name', sort: true, title: '名称', width: 200 },
{ field: 'typed', title: '类型', templet: function (d) {
if (d.typed === 'tr') {
return '<img src="static/assets/images/tr.png" class="tb-img-circle" tb-img alt=""/>';
}else{
return '<img src="static/assets/images/qb.png" class="tb-img-circle" tb-img alt=""/>';
}
}, align: 'center', width: 90, unresize: true
},
{ field: 'url', sort: true, title: '地址' },
{ field: 'username', sort: true, title: '用户名', width: 150 },
{ field: 'password', sort: true, title: '密码' , hide: true},
{ field: 'dirname', sort: true, title: '下载目录' },
{ align: 'center', toolbar: '#toolsTableBar', title: '操作', width: 150 }
]]
});
// 添加按钮点击事件
$('#toolsBtnAdd').click(function () {
showAddModel();
});
// 工具条点击事件
table.on('tool(toolsTable)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') {
//var csrftoken = getCookie('csrftoken');
layer.confirm('确定删除此记录 ' + data.name + '?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
$.ajax({
url: "download/tools/del",
type: 'POST',
dataType: "json",
data: { 'ids': [data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
// beforeSend: function(request) {
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
// request.setRequestHeader("X-CSRFToken", csrftoken);
// },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('toolsTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
//显示表单弹窗
var showAddModel = function () {
var title = '添加下载器';
admin.open({
title: title,
area: ['600px', '500px'],
url: 'download/tools/add',
success: function (layero, index) {
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
}
});
};
var showEditModel = function (data) {
var title = '编辑下载器';
admin.open({
title: title,
area: ['600px', '500px'],
url: 'download/tools/edit?id=' + data.id,
success: function (layero, index) {
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
}
});
};
// 批量删除按钮点击事件
$('#toolsBtnDel').click(function () {
//选中事件
var checkStatus = table.checkStatus('toolsTable')
//得到选择长度
dataLength = checkStatus.data.length
data = checkStatus.data;
var ids = [];
if (dataLength == 0) {
//未选中
layer.msg("请选择需要删除的记录", { icon: 2 });
} else {
layer.confirm('确定删除选中的' + dataLength + '个记录?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
for (var i = 0; i < data.length; i++) {
ids.push(data[i].id);
}
$.ajax({
url: "download/tools/del",
type: 'POST',
dataType: "json",
data: { 'ids': ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('toolsTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
// 监听enter键,光标在搜索框
$('#toolsSearchValue').keydown(function (event) {
if (event.keyCode === 13) {
var key = 'search';
var value = $('#toolsSearchValue').val();
table.reload('toolsTable', { where: { searchKey: key, searchValue: value } });
} else if (event.keyCode === 8 && !$psw.val()) {
$("#toolsSearchValue").val('');
ptable();
if (event.preventDefault) event.preventDefault();
if (event.returnValue) event.returnValue = false;
}
});
// 搜索按钮点击事件
$('#toolsBtnSearch').click(function () {
var key = 'search';
var value = $('#toolsSearchValue').val();
table.reload('toolsTable', { where: { searchKey: key, searchValue: value } });
});
});
</script>

View File

@@ -0,0 +1,112 @@
<!-- 表单弹窗 -->
<form id="toolsAddForm" lay-filter="toolsAddForm" class="layui-form model-form" method="POST">{% csrf_token %}
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input name="name" type="text" class="layui-input" lay-verify="required" required />
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">类型</label>
<div class="layui-input-block">
<div id="typed"></div>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">地址</label>
<div class="layui-input-block">
<input name="url" type="text" class="layui-input" lay-verify="required|urlX" required placeholder="如:http://1.1.1.1:9091" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input name="username" type="text" class="layui-input" lay-verify="required" required />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input name="password" type="password" class="layui-input" lay-verify="required" required />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">下载目录</label>
<div class="layui-input-block">
<input name="dirname" type="text" class="layui-input" />
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-normal" lay-filter="toolsTestForm-submit" lay-submit>测试连接</button>&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
<button class="layui-btn" lay-filter="toolsAddForm-submit" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'formX', 'dropdown'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
form.render('select', 'toolsAddForm');
// 渲染多选下拉框
var insSel = xmSelect.render({
el: '#typed',
name: 'typed',
layVerify: 'required',
radio: true,
clickClose: true,
data: [
{name: 'Transmission', value: 'tr'},
{name: 'qBittorrent', value: 'qb'},
]
});
form.on('submit(toolsTestForm-submit)', function (data) {
layer.load(2);
admin.req('download/tools/test', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'POST');
return false;
});
form.on('submit(toolsAddForm-submit)', function (data) {
layer.load(2);
admin.req('download/tools/add', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
table.reload('toolsTable', {});
admin.closeDialog('#toolsAddForm');
} else {
layer.msg(data.msg, {icon: 2});
}
}, $('#toolsAddForm').attr('method'));
return false;
});
});
</script>

View File

@@ -0,0 +1,140 @@
<style type="text/css">
#img {
width: 25px;
height: 15px;
position: absolute;
right: 5px;
margin-top: 12px;
top: 1px;
text-align: center;
}
</style>
<!-- 表单弹窗 -->
<form id="toolsEditForm" lay-filter="toolsEditForm" class="layui-form model-form" method="POST">{% csrf_token %}
<input name="id" type="hidden" value="{{id}}"/>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input name="name" type="text" class="layui-input" lay-verify="required" required value="{{name}}" />
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">类型</label>
<div class="layui-input-block">
<div id="typed"></div>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">地址</label>
<div class="layui-input-block">
<input name="url" type="text" class="layui-input" lay-verify="required|urlX" required value="{{url}}"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">用户名</label>
<div class="layui-input-block">
<input name="username" type="text" class="layui-input" lay-verify="required" required value="{{username}}"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码</label>
<div class="layui-input-block">
<input name="password" id="password" type="password" class="layui-input" lay-verify="required" required value="{{password}}" onfocus="this.placeholder=''"
onblur="this.placeholder='密码'"/>
<!--在输入框后接img标签-->
<img id="img" onclick="hideShowPsw()" src="static/assets/images/icon-visible.png">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">下载目录</label>
<div class="layui-input-block">
<input name="dirname" type="text" class="layui-input" value="{{dirname}}"/>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-normal" lay-filter="toolsTestForm-submit" lay-submit>测试连接</button>&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
<button class="layui-btn" lay-filter="toolsEditForm-submit" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'xmSelect', 'table', 'admin','formX'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var xmSelect = layui.xmSelect;
var table = layui.table;
var admin = layui.admin;
var formX = layui.formX;
form.render('select',"toolsEditForm");
// 渲染多选下拉框
var insSel = xmSelect.render({
el: '#typed',
name: 'typed',
layVerify: 'required',
radio: true,
clickClose: true,
data: [
{name: 'Transmission', value: 'tr'},
{name: 'qBittorrent', value: 'qb'},
]
});
insSel.setValue(["{{typed}}"]);
form.on('submit(toolsTestForm-submit)', function (data) {
layer.load(2);
admin.req('download/tools/test', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
} else {
layer.msg(data.msg, {icon: 2});
}
}, 'POST');
return false;
});
form.on('submit(toolsEditForm-submit)', function (data) {
layer.load(2);
admin.req('download/tools/edit', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
table.reload('toolsTable', {});
admin.closeDialog('#toolsEditForm');
} else {
layer.msg(data.msg, {icon: 2});
}
}, $('#toolsEditForm').attr('method'));
return false;
});
});
//隐藏text block,显示password block
var demoImg = document.getElementById("img");
var PWD = document.getElementById("password");
function hideShowPsw() {
if (PWD.type == "password") {
PWD.type = "text";
demoImg.src = "static/assets/images/icon-invisible.png"; //图片路径(闭眼图片)
} else {
PWD.type = "password";
demoImg.src = "static/assets/images/icon-visible.png"; // 图片路径(睁眼图片)
}
}
</script>

3
apps/download/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,21 @@
# coding:utf-8
from django.urls import path
from .. import views, views_request
app_name = 'download'
urlpatterns = [
# Resource asset path
#下载器配置
path('tools', views.ToolsListView.as_view()),
path('tools/add', views.ToolsAddView.as_view()),
path('tools/edit', views.ToolsEditView.as_view()),
path('tools/del', views.ToolsDelView.as_view()),
path('tools/list', views_request.tools),
path('tools/select/list', views_request.select_tools),
path('tools/test', views_request.tools_test),
]

165
apps/download/views.py Normal file
View File

@@ -0,0 +1,165 @@
from django.shortcuts import render
from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse
from .models import Tools
# Create your views here.
class ToolsListView(LoginRequiredMixin,TemplateView):
"""
显示
"""
template_name = 'download/tools.html'
def get(self, request, *args, **kwargs):
"""
"""
return super(ToolsListView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
# 'data': self.ormdata_game
}
kwargs.update(context)
return super(ToolsListView, self).get_context_data(**kwargs)
class ToolsAddView(LoginRequiredMixin,TemplateView):
"""
添加
"""
template_name = 'download/toolsadd.html'
#显示添加模板
def get(self, request, *args, **kwargs):
"""
"""
return super(ToolsAddView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
}
kwargs.update(context)
return super(ToolsAddView, self).get_context_data(**kwargs)
#数据提交接收方法
def post(self, request, *args, **kwargs):
"""
数据提交
"""
name = request.POST.get("name").strip()
url = request.POST.get("url").strip()
typed = request.POST.get("typed").strip()
username = request.POST.get("username").strip()
password = request.POST.get("password").strip()
dirname = request.POST.get("dirname").strip()
ormdata = Tools.objects.create(name=name,
url=url,
typed=typed,
username=username,
password=password,
dirname=dirname
)
ormdata.save()
response_data={"code":1,"msg":"添加成功"}
return JsonResponse(response_data)
class ToolsEditView(LoginRequiredMixin,TemplateView):
"""
编辑域名
"""
template_name = 'download/toolsedit.html'
#显示添加模板
def get(self, request, *args, **kwargs):
"""
得到
"""
#记录ID
self._id = request.GET.get('id')
self.ormdata = Tools.objects.get(id=self._id)
return super(ToolsEditView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
'id': self._id,
'url': self.ormdata.url,
'name': self.ormdata.name,
'typed': self.ormdata.typed,
'username': self.ormdata.username,
'password': self.ormdata.password,
'dirname': self.ormdata.dirname
}
kwargs.update(context)
return super(ToolsEditView, self).get_context_data(**kwargs)
#数据提交接收方法
def post(self, request, *args, **kwargs):
"""
数据提交
"""
url = request.POST.get("url").strip()
name = request.POST.get("name").strip()
typed = request.POST.get("typed").strip()
username = request.POST.get("username").strip()
password = request.POST.get("password").strip()
dirname = request.POST.get("dirname").strip()
_id = request.POST.get('id')
ormdata = Tools.objects.get(id=_id)
ormdata.url = url
ormdata.name=name
ormdata.typed = typed
ormdata.username = username
ormdata.password = password
ormdata.dirname = dirname
ormdata.save()
response_data={"code":1,"msg":"添加成功"}
return JsonResponse(response_data)
class ToolsDelView(LoginRequiredMixin,TemplateView):
"""
删除
"""
def post(self, request, *args, **kwargs):
"""
数据提交
"""
#得到批量或者单个要删除的id
ids = request.POST.getlist("ids[]")
#print("ids====>",ids)
Tools.objects.filter(id__in=ids).delete()
#Site.objects.filter(id=i).delete()
response_data={"code":1,"msg":"操作成功"}
return JsonResponse(response_data)
#==================

View File

@@ -0,0 +1,158 @@
#coding=utf-8
from django.http import JsonResponse, StreamingHttpResponse,HttpResponse
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from transmission_rpc import Client
import qbittorrentapi
from .models import Tools
from common.utils import parseUrl
#直接使用检查是否管理员
#from django.contrib.auth.decorators import user_passes_test
#@user_passes_test(lambda u: u.is_superuser)
@login_required
def tools(request):
"""
提供相应的数据
"""
#得到偏移量
pageIndex= int(request.GET.get('page',1))
#得到查询个数
pageSize = int(request.GET.get('limit',10))
#搜索字段
searchKey = request.GET.get('searchKey','')
searchValue = request.GET.get('searchValue','')
#去掉空格
searchValue = searchValue.strip()
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
if searchValue == '':
data['count'] = Tools.objects.count()
if pageIndex == 1:
ormdata = Tools.objects.order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = Tools.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
else:
data['count'] = Tools.objects.filter(Q(name__icontains=searchValue)|Q(typed__icontains=searchValue)).count()
if pageIndex == 1:
ormdata = Tools.objects.filter(Q(name__icontains=searchValue)|Q(typed__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = Tools.objects.filter(Q(name__icontains=searchValue)|Q(typed__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
for i in ormdata:
data['data'].append({"id":i.id,
"name":i.name,
"typed":i.typed,
"username":i.username,
"url":i.url,
"password":i.password,
"dirname":i.dirname,
})
#返回json串
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
return JsonResponse(data)
#==================
@login_required
def tools_test(request):
"""
测试下载器配置连通性
"""
url = request.POST.get("url").strip()
typed = request.POST.get("typed").strip()
username = request.POST.get("username").strip()
password = request.POST.get("password").strip()
url_data = parseUrl(url)
if typed == 'tr':
try:
c = Client(protocol= url_data['protocol'],
host=url_data['host'],
port=url_data['port'],
username=username,
password=password,
timeout = 10
)
response_data={"code":1,"msg": "连接成功" }
except:
response_data={"code":0,"msg": "连接失败" }
elif typed == 'qb':
c = qbittorrentapi.Client(
host=url_data['host'],
port=url_data['port'],
username=username,
password=password,
)
try:
c.auth_log_in()
response_data={"code":1,"msg": "连接成功" }
except:
response_data={"code":0,"msg": "连接失败" }
return JsonResponse(response_data)
#==================
@login_required
def select_tools(request):
"""
RSS规则
xmSelect
"""
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
#获取所有记录
data['count'] = Tools.objects.count()
ormdata = Tools.objects.order_by(order_by).all()
for i in ormdata:
data['data'].append({
"id":i.id,
"name":i.name
})
return JsonResponse(data)

View File

@@ -1,16 +1,17 @@
from myauth.models import User
from django.contrib.auth.hashers import make_password
from sites.models import SiteConfig
from sites.models import SiteConfig, SiteInfo
from cron.models import JobType
from notify.models import MailType
from rss.models import FilmCategory, FilmType
def init_datas():
"""
初始化数据库
"""
print('开始初始化用户数据库...')
if User.objects.count() == 0:
#添加管理员
User.objects.create(name='admin',
@@ -24,7 +25,7 @@ def init_datas():
print('初始化管理员成功.')
else:
print("管理员已经存在,忽略...")
print('开始初始化站点配置...')
sites_data = [{'name':'hdfans','name_cn':'红豆饭','index_url':'https://hdfans.org','sign_type':'general'},
{'name':'1ptba','name_cn':'壹PT吧','index_url':'https://1ptba.com','sign_type':'general'},
@@ -78,7 +79,7 @@ def init_datas():
{'name':'wintersakura','name_cn':'冬樱','index_url':'https://wintersakura.net','sign_type':'general'},
{'name':'hdpt','name_cn':'明教','index_url':'https://hdpt.xyz','sign_type':'general'},
]
for site in sites_data:
get_site = SiteConfig.objects.filter(name=site['name']).count()
if get_site == 0:
@@ -91,18 +92,25 @@ def init_datas():
#else:
##更新站点配置
#SiteConfig.objects.filter(name=site['name']).update(sign_type = site['sign_type'])
#print('站点 [%s] 更新成功' % site['name'])
##print('站点 [%s] 已经存在,忽略...' % site['name'])
print('开始修复站点信息中文名称...')
ormdata_siteinfo = SiteInfo.objects.filter(siteconfig_name_cn='')
for siteinfo in ormdata_siteinfo:
siteconfig_name_cn = list(SiteConfig.objects.filter(name=siteinfo.siteconfig_name).values_list('name_cn',flat=True))
SiteInfo.objects.filter(siteconfig_name=siteinfo.siteconfig_name).update(siteconfig_name_cn = siteconfig_name_cn[0])
print('开始初始化任务类型...')
jobtypes_data = [{'name':'签到','type_id':1000},
{'name':'辅种','type_id':1001},
{'name':'刷流','type_id':1002},
{'name':'RSS订阅','type_id':1002},
{'name':'签到重试','type_id':1003},
]
for jobtype in jobtypes_data:
get_jobtype = JobType.objects.filter(name=jobtype['name']).count()
if get_jobtype == 0:
@@ -111,14 +119,16 @@ def init_datas():
)
print('添加任务类型 [%s] 成功' % jobtype['name'])
else:
#将原刷流更新为RSS订阅
JobType.objects.filter(type_id=1002).update(name = 'RSS订阅')
print('任务类型 [%s] 已经存在,忽略...' % jobtype['name'])
print('开始初始化通知邮箱类型...')
mailtypes_data = [{'name':'QQ邮箱','alias_name':'qq','smtp_server':'smtp.qq.com','smtp_port':465},
{'name':'新浪邮箱(非vip)','alias_name':'sina','smtp_server':'smtp.sina.com','smtp_port':465},
{'name':'网易163邮箱','alias_name':'163','smtp_server':'smtp.163.com','smtp_port':465},
{'name':'新浪邮箱(非vip)','alias_name':'sina','smtp_server':'smtp.sina.com','smtp_port':465},
{'name':'网易163邮箱','alias_name':'163','smtp_server':'smtp.163.com','smtp_port':465},
]
for mailtype in mailtypes_data:
get_mailtype = MailType.objects.filter(name=mailtype['name']).count()
if get_mailtype == 0:
@@ -130,6 +140,62 @@ def init_datas():
print('添加邮箱类型 [%s] 成功' % mailtype['name'])
else:
print('邮箱类型 [%s] 已经存在,忽略...' % mailtype['name'])
filmcategorys_data = [{'name_en':'Movies','name_cn':'电影'},
{'name_en':'TV Series','name_cn':'电视剧'},
{'name_en':'Docs','name_cn':'纪录片'},
{'name_en':'Animations','name_cn':'动画片'},
{'name_en':'TV Shows','name_cn':'综艺'},
{'name_en':'Sports','name_cn':'体育'},
{'name_en':'MV','name_cn':'音乐视频'},
{'name_en':'Music','name_cn':'音乐'},
{'name_en':'Others','name_cn':'其他'},
]
for film_category in filmcategorys_data:
get_film_category = FilmCategory.objects.filter(name_en=film_category['name_en']).count()
if get_film_category == 0:
FilmCategory.objects.create(name_en = film_category['name_en'],
name_cn = film_category['name_cn']
)
print('添加影视类别 [%s] 成功' % film_category['name_en'])
else:
print('影视类别 [%s] 已经存在,忽略...' % film_category['name_en'])
filmtype_data = [{'name':'喜剧'},
{'name':'爱情'},
{'name':'动作'},
{'name':'科幻'},
{'name':'动画'},
{'name':'悬疑'},
{'name':'犯罪'},
{'name':'惊悚'},
{'name':'冒险'},
{'name':'音乐'},
{'name':'历史'},
{'name':'奇幻'},
{'name':'恐怖'},
{'name':'战争'},
{'name':'传记'},
{'name':'歌舞'},
{'name':'武侠'},
{'name':'情色'},
{'name':'灾难'},
{'name':'西部'},
{'name':'纪录片'},
{'name':'短片'},
{'name':'真人秀'},
{'name':'脱口秀'},
{'name':'古装'},
{'name':'家庭'},
{'name':'剧情'},
]
for film_type in filmtype_data:
get_film_type = FilmType.objects.filter(name=film_type['name']).count()
if get_film_type == 0:
FilmType.objects.create(name = film_type['name'])
print('添加影视类型 [%s] 成功' % film_type['name'])
else:
print('影视类型 [%s] 已经存在,忽略...' % film_type['name'])
print('初始化成功.')

View File

@@ -23,37 +23,37 @@ def iyuutest(request):
"""
_id = request.POST.get('id','')
if _id == '':
iyuu_key = request.POST.get('iyuu_key').strip()
#if _id == '':
response_data={"code":0,"msg":"请先配置IYUU令牌或刷新页面"}
#response_data={"code":0,"msg":"请先配置IYUU令牌或刷新页面"}
#else:
#ormdata = NotifyConfig.objects.get(id=_id)
#iyuu_key = ormdata.iyuu_key
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
now = datetime.datetime.now()
time = now.strftime("%Y-%m-%d %H:%M:%S")
headers = {
'user_agent': user_agent,
'Content-type': 'application/x-www-form-urlencoded'
}
now_time = parse.quote('<center><b><font color="#55a7e3">') + parse.unquote(time) + parse.quote('</font></b></center><br>')
send_txts = parse.quote('<center><b><font color="#4CAF50">[测试]</font></b></center><br>')
api = 'https://iyuu.cn/' + iyuu_key + '.send'
sen_url = api + '?text='+ parse.quote('测试提示') + '&desp=' + now_time + send_txts
response = requests.get(sen_url, headers=headers ,verify=False)
response_msg = json.loads(response.text)
if response.status_code == 200 and response_msg['errcode'] == 0:
response_data={"code":1,"msg":"发送测试成功"}
else:
ormdata = NotifyConfig.objects.get(id=_id)
iyuu_key = ormdata.iyuu_key
user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36'
now = datetime.datetime.now()
time = now.strftime("%Y-%m-%d %H:%M:%S")
headers = {
'user_agent': user_agent,
'Content-type': 'application/x-www-form-urlencoded'
}
now_time = parse.quote('<center><b><font color="#55a7e3">') + parse.unquote(time) + parse.quote('</font></b></center><br>')
send_txts = parse.quote('<center><b><font color="#4CAF50">[测试]</font></b></center><br>')
api = 'https://iyuu.cn/' + iyuu_key + '.send'
sen_url = api + '?text='+ parse.quote('测试提示') + '&desp=' + now_time + send_txts
response = requests.get(sen_url, headers=headers ,verify=False)
response_msg = json.loads(response.text)
if response.status_code == 200 and response_msg['errcode'] == 0:
response_data={"code":1,"msg":"发送测试成功"}
else:
response_data={"code":0,"msg":"发送测试失败,%s" % response_msg['errmsg'] }
response_data={"code":0,"msg":"发送测试失败,%s" % response_msg['errmsg'] }
return JsonResponse(response_data)

0
apps/rss/__init__.py Normal file
View File

3
apps/rss/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps/rss/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class RSSConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'rss'

View File

@@ -0,0 +1,40 @@
# Generated by Django 4.1 on 2022-09-08 09:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('sites', '0014_alter_siteinfo_siteconfig_name_cn'),
]
operations = [
migrations.CreateModel(
name='FilmCategory',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name_en', models.CharField(max_length=20, verbose_name='英文名')),
('name_cn', models.CharField(max_length=20, verbose_name='中文名')),
],
),
migrations.CreateModel(
name='FilmType',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, verbose_name='名称')),
],
),
migrations.CreateModel(
name='Config',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.URLField(blank=True, verbose_name='RSS地址')),
('refresh_time', models.IntegerField(verbose_name='刷新间隔分钟')),
('siteinfo_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='siteinfo', to='sites.siteinfo')),
],
),
]

View File

@@ -0,0 +1,25 @@
# Generated by Django 4.1 on 2022-09-08 17:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('download', '0002_alter_tools_name'),
('rss', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Rule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='规则名称')),
('keyword', models.CharField(max_length=100, null=True, verbose_name='关键字,使用逗号分隔')),
('config_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='config', to='rss.config')),
('tools_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tools', to='download.tools')),
],
),
]

View File

@@ -0,0 +1,23 @@
# Generated by Django 4.1 on 2022-09-08 19:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0002_rule'),
]
operations = [
migrations.AddField(
model_name='rule',
name='status',
field=models.BooleanField(default=False, verbose_name='状态启用,禁用'),
),
migrations.AlterField(
model_name='rule',
name='name',
field=models.CharField(max_length=50, unique=True, verbose_name='规则名称'),
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 4.1 on 2022-09-09 13:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0003_rule_status_alter_rule_name'),
]
operations = [
migrations.RemoveField(
model_name='config',
name='refresh_time',
),
migrations.AddField(
model_name='rule',
name='refresh_time',
field=models.IntegerField(default=5, verbose_name='刷新间隔分钟'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.1 on 2022-09-09 14:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0004_remove_config_refresh_time_rule_refresh_time'),
]
operations = [
migrations.AddField(
model_name='rule',
name='crontab_id',
field=models.CharField(max_length=64, null=True, verbose_name='执行ID'),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 4.1 on 2022-09-09 17:03
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('cron', '0013_alter_job_sites'),
('rss', '0005_rule_crontab_id'),
]
operations = [
migrations.RemoveField(
model_name='rule',
name='crontab_id',
),
migrations.AddField(
model_name='rule',
name='job_id',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='job', to='cron.job'),
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 4.0.3 on 2022-09-10 10:36
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('sites', '0014_alter_siteinfo_siteconfig_name_cn'),
('rss', '0006_remove_rule_crontab_id_rule_job_id'),
]
operations = [
migrations.CreateModel(
name='SeedInfo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('seed_name', models.CharField(max_length=500, verbose_name='种子名称')),
('seed_type', models.CharField(max_length=50, verbose_name='种子类型')),
('seed_details_link', models.URLField(blank=True, verbose_name='种子详情地址')),
('seed_published_time', models.DateField(verbose_name='发布时间')),
('seed_hash_id', models.CharField(max_length=50, verbose_name='种子hash值')),
('seed_donwload_link', models.URLField(blank=True, verbose_name='种子下载地址')),
('seed_file_size', models.IntegerField(verbose_name='种子文件大小')),
('seed_status', models.BooleanField(default=False, verbose_name='状态:下载,未下载')),
('photos', models.URLField(blank=True, verbose_name='海报')),
('douban_link', models.URLField(blank=True, verbose_name='豆瓣电影地址')),
('imdb_link', models.URLField(blank=True, verbose_name='IMDB电影地址')),
('siteinfo_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.siteinfo')),
],
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 11:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0007_seedinfo'),
]
operations = [
migrations.AlterField(
model_name='config',
name='url',
field=models.URLField(blank=True, max_length=350, verbose_name='RSS地址'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 11:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0008_alter_config_url'),
]
operations = [
migrations.AlterField(
model_name='seedinfo',
name='seed_published_time',
field=models.DateTimeField(verbose_name='发布时间'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 11:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0009_alter_seedinfo_seed_published_time'),
]
operations = [
migrations.AlterField(
model_name='seedinfo',
name='seed_file_size',
field=models.BigIntegerField(verbose_name='种子文件大小'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 12:20
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('sites', '0014_alter_siteinfo_siteconfig_name_cn'),
('rss', '0010_alter_seedinfo_seed_file_size'),
]
operations = [
migrations.AlterUniqueTogether(
name='seedinfo',
unique_together={('seed_hash_id', 'siteinfo_id')},
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.0.3 on 2022-09-10 15:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('rss', '0011_alter_seedinfo_unique_together'),
]
operations = [
migrations.AddField(
model_name='seedinfo',
name='rule_id',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='rss.rule'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 16:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0012_seedinfo_rule_id'),
]
operations = [
migrations.AddField(
model_name='seedinfo',
name='seed_torrent_id',
field=models.IntegerField(null=True, verbose_name='在下载器的标识ID'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 20:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0013_seedinfo_seed_torrent_id'),
]
operations = [
migrations.AddField(
model_name='rule',
name='rate',
field=models.CharField(max_length=10, null=True, verbose_name='码率:空,1080,2060'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 20:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0014_rule_rate'),
]
operations = [
migrations.AlterField(
model_name='rule',
name='rate',
field=models.CharField(default='all', max_length=10, verbose_name='码率:all,1080,2060'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-09-10 21:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rss', '0015_alter_rule_rate'),
]
operations = [
migrations.AddField(
model_name='rule',
name='is_paused',
field=models.BooleanField(default=False, verbose_name='是否立刻下载'),
),
]

View File

74
apps/rss/models.py Normal file
View File

@@ -0,0 +1,74 @@
from django.db import models
from sites.models import SiteInfo
from download.models import Tools
from cron.models import Job
# Create your models here.
class FilmCategory(models.Model):
"""影视类别"""
name_en = models.CharField('英文名', max_length=20)
name_cn = models.CharField('中文名', max_length=20)
class FilmType(models.Model):
"""影视类型"""
name = models.CharField('名称', max_length=20)
#class FilmActor(models.Model):
#"""影视演员"""
#name = models.CharField('名称', max_length=50)
#name_en = models.CharField('英文名称', max_length=100)
#constellation = models.CharField('星座', max_length=10)
#gender = models.CharField('性别', max_length=2)
#birth = models.DateField('出生日期')
#birthplace = models.CharField('出生地', max_length=50)
#job = models.CharField('职业', max_length=100)
#description = models.TextField('简介')
#avatar = models.URLField('头像', blank=True)
class Config(models.Model):
"""RSS配置"""
url = models.URLField('RSS地址', blank=True, max_length=350)
#site_name = models.CharField('网站名简称,英文', max_length=50, unique=True)
#site_name_cn = models.CharField('网站名简称,中文', max_length=50, unique=True)
#on_update 和 on_delete 后面可以跟的词语有四个
#1. no action 表示 不做任何操作,
#2. set null 表示在外键表中将相应字段设置为null
#3. set default 表示设置为默认值
#4. cascade 表示级联操作,就是说,如果主键表中被参考字段更新,外键表中也更新,主键表中的记录被删除,外键表中改行也相应删除
siteinfo_id = models.ForeignKey(SiteInfo, on_delete=models.CASCADE, related_name='siteinfo')
class Rule(models.Model):
"""RSS规则"""
name = models.CharField('规则名称', max_length=50, unique=True)
config_id = models.ForeignKey(Config, on_delete=models.CASCADE, related_name='config')
keyword = models.CharField('关键字,使用逗号分隔', max_length=100, null=True)
tools_id = models.ForeignKey(Tools, on_delete=models.CASCADE, related_name='tools')
status = models.BooleanField("状态启用,禁用",default=False)
refresh_time = models.IntegerField('刷新间隔分钟', default=5)
rate = models.CharField('码率:all,1080,2060', max_length=10, default='all')
is_paused = models.BooleanField("是否立刻下载",default=False)
job_id = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='job', null=True)
class SeedInfo(models.Model):
"""种子信息"""
seed_name = models.CharField('种子名称', max_length=500)
seed_type = models.CharField('种子类型', max_length=50)
seed_details_link = models.URLField('种子详情地址', blank=True)
seed_published_time = models.DateTimeField('发布时间')
seed_hash_id = models.CharField('种子hash值', max_length=50)
seed_donwload_link = models.URLField('种子下载地址', blank=True)
seed_file_size = models.BigIntegerField('种子文件大小')
seed_status = models.BooleanField('状态:下载,未下载', default=False)
seed_torrent_id = models.IntegerField("在下载器的标识ID", null=True)
photos = models.URLField('海报', blank=True)
douban_link = models.URLField('豆瓣电影地址', blank=True)
imdb_link = models.URLField('IMDB电影地址', blank=True)
siteinfo_id = models.ForeignKey(SiteInfo, on_delete=models.CASCADE)
rule_id = models.ForeignKey(Rule, on_delete=models.CASCADE, null=True)
class Meta:
unique_together=("seed_hash_id", "siteinfo_id")

View File

@@ -0,0 +1,203 @@
<!-- 正文开始 -->
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline searchInput">
<input id="rssconfigSearchValue" class="layui-input" type="text" placeholder="输入关键字" />
</div>
<div class="layui-inline">
<button id="rssconfigBtnSearch" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>
<button id="rssconfigBtnAdd" class="layui-btn icon-btn"><i
class="layui-icon">&#xe654;</i>添加</button>
<button id="rssconfigBtnDel" class="layui-btn layui-btn-danger icon-btn"><i
class="layui-icon">&#xe640;</i>删除</button>
</button>
此处仅配置不运行,需定制规则启动
</div>
</div>
</div>
<table class="layui-table" id="rssconfigTable" lay-filter="rssconfigTable"></table>
</div>
</div>
</div>
<!-- 表格操作列 -->
<script type="text/html" id="rssconfigTableBar">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown', 'upload'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
var xmSelect = layui.xmSelect;
var upload = layui.upload;
form.render('select');
// 渲染表格
var insTb = tableX.render({
elem: '#rssconfigTable',
url: setter.base_server + 'rss/config/list',
method: 'GET',
page: true,
limit: 15,
limits: [15, 50, 100],
cols: [[
{ type: 'checkbox' },
{ field: 'id', title: 'ID', hide: true },
{ field: 'site_name', sort: true, title: '站点', width: 180 },
{ field: 'url', sort: true, title: 'RSS地址' },
{ align: 'center', toolbar: '#rssconfigTableBar', title: '操作', width: 150 }
]]
});
// 添加按钮点击事件
$('#rssconfigBtnAdd').click(function () {
showAddModel();
});
// 工具条点击事件
table.on('tool(rssconfigTable)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') {
//var csrftoken = getCookie('csrftoken');
layer.confirm('确定删除此记录 ' + data.name + '?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
$.ajax({
url: "rss/config/del",
type: 'POST',
dataType: "json",
data: { 'ids': [data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
// beforeSend: function(request) {
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
// request.setRequestHeader("X-CSRFToken", csrftoken);
// },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssconfigTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
//显示表单弹窗
var showAddModel = function () {
var title = '添加RSS订阅';
admin.open({
title: title,
area: ['600px', '300px'],
url: 'rss/config/add',
success: function (layero, index) {
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
}
});
};
var showEditModel = function (data) {
var title = '编辑';
admin.open({
title: title,
area: ['600px', '300px'],
url: 'rss/config/edit?id=' + data.id,
success: function (layero, index) {
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
}
});
};
// 批量删除按钮点击事件
$('#rssconfigBtnDel').click(function () {
//选中事件
var checkStatus = table.checkStatus('rssconfigTable')
//得到选择长度
dataLength = checkStatus.data.length
data = checkStatus.data;
var ids = [];
if (dataLength == 0) {
//未选中
layer.msg("请选择需要删除的记录", { icon: 2 });
} else {
layer.confirm('确定删除选中的' + dataLength + '个记录?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
for (var i = 0; i < data.length; i++) {
ids.push(data[i].id);
}
$.ajax({
url: "rss/config/del",
type: 'POST',
dataType: "json",
data: { 'ids': ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssconfigTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
// 监听enter键,光标在搜索框
$('#rssconfigSearchValue').keydown(function (event) {
if (event.keyCode === 13) {
var key = 'search';
var value = $('#rssconfigSearchValue').val();
table.reload('rssconfigTable', { where: { searchKey: key, searchValue: value } });
} else if (event.keyCode === 8 && !$psw.val()) {
$("#rssconfigSearchValue").val('');
ptable();
if (event.preventDefault) event.preventDefault();
if (event.returnValue) event.returnValue = false;
}
});
// 搜索按钮点击事件
$('#rssconfigBtnSearch').click(function () {
var key = 'search';
var value = $('#rssconfigSearchValue').val();
table.reload('rssconfigTable', { where: { searchKey: key, searchValue: value } });
});
});
</script>

View File

@@ -0,0 +1,87 @@
<!-- 表单弹窗 -->
<form id="rssconfigAddForm" lay-filter="rssconfigAddForm" class="layui-form model-form" method="POST">{% csrf_token %}
<div class="layui-form-item">
<label class="layui-form-label">站点名称</label>
<div class="layui-input-block">
<div id="siteinfo_id"></div>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">RSS地址</label>
<div class="layui-input-block">
<input name="url" type="text" class="layui-input" lay-verify="required|urlX"/>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
<button class="layui-btn" lay-filter="rssconfigAddForm-submit" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'formX', 'dropdown'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
form.render('select', 'rssconfigAddForm');
var siteInfoIDList = [];
// 渲染多选下拉框
var insSel = xmSelect.render({
el: '#siteinfo_id',
name: 'siteinfo_id',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
filterable: true, //开启搜索
data: []
});
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('sites/siteinfo/select/list', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {
siteInfoIDList.push({name: item.name, value: item.id});
});
insSel.update({
data: siteInfoIDList,
autoRow: true,
})
} else {
layer.msg(res.msg, {icon: 2});
}
});
form.on('submit(rssconfigAddForm-submit)', function (data) {
layer.load(2);
admin.req('rss/config/add', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
table.reload('rssconfigTable', {});
admin.closeDialog('#rssconfigAddForm');
} else {
layer.msg(data.msg, {icon: 2});
}
}, $('#rssconfigAddForm').attr('method'));
return false;
});
});
</script>

View File

@@ -0,0 +1,93 @@
<!-- 表单弹窗 -->
<form id="rssconfigEditForm" lay-filter="rssconfigEditForm" class="layui-form model-form" method="POST">{% csrf_token %}
<input name="id" type="hidden" value="{{id}}"/>
<div class="layui-form-item">
<label class="layui-form-label">站点名称</label>
<div class="layui-input-block">
<div id="siteinfo_id"></div>
</div>
</div>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">RSS地址</label>
<div class="layui-input-block">
<input name="url" type="text" class="layui-input" lay-verify="required|urlX" value="{{url}}"/>
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
<button class="layui-btn" lay-filter="rssconfigEditForm-submit" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'xmSelect', 'table', 'admin','formX'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var xmSelect = layui.xmSelect;
var table = layui.table;
var admin = layui.admin;
var formX = layui.formX;
form.render('select',"rssconfigEditForm");
var siteInfoIDList = [];
// 渲染多选下拉框
var insSel = xmSelect.render({
el: '#siteinfo_id',
name: 'siteinfo_id',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
filterable: true, //开启搜索
data: []
});
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('sites/siteinfo/select/list?flag=1', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {
if ( item.id === {{siteinfo_id}} ){
siteInfoIDList.push({name: item.name, value: item.id,selected: true});
} else {
siteInfoIDList.push({name: item.name, value: item.id});
}
});
insSel.update({
data: siteInfoIDList,
autoRow: true,
})
} else {
layer.msg(res.msg, {icon: 2});
}
});
form.on('submit(rssconfigEditForm-submit)', function (data) {
layer.load(2);
admin.req('rss/config/edit', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
table.reload('rssconfigTable', {});
admin.closeDialog('#rssconfigEditForm');
} else {
layer.msg(data.msg, {icon: 2});
}
}, $('#rssconfigEditForm').attr('method'));
return false;
});
});
</script>

View File

@@ -0,0 +1,241 @@
<!-- 正文开始 -->
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline searchInput">
<input id="rssruleSearchValue" class="layui-input" type="text" placeholder="输入关键字" />
</div>
<div class="layui-inline">
<button id="rssruleBtnSearch" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>
<button id="rssruleBtnAdd" class="layui-btn icon-btn"><i
class="layui-icon">&#xe654;</i>添加</button>
<button id="rssruleBtnDel" class="layui-btn layui-btn-danger icon-btn"><i
class="layui-icon">&#xe640;</i>删除</button>
</button>
</div>
</div>
</div>
<table class="layui-table" id="rssruleTable" lay-filter="rssruleTable"></table>
</div>
</div>
</div>
<!-- 表格操作列 -->
<script type="text/html" id="rssruleTableBar">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
{% verbatim myblock %}
<!-- 表格状态列 -->
<script type="text/html" id="rssruleTableState">
<input type="checkbox" lay-filter="RssRuleState" value="{{d.id}}" lay-skin="switch"
lay-text="启用|禁用" {{d.status==1?'checked':''}}/>
</script>
{% endverbatim myblock %}
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown', 'upload'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
var xmSelect = layui.xmSelect;
var upload = layui.upload;
form.render('select');
// 渲染表格
var insTb = tableX.render({
elem: '#rssruleTable',
url: setter.base_server + 'rss/rule/list',
method: 'GET',
page: true,
limit: 15,
limits: [15, 50, 100],
cols: [[
{ type: 'checkbox' },
{ field: 'id', title: 'ID', hide: true },
{ field: 'name', sort: true, title: '名称', width: 120 },
{ field: 'config_id', sort: true, title: '站点', width: 150 },
{ field: 'tools_id', sort: true, title: '下载器', width: 200 },
{ field: 'keyword', title: '关键字'},
{ field: 'rate', title: '码率', width: 100},
{ field: 'is_paused', title: '下载状态', width: 100, templet: function (d) {
if (d.is_paused == 1){
return "下载";
}else{
return "暂停";
}
}
},
{ field: 'refresh_time', sort: true, title: '刷新间隔', width: 110 },
{ field: 'status', templet: '#rssruleTableState', title: '状态', width: 80 },
{ align: 'center', toolbar: '#rssruleTableBar', title: '操作', width: 120 }
]]
});
// 添加按钮点击事件
$('#rssruleBtnAdd').click(function () {
showAddModel();
});
// 工具条点击事件
table.on('tool(rssruleTable)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'edit') { // 修改
showEditModel(data);
} else if (layEvent === 'del') {
//var csrftoken = getCookie('csrftoken');
layer.confirm('确定删除此记录 ' + data.name + '?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
$.ajax({
url: "rss/rule/del",
type: 'POST',
dataType: "json",
data: { 'ids': [data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
// beforeSend: function(request) {
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
// request.setRequestHeader("X-CSRFToken", csrftoken);
// },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssruleTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
//显示表单弹窗
var showAddModel = function () {
var title = '添加';
admin.open({
title: title,
area: ['600px', '600px'],
url: 'rss/rule/add',
success: function (layero, index) {
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
}
});
};
var showEditModel = function (data) {
var title = '编辑';
admin.open({
title: title,
area: ['600px', '600px'],
url: 'rss/rule/edit?id=' + data.id,
success: function (layero, index) {
$(layero).children('.layui-layer-content').css('overflow', 'visible'); // 禁止出现滚动条
}
});
};
// 批量删除按钮点击事件
$('#rssruleBtnDel').click(function () {
//选中事件
var checkStatus = table.checkStatus('rssruleTable')
//得到选择长度
dataLength = checkStatus.data.length
data = checkStatus.data;
var ids = [];
if (dataLength == 0) {
//未选中
layer.msg("请选择需要删除的记录", { icon: 2 });
} else {
layer.confirm('确定删除选中的' + dataLength + '个记录?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
for (var i = 0; i < data.length; i++) {
ids.push(data[i].id);
}
$.ajax({
url: "rss/rule/del",
type: 'POST',
dataType: "json",
data: { 'ids': ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssruleTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
// 监听enter键,光标在搜索框
$('#rssruleSearchValue').keydown(function (event) {
if (event.keyCode === 13) {
var key = 'search';
var value = $('#rssruleSearchValue').val();
table.reload('rssruleTable', { where: { searchKey: key, searchValue: value } });
} else if (event.keyCode === 8 && !$psw.val()) {
$("#rssruleSearchValue").val('');
ptable();
if (event.preventDefault) event.preventDefault();
if (event.returnValue) event.returnValue = false;
}
});
// 搜索按钮点击事件
$('#rssruleBtnSearch').click(function () {
var key = 'search';
var value = $('#rssruleSearchValue').val();
table.reload('rssruleTable', { where: { searchKey: key, searchValue: value } });
});
// 修改规则状态
form.on('switch(RssRuleState)', function (obj) {
layer.load(2);
admin.req('rss/rule/checkbox/setstatus', {
id: obj.elem.value,
value: obj.elem.checked ? 1 : 0,
tag:'status'
}, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
//table.reload('table-user', {});
} else {
layer.msg(data.msg, {icon: 2});
$(obj.elem).prop('checked', !obj.elem.checked);
form.render('checkbox');
}
}, 'GET');
});
});
</script>

View File

@@ -0,0 +1,207 @@
<!-- 表单弹窗 -->
<form id="rssruleAddForm" lay-filter="rssruleAddForm" class="layui-form model-form" method="POST">{% csrf_token %}
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input name="name" type="text" class="layui-input" lay-verify="required"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">RSS站点</label>
<div class="layui-input-block">
<div id="config_id"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">下载器</label>
<div class="layui-input-block">
<div id="tools_id"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键字</label>
<div class="layui-input-block">
<input name="keyword" type="text" class="layui-input" maxlength="100" placeholder="用于匹配影视,多个使用逗号分隔,不写为全部" />
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">码率</label>
<div class="layui-input-block">
<div id="rate"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">刷新间隔</label>
<div class="layui-input-block">
<input name="refresh_time" type="text" class="layui-input inline-block" lay-verify="numberX" min="1" maxlength="5"
style="width: 160px;" placeholder="默认5分钟" />
<span>&nbsp;&nbsp;分钟</span>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">立即下载</label>
<div class="layui-inline-block" style="margin: 10px;">
<input type="radio" name="is_paused" value="0" title="暂停" checked/>
<input type="radio" name="is_paused" value="1" title="下载"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
<input type="checkbox" name="status" lay-skin="switch" lay-text="启用|禁用"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">消息通知</label>
<div class="layui-inline-block" style="margin: 10px;">
{{ notifys|safe }}
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
<button class="layui-btn" lay-filter="rssruleAddForm-submit" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'formX', 'dropdown'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
form.render('checkbox', 'rssruleAddForm');
form.render('select', 'rssruleAddForm');
form.render('radio');
/*
form.verify({
keyword: [
/^[a-zA-Z0-9_\u4e00-\u9fa5\\s·]+$/
,'用户名不能有特殊字符'
]
});
*/
var siteInfoIDList = [];
// 渲染多选下拉框
var insSel = xmSelect.render({
el: '#config_id',
name: 'config_id',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
filterable: true, //开启搜索
data: []
});
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('rss/config/select/list', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {
siteInfoIDList.push({name: item.name, value: item.id});
});
insSel.update({
data: siteInfoIDList,
autoRow: true,
})
} else {
layer.msg(res.msg, {icon: 2});
}
});
var toolsIDList = [];
// 渲染多选下拉框
var toolsSel = xmSelect.render({
el: '#tools_id',
name: 'tools_id',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
filterable: true, //开启搜索
data: []
});
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('download/tools/select/list', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {
toolsIDList.push({name: item.name, value: item.id});
});
toolsSel.update({
data: toolsIDList,
autoRow: true,
})
} else {
layer.msg(res.msg, {icon: 2});
}
});
// 渲染多选下拉框
var rateSel = xmSelect.render({
el: '#rate',
name: 'rate',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
data: [{'name':'all','value':'all', selected: true},
{'name':'1080','value':'1080'},
{'name':'2060','value':'2060'}
]
});
form.on('submit(rssruleAddForm-submit)', function (data) {
layer.load(2);
//将页面全部复选框选中的值拼接到一个数组中
var notifys_box = [];
$('input[name="notifys"]:checked').each(function() {
notifys_box.push($(this).val());
});
data.field.notifys = notifys_box
admin.req('rss/rule/add', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
table.reload('rssruleTable', {});
admin.closeDialog('#rssruleAddForm');
} else {
layer.msg(data.msg, {icon: 2});
}
}, $('#rssruleAddForm').attr('method'));
return false;
});
});
</script>

View File

@@ -0,0 +1,222 @@
<!-- 表单弹窗 -->
<form id="rssruleEditForm" lay-filter="rssruleEditForm" class="layui-form model-form" method="POST">{% csrf_token %}
<input name="id" type="hidden" value="{{id}}"/>
<input name="crontab_id" type="hidden" value="{{crontab_id}}"/>
<div class="layui-form-item layui-form-text">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input name="name" type="text" class="layui-input" lay-verify="required" value="{{name}}"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">RSS站点</label>
<div class="layui-input-block">
<div id="config_id"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">下载器</label>
<div class="layui-input-block">
<div id="tools_id"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">关键字</label>
<div class="layui-input-block">
<input name="keyword" type="text" class="layui-input" maxlength="100"
placeholder="用于匹配影视,多个使用逗号分隔,不写为全部" value="{{keyword}}"/>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">刷新间隔</label>
<div class="layui-input-block">
<input name="refresh_time" type="text" class="layui-input inline-block" lay-verify="numberX" min="1" maxlength="5"
style="width: 160px;" placeholder="默认5分钟" value="{{refresh_time}}" />
<span>&nbsp;&nbsp;分钟</span>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">立即下载</label>
<div class="layui-inline-block" style="margin: 10px;">
{% if is_paused %}
<input type="radio" name="is_paused" value="0" title="暂停"/>
<input type="radio" name="is_paused" value="1" title="下载" checked/>
{% else %}
<input type="radio" name="is_paused" value="0" title="暂停" checked/>
<input type="radio" name="is_paused" value="1" title="下载"/>
{% endif %}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">码率</label>
<div class="layui-input-block">
<div id="rate"></div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">状态</label>
<div class="layui-input-block">
{% if status == 1 %}
<input type="checkbox" name="status" checked="" lay-skin="switch" lay-text="启用|禁用"/>
{% else %}
<input type="checkbox" name="status" lay-skin="switch" lay-text="启用|禁用"/>
{% endif %}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">消息通知</label>
<div class="layui-inline-block" style="margin: 10px;">
{{ notifys|safe }}
</div>
</div>
<div class="layui-form-item text-right">
<button class="layui-btn layui-btn-primary" ew-event="closeDialog" type="button">取消</button>
<button class="layui-btn" lay-filter="rssruleEditForm-submit" lay-submit>保存</button>
</div>
</form>
<script>
layui.use(['layer', 'form', 'xmSelect', 'table', 'admin','formX'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var xmSelect = layui.xmSelect;
var table = layui.table;
var admin = layui.admin;
var formX = layui.formX;
form.render('checkbox', 'rssruleEditForm');
form.render('select',"rssruleEditForm");
form.render('radio');
var siteInfoIDList = [];
// 渲染多选下拉框
var insSel = xmSelect.render({
el: '#config_id',
name: 'config_id',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
filterable: true, //开启搜索
data: []
});
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('rss/config/select/list', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {
if ( item.id === {{config_id}} ){
siteInfoIDList.push({name: item.name, value: item.id,selected: true});
} else {
siteInfoIDList.push({name: item.name, value: item.id});
}
});
insSel.update({
data: siteInfoIDList,
autoRow: true,
})
} else {
layer.msg(res.msg, {icon: 2});
}
});
var toolsIDList = [];
// 渲染多选下拉框
var toolsSel = xmSelect.render({
el: '#tools_id',
name: 'tools_id',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
filterable: true, //开启搜索
data: []
});
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('download/tools/select/list', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {
if ( item.id === {{tools_id}} ){
toolsIDList.push({name: item.name, value: item.id,selected: true});
} else {
toolsIDList.push({name: item.name, value: item.id});
}
});
toolsSel.update({
data: toolsIDList,
autoRow: true,
})
} else {
layer.msg(res.msg, {icon: 2});
}
});
// 渲染多选下拉框
var rateSel = xmSelect.render({
el: '#rate',
name: 'rate',
prop: {
name: 'name',
value: 'value',
},
layVerify: 'required',
radio: true,
clickClose: true,
data: [{'name':'all','value':'all'},
{'name':'1080','value':'1080'},
{'name':'2060','value':'2060'}
]
});
rateSel.setValue([ "{{rate}}" ])
form.on('submit(rssruleEditForm-submit)', function (data) {
layer.load(2);
//将页面全部复选框选中的值拼接到一个数组中
var notifys_box = [];
$('input[name="notifys"]:checked').each(function() {
notifys_box.push($(this).val());
});
data.field.notifys = notifys_box
admin.req('rss/rule/edit', data.field, function (data) {
layer.closeAll('loading');
if (data.code == 1) {
layer.msg(data.msg, {icon: 1});
table.reload('rssruleTable', {});
admin.closeDialog('#rssruleEditForm');
} else {
layer.msg(data.msg, {icon: 2});
}
}, $('#rssruleEditForm').attr('method'));
return false;
});
});
</script>

View File

@@ -0,0 +1,217 @@
<!-- 正文开始 -->
<div class="layui-fluid">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-form toolbar">
<div class="layui-form-item">
<div class="layui-inline searchInput">
<input id="rssseedinfoSearchValue" class="layui-input" type="text" placeholder="输入关键字" />
</div>
<div class="layui-inline">
<button id="rssseedinfoBtnSearch" class="layui-btn icon-btn"><i class="layui-icon">&#xe615;</i>搜索
</button>
<button id="rssseedinfoBtnDel" class="layui-btn layui-btn-danger icon-btn"><i
class="layui-icon">&#xe640;</i>删除</button>
</button>
</div>
</div>
</div>
<table class="layui-table" id="rssseedinfoTable" lay-filter="rssseedinfoTable"></table>
</div>
</div>
</div>
<!-- 表格操作列 -->
<script type="text/html" id="rssseedinfoTableBar">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="download">下载</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
</script>
<script>
layui.use(['layer', 'form', 'table', 'tableX', 'util', 'admin', 'xmSelect', 'formX', 'dropdown', 'upload'], function () {
var $ = layui.jquery;
var layer = layui.layer;
var form = layui.form;
var table = layui.table;
var tableX = layui.tableX;
var util = layui.util;
var admin = layui.admin;
var formX = layui.formX;
var setter = layui.setter;
var xmSelect = layui.xmSelect;
var upload = layui.upload;
form.render('select');
// 渲染表格
var insTb = tableX.render({
elem: '#rssseedinfoTable',
url: setter.base_server + 'rss/seedinfo/list',
method: 'GET',
page: true,
limit: 15,
limits: [15, 50, 100],
cols: [[
{ type: 'checkbox' },
{ field: 'id', title: 'ID', hide: true },
{ field: 'siteinfo_id', sort: true, title: '站点', width: 150 },
{ field: 'seed_details_link', title: '种子介绍页', hide: true },
{ field: 'seed_name', title: '名称', templet: function (d) {
return '<a href="' + d.seed_details_link +'" target="_blank" id="test'+d.id+'" onmouseover="show('+ d.id +')"; onmouseleave="closeTips();">' + d.seed_name + '</a>';
}, unresize: true
},
{ field: 'seed_type', sort: true, title: '类型', width: 150 },
{ field: 'seed_published_time', title: '发布时间',sort: true, width: 170},
{ field: 'seed_file_size', sort: true, title: '大小', width: 100},
{ field: 'seed_status', title: '状态', width: 80, templet: function (d) {
if (d.seed_status) {
return '已下载';
}else{
return '未下载';
}
}
},
{ align: 'center', toolbar: '#rssseedinfoTableBar', title: '操作', width: 120 }
]]
});
// 工具条点击事件
table.on('tool(rssseedinfoTable)', function (obj) {
var data = obj.data;
var layEvent = obj.event;
if (layEvent === 'download') { // 下载
layer.confirm('确定下载 ' + data.seed_name + '?', { icon: 1, title: '提示', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
$.ajax({
url: "rss/seedinfo/donwload",
type: 'POST',
dataType: "json",
data: { 'id': data.id, csrfmiddlewaretoken: '{{ csrf_token }}' },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssseedinfoTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
}
);
} else if (layEvent === 'del') {
//var csrftoken = getCookie('csrftoken');
layer.confirm('确定删除此记录 ' + data.seed_name + '?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
$.ajax({
url: "rss/seedinfo/del",
type: 'POST',
dataType: "json",
data: { 'ids': [data.id], csrfmiddlewaretoken: '{{ csrf_token }}' },
// beforeSend: function(request) {
// //django需要csrf验证,Forbidden (CSRF cookie not set.):
// request.setRequestHeader("X-CSRFToken", csrftoken);
// },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssseedinfoTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
}
);
}
});
// 批量删除按钮点击事件
$('#rssseedinfoBtnDel').click(function () {
//选中事件
var checkStatus = table.checkStatus('rssseedinfoTable')
//得到选择长度
dataLength = checkStatus.data.length
data = checkStatus.data;
var ids = [];
if (dataLength == 0) {
//未选中
layer.msg("请选择需要删除的记录", { icon: 2 });
} else {
layer.confirm('确定删除选中的' + dataLength + '个记录?', { icon: 3, title: '警告', shadeClose: true },
function (i) {
layer.close(i);
layer.load(2);
for (var i = 0; i < data.length; i++) {
ids.push(data[i].id);
}
$.ajax({
url: "rss/seedinfo/del",
type: 'POST',
dataType: "json",
data: { 'ids': ids, csrfmiddlewaretoken: '{{ csrf_token }}' },
success: function (d) {
layer.closeAll('loading');
if (d.code == 1) {
table.reload('rssseedinfoTable', {});
layer.msg(d.msg, { icon: 1 });
} else {
layer.msg(d.msg, { icon: 2 });
}
}
});
});
}
});
// 监听enter键,光标在搜索框
$('#rssseedinfoSearchValue').keydown(function (event) {
if (event.keyCode === 13) {
var key = 'search';
var value = $('#rssseedinfoSearchValue').val();
table.reload('rssseedinfoTable', { where: { searchKey: key, searchValue: value } });
} else if (event.keyCode === 8 && !$psw.val()) {
$("#rssseedinfoSearchValue").val('');
ptable();
if (event.preventDefault) event.preventDefault();
if (event.returnValue) event.returnValue = false;
}
});
// 搜索按钮点击事件
$('#rssseedinfoBtnSearch').click(function () {
var key = 'search';
var value = $('#rssseedinfoSearchValue').val();
table.reload('rssseedinfoTable', { where: { searchKey: key, searchValue: value } });
});
});
//悬停显示
var tipsVal;
function show(obj) {
var node = "#test"+obj;
//options : tips的配置型,即位置[1上;2右;3下;4左, 字体颜色]
tipsVal=layer.tips("<div style='color:#000000;'>鼠标点击访问种子详情页</div>", node, {tips:[1,"#51a9e4"]});
}
function closeTips() {//关闭显示的信息
layer.close(tipsVal);
}
</script>

3
apps/rss/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,32 @@
# coding:utf-8
from django.urls import path
from .. import views, views_request
app_name = 'rss'
urlpatterns = [
# Resource asset path
#RSS配置
path('config', views.RssConfigListView.as_view()),
path('config/add', views.RssConfigAddView.as_view()),
path('config/edit', views.RssConfigEditView.as_view()),
path('config/del', views.RssConfigDelView.as_view()),
path('config/list', views_request.config),
path('config/select/list', views_request.select_rssconfig),
#RSS规则
path('rule', views.RssRuleListView.as_view()),
path('rule/add', views.RssRuleAddView.as_view()),
path('rule/edit', views.RssRuleEditView.as_view()),
path('rule/del', views.RssRuleDelView.as_view()),
path('rule/list', views_request.rule),
path('rule/checkbox/setstatus', views_request.checkbox_setRuleStatus),
#RSS种子详情
path('seedinfo', views.RssSeedInfoListView.as_view()),
path('seedinfo/del', views.RssSeedInfoDelView.as_view()),
path('seedinfo/list', views_request.seedinfo),
path('seedinfo/donwload', views_request.seed_download),
]

455
apps/rss/views.py Normal file
View File

@@ -0,0 +1,455 @@
from django.shortcuts import render
from django.views.generic import TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import JsonResponse
from django.db import transaction
import uuid
from .models import Config, Rule, SeedInfo
from sites.models import SiteInfo
from download.models import Tools
from cron.crontabs import my_scheduler
from notify.models import NotifyConfig
from cron.models import Job
# Create your views here.
class RssConfigListView(LoginRequiredMixin,TemplateView):
"""
显示
"""
template_name = 'rss/rssconfig.html'
def get(self, request, *args, **kwargs):
"""
"""
return super(RssConfigListView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
# 'data': self.ormdata_game
}
kwargs.update(context)
return super(RssConfigListView, self).get_context_data(**kwargs)
class RssConfigAddView(LoginRequiredMixin,TemplateView):
"""
添加
"""
template_name = 'rss/rssconfigadd.html'
#显示添加模板
def get(self, request, *args, **kwargs):
"""
"""
return super(RssConfigAddView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
}
kwargs.update(context)
return super(RssConfigAddView, self).get_context_data(**kwargs)
#数据提交接收方法
def post(self, request, *args, **kwargs):
"""
数据提交
"""
siteinfo_id = request.POST.get("siteinfo_id").strip()
url = request.POST.get("url").strip()
ormdata_siteinfo = SiteInfo.objects.get(id=int(siteinfo_id))
ormdata = Config.objects.create(url=url,
siteinfo_id=ormdata_siteinfo
)
ormdata.save()
response_data={"code":1,"msg":"添加成功"}
return JsonResponse(response_data)
class RssConfigEditView(LoginRequiredMixin,TemplateView):
"""
编辑域名
"""
template_name = 'rss/rssconfigedit.html'
#显示添加模板
def get(self, request, *args, **kwargs):
"""
得到
"""
#记录ID
self._id = request.GET.get('id')
self.ormdata = Config.objects.get(id=self._id)
return super(RssConfigEditView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
'id': self._id,
'url': self.ormdata.url,
'siteinfo_id': self.ormdata.siteinfo_id.id
}
kwargs.update(context)
return super(RssConfigEditView, self).get_context_data(**kwargs)
#数据提交接收方法
def post(self, request, *args, **kwargs):
"""
数据提交
"""
url = request.POST.get("url").strip()
siteinfo_id = request.POST.get("siteinfo_id").strip()
_id = request.POST.get('id')
ormdata_siteinfo = SiteInfo.objects.get(id=int(siteinfo_id))
ormdata = Config.objects.get(id=_id)
ormdata.url = url
ormdata.siteinfo_id=ormdata_siteinfo
ormdata.save()
response_data={"code":1,"msg":"添加成功"}
return JsonResponse(response_data)
class RssConfigDelView(LoginRequiredMixin,TemplateView):
"""
删除
"""
def post(self, request, *args, **kwargs):
"""
数据提交
"""
#得到批量或者单个要删除的id
ids = request.POST.getlist("ids[]")
#print("ids====>",ids)
Config.objects.filter(id__in=ids).delete()
#Site.objects.filter(id=i).delete()
response_data={"code":1,"msg":"操作成功"}
return JsonResponse(response_data)
#==================
class RssRuleListView(LoginRequiredMixin,TemplateView):
"""
显示
"""
template_name = 'rss/rssrule.html'
def get(self, request, *args, **kwargs):
"""
"""
return super(RssRuleListView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
# 'data': self.ormdata_game
}
kwargs.update(context)
return super(RssRuleListView, self).get_context_data(**kwargs)
class RssRuleAddView(LoginRequiredMixin,TemplateView):
"""
添加
"""
template_name = 'rss/rssruleadd.html'
#显示添加模板
def get(self, request, *args, **kwargs):
"""
"""
return super(RssRuleAddView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
html_notifys = []
notify_type = {'iyuu':'IYUU','telegram':'Telegram','email':'邮箱','enwechat':'企业微信'}
#获取通知配置
notifys_count = NotifyConfig.objects.count()
if notifys_count == 0:
html_notifys.append('<b><span style="color: red;"><font size="2">请先配置通知</font></span></b>')
else:
for d in NotifyConfig.objects.all():
html_notifys.append('<input type="checkbox" name="notifys" value="' + d.name +'" title="' + notify_type[d.name] + '"/>')
context = {
'notifys':"".join(html_notifys),
}
kwargs.update(context)
return super(RssRuleAddView, self).get_context_data(**kwargs)
#事务回滚
@transaction.atomic
def post(self, request, *args, **kwargs):
"""
数据提交
"""
name = request.POST.get("name").strip()
config_id = request.POST.get("config_id").strip()
keyword = request.POST.get("keyword").strip()
tools_id = request.POST.get("tools_id").strip()
status = request.POST.get("status")
refresh_time = request.POST.get("refresh_time",'')
notifys = request.POST.getlist("notifys[]")
rate = request.POST.get("rate").strip()
is_paused = request.POST.get("rate")
ormdata_config = Config.objects.get(id=int(config_id))
ormdata_tools = Tools.objects.get(id=int(tools_id))
#任务ID号
crontab_id = str(uuid.uuid1())
if status == "on":
status = 1
else:
status = 0
if refresh_time == '':
refresh_time = 5
#创建任务
#每几分运行
crontab_time = '*/%s * * * *' % refresh_time
ormdata_job = Job.objects.create(name=name,
jobtype_id=1002,
crontab_time_type = "minute",
crontab_status = status,
minute = refresh_time,
notifys = ",".join(notifys),
crontab_time = crontab_time,
crontab_id = crontab_id,
)
ormdata_job.save()
#创建订阅规则
ormdata_rule = Rule.objects.create(name=name,
config_id=ormdata_config,
tools_id=ormdata_tools,
keyword=keyword,
status = status,
refresh_time = int(refresh_time),
job_id = ormdata_job,
rate = rate,
is_paused = int(is_paused)
)
ormdata_rule.save()
#开始进行订阅操作
minute = '*/%s' % refresh_time
my_scheduler(crontab_id, status, minute=minute, jobtype_id=1002)
response_data={"code":1,"msg":"添加成功"}
return JsonResponse(response_data)
class RssRuleEditView(LoginRequiredMixin,TemplateView):
"""
编辑域名
"""
template_name = 'rss/rssruleedit.html'
#显示添加模板
def get(self, request, *args, **kwargs):
"""
得到
"""
#记录ID
self._id = request.GET.get('id')
self.ormdata = Rule.objects.get(id=self._id)
return super(RssRuleEditView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
html_notifys = []
notify_type = {'iyuu':'IYUU','telegram':'Telegram','email':'邮箱','enwechat':'企业微信'}
ormdata_job = Job.objects.get(id=self.ormdata.job_id.id)
for d in NotifyConfig.objects.all():
if d.name in ormdata_job.notifys:
html_notifys.append('<input type="checkbox" name="notifys" value="' + d.name +'" title="' + notify_type[d.name] + '" checked />')
else:
html_notifys.append('<input type="checkbox" name="notifys" value="' + d.name +'" title="' + notify_type[d.name] + '"/>')
context = {
'id': self._id,
'name': self.ormdata.name,
'config_id': self.ormdata.config_id.id,
'tools_id': self.ormdata.tools_id.id,
'keyword': self.ormdata.keyword,
'status': self.ormdata.status,
'refresh_time': self.ormdata.refresh_time,
'notifys':"".join(html_notifys),
'crontab_id': ormdata_job.crontab_id,
'rate': self.ormdata.rate,
'is_paused':self.ormdata.is_paused
}
kwargs.update(context)
return super(RssRuleEditView, self).get_context_data(**kwargs)
#数据提交接收方法
def post(self, request, *args, **kwargs):
"""
数据提交
"""
name = request.POST.get("name").strip()
config_id = request.POST.get("config_id").strip()
keyword = request.POST.get("keyword").strip()
tools_id = request.POST.get("tools_id").strip()
status = request.POST.get("status")
refresh_time = request.POST.get("refresh_time",'')
crontab_id = request.POST.get("crontab_id")
rate = request.POST.get("rate")
is_paused = request.POST.get("is_paused")
notifys = request.POST.getlist("notifys[]")
_id = request.POST.get('id')
ormdata_config = Config.objects.get(id=int(config_id))
ormdata_tools = Tools.objects.get(id=int(tools_id))
if status == "on":
status = 1
else:
status = 0
if refresh_time == '':
refresh_time = 5
ormdata = Rule.objects.get(id=_id)
ormdata.name = name
ormdata.config_id=ormdata_config
ormdata.tools_id = ormdata_tools
ormdata.keyword = keyword
ormdata.status = status
ormdata.refresh_time = refresh_time
ormdata.rate = rate
ormdata.is_paused = int(is_paused)
ormdata.save()
crontab_time = '*/%s * * * *' % refresh_time
Job.objects.filter(crontab_id=crontab_id).update(notifys=",".join(notifys),
crontab_time = crontab_time,
minute = refresh_time
)
minute = '*/%s' % refresh_time
my_scheduler(crontab_id = crontab_id, crontab_status = status, minute=minute, jobtype_id=1002 )
response_data={"code":1,"msg":"添加成功"}
return JsonResponse(response_data)
class RssRuleDelView(LoginRequiredMixin,TemplateView):
"""
删除
"""
def post(self, request, *args, **kwargs):
"""
数据提交
"""
#得到批量或者单个要删除的id
ids = request.POST.getlist("ids[]")
#print("ids====>",ids)
#Rule.objects.filter(id__in=ids).delete()
for i in ids:
_id = Rule.objects.get(id=i)
#删除任务
Job.objects.filter(id=_id.job_id.id).delete()
#删除记录
Rule.objects.filter(id=i).delete()
#删除计划任务
my_scheduler(crontab_id = _id.job_id.crontab_id, action="del")
response_data={"code":1,"msg":"操作成功"}
return JsonResponse(response_data)
#==================
class RssSeedInfoListView(LoginRequiredMixin,TemplateView):
"""
显示
"""
template_name = 'rss/rssseedinfo.html'
def get(self, request, *args, **kwargs):
"""
"""
return super(RssSeedInfoListView, self).get(request, *args, **kwargs)
#显示编辑模板
def get_context_data(self, **kwargs):
context = {
# 'data': self.ormdata_game
}
kwargs.update(context)
return super(RssSeedInfoListView, self).get_context_data(**kwargs)
class RssSeedInfoDelView(LoginRequiredMixin,TemplateView):
"""
删除
"""
def post(self, request, *args, **kwargs):
"""
数据提交
"""
#得到批量或者单个要删除的id
ids = request.POST.getlist("ids[]")
#print("ids====>",ids)
SeedInfo.objects.filter(id__in=ids).delete()
response_data={"code":1,"msg":"操作成功"}
return JsonResponse(response_data)
#==================

339
apps/rss/views_request.py Normal file
View File

@@ -0,0 +1,339 @@
#coding=utf-8
from django.http import JsonResponse, StreamingHttpResponse,HttpResponse
from django.db.models import Q
from django.contrib.auth.decorators import login_required
from transmission_rpc import Client
import qbittorrentapi
from .models import Config, Rule, SeedInfo
from cron.crontabs import my_scheduler
from common.utils import filesizeformat, parseUrl
#直接使用检查是否管理员
#from django.contrib.auth.decorators import user_passes_test
#@user_passes_test(lambda u: u.is_superuser)
@login_required
def config(request):
"""
提供相应的数据
"""
#得到偏移量
pageIndex= int(request.GET.get('page',1))
#得到查询个数
pageSize = int(request.GET.get('limit',10))
#搜索字段
searchKey = request.GET.get('searchKey','')
searchValue = request.GET.get('searchValue','')
#去掉空格
searchValue = searchValue.strip()
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
if searchValue == '':
data['count'] = Config.objects.count()
if pageIndex == 1:
ormdata = Config.objects.order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = Config.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
else:
data['count'] = Config.objects.filter(Q(name__icontains=searchValue)|Q(name_cn__icontains=searchValue)).count()
if pageIndex == 1:
ormdata = Config.objects.filter(Q(name__icontains=searchValue)|Q(name_cn__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = Config.objects.filter(Q(name__icontains=searchValue)|Q(name_cn__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
for i in ormdata:
data['data'].append({"id":i.id,
"site_name":i.siteinfo_id.siteconfig_name + '(' + i.siteinfo_id.siteconfig_name_cn + ')',
"url":i.url,
})
#返回json串
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
return JsonResponse(data)
#===================================================================================================================
@login_required
def rule(request):
"""
提供相应的数据
"""
#得到偏移量
pageIndex= int(request.GET.get('page',1))
#得到查询个数
pageSize = int(request.GET.get('limit',10))
#搜索字段
searchKey = request.GET.get('searchKey','')
searchValue = request.GET.get('searchValue','')
#去掉空格
searchValue = searchValue.strip()
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
if searchValue == '':
data['count'] = Rule.objects.count()
if pageIndex == 1:
ormdata = Rule.objects.order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = Rule.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
else:
data['count'] = Rule.objects.filter(Q(name__icontains=searchValue)).count()
if pageIndex == 1:
ormdata = Rule.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = Rule.objects.filter(Q(name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
for i in ormdata:
data['data'].append({"id":i.id,
#"config_id":i.config_id.siteinfo_id.siteconfig_name_cn,
"config_id":i.config_id.siteinfo_id.siteconfig_name + '(' + i.config_id.siteinfo_id.siteconfig_name_cn + ')',
"tools_id":i.tools_id.name,
"name":i.name,
"keyword":i.keyword,
"status":i.status,
"refresh_time":i.refresh_time,
"rate":i.rate,
"is_paused":i.is_paused
})
#返回json串
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
return JsonResponse(data)
#===================================================================================================================
@login_required
def seedinfo(request):
"""
提供相应的数据
"""
#得到偏移量
pageIndex= int(request.GET.get('page',1))
#得到查询个数
pageSize = int(request.GET.get('limit',10))
#搜索字段
searchKey = request.GET.get('searchKey','')
searchValue = request.GET.get('searchValue','')
#去掉空格
searchValue = searchValue.strip()
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
if searchValue == '':
data['count'] = SeedInfo.objects.count()
if pageIndex == 1:
ormdata = SeedInfo.objects.order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = SeedInfo.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
else:
data['count'] = SeedInfo.objects.filter(Q(seed_name__icontains=searchValue)).count()
if pageIndex == 1:
ormdata = SeedInfo.objects.filter(Q(seed_name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = SeedInfo.objects.filter(Q(seed_name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
for i in ormdata:
data['data'].append({"id":i.id,
"siteinfo_id":i.siteinfo_id.siteconfig_name + '(' + i.siteinfo_id.siteconfig_name_cn + ')',
"seed_name":i.seed_name,
"seed_type":i.seed_type,
"seed_published_time":i.seed_published_time,
"seed_file_size":filesizeformat(i.seed_file_size),
"seed_status":i.seed_status,
"seed_details_link":i.seed_details_link,
})
#返回json串
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
return JsonResponse(data)
#===================================================================================================================
@login_required
def checkbox_setRuleStatus(request):
"""
设置规则启用,禁用
"""
_id = request.GET.get('id')
value = request.GET.get('value')
tag = request.GET.get('tag')
ormdata_rule = Rule.objects.get(id=_id)
if tag == 'status':
#修改状态
Rule.objects.filter(id=_id).update(status=int(value))
if int(value) == 1:
#激活任务
minute = '*/%s' % ormdata_rule.refresh_time
my_scheduler(crontab_id = ormdata_rule.job_id.crontab_id, crontab_status = True, minute=minute, jobtype_id=1002 )
else:
#禁用任务
my_scheduler(crontab_id = ormdata_rule.job_id.crontab_id, action="del" )
data = {'code':1,'msg':'操作成功'}
else:
data = {'code':0,'msg':'未知操作'}
#返回json串
#return HttpResponse(json.dumps(data,ensure_ascii = False), "application/json")
return JsonResponse(data)
#==================
@login_required
def select_rssconfig(request):
"""
RSS订阅
xmSelect
"""
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
#获取数据标志 1 返回所有 其它返回未配置站点
#flag = request.GET.get('flag', 0)
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
#获取所有记录
data['count'] = Config.objects.count()
ormdata = Config.objects.order_by(order_by).all()
for i in ormdata:
data['data'].append({
"id":i.id,
"name":i.siteinfo_id.siteconfig_name + "(" + i.siteinfo_id.siteconfig_name_cn + ")"
})
return JsonResponse(data)
#==================
@login_required
def seed_download(request):
"""
下载种子
"""
#种子ID
seed_id = request.POST.get("id")
ormdata_seedinfo = SeedInfo.objects.get(id = seed_id)
#下载连接
seed_donwload_link = ormdata_seedinfo.seed_donwload_link
#下载器类型
download_type = ormdata_seedinfo.rule_id.tools_id.typed
download_username = ormdata_seedinfo.rule_id.tools_id.username
download_password = ormdata_seedinfo.rule_id.tools_id.password
download_url = ormdata_seedinfo.rule_id.tools_id.url
#下载目录
download_dirname = ormdata_seedinfo.rule_id.tools_id.dirname
if download_dirname == "":
download_dirname = None
url_data = parseUrl(download_url)
if download_type == 'tr':
try:
c = Client(protocol= url_data['protocol'],
host=url_data['host'],
port=url_data['port'],
username=download_username,
password=download_password,
timeout = 10
)
torrent = c.add_torrent(seed_donwload_link,
timeout = 10,
download_dir = download_dirname
)
#获取种子id
torrent_id = torrent.id
ormdata_seedinfo.seed_torrent_id = torrent_id
ormdata_seedinfo.seed_status = True
ormdata_seedinfo.save()
response_data={"code":1,"msg": "添加成功" }
except Exception as e:
print(e)
response_data={"code":0,"msg": "添加失败" }
elif download_type == 'qb':
c = qbittorrentapi.Client(
host=url_data['host'],
port=url_data['port'],
username=download_username,
password=download_password,
)
try:
c.auth_log_in()
response_data={"code":1,"msg": "添加成功" }
except:
response_data={"code":0,"msg": "添加失败" }
return JsonResponse(response_data)

View File

@@ -61,7 +61,7 @@
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('sites/siteconfigname2siteinfo/list', function (res) {
admin.req('sites/siteconfigname/list', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {

View File

@@ -60,7 +60,7 @@
/* 获取所有记录类型 */
var loadIndex = layer.load(2);
admin.req('sites/siteconfigname/list', function (res) {
admin.req('sites/siteconfigname/list?flag=1', function (res) {
layer.close(loadIndex);
if (0 === res.code) {
$.each(res.data, function (index, item) {

View File

@@ -14,7 +14,6 @@ urlpatterns = [
path('siteconfig/del', views.SiteConfigDelView.as_view()),
path('siteconfig/list', views_request.siteconfig),
path('siteconfigname/list', views_request.siteconfigname),
path('siteconfigname2siteinfo/list', views_request.siteconfigname2siteinfo),
path('siteconfig/export', views_request.siteconfigExport),
path('siteconfig/import', views_request.siteconfigImport),
#站点升级配置
@@ -31,6 +30,7 @@ urlpatterns = [
path('siteinfo/list', views_request.siteinfo),
path('siteinfo/export', views_request.siteinfoExport),
path('siteinfo/import', views_request.siteinfoImport),
path('siteinfo/select/list', views_request.select_siteinfo),
]

View File

@@ -370,9 +370,12 @@ class SiteInfoAddView(LoginRequiredMixin,TemplateView):
cookie = request.POST.get("cookie").strip()
passkey = request.POST.get("passkey").strip()
siteconfig_name_cn = list(SiteConfig.objects.filter(name=siteconfig_name).values_list('name_cn',flat=True))
ormdata = SiteInfo.objects.create(siteconfig_name=siteconfig_name,
cookie=cookie,
passkey=passkey,
siteconfig_name_cn = siteconfig_name_cn[0]
)
ormdata.save()
@@ -423,12 +426,14 @@ class SiteInfoEditView(LoginRequiredMixin,TemplateView):
_id = request.POST.get('id')
siteconfig_name_cn = list(SiteConfig.objects.filter(name=siteconfig_name).values_list('name_cn',flat=True))
ormdata = SiteInfo.objects.get(id=_id)
ormdata.siteconfig_name = siteconfig_name
ormdata.cookie = cookie
ormdata.passkey = passkey
ormdata.siteconfig_name_cn = siteconfig_name_cn[0]
ormdata.save()

View File

@@ -4,6 +4,7 @@ from django.db.models import Q
from django.contrib.auth.decorators import login_required
from .models import SiteConfig, SiteRank, SiteInfo
from rss.models import Config
import simplejson as json
import io
from urllib import parse
@@ -178,19 +179,19 @@ def siteinfo(request):
ormdata = SiteInfo.objects.order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
else:
data['count'] = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)).count()
data['count'] = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)|Q(siteconfig_name_cn__icontains=searchValue)).count()
if pageIndex == 1:
ormdata = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
ormdata = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)|Q(siteconfig_name_cn__icontains=searchValue)).order_by(order_by)[:pageSize] #offset:limit
else:
ormdata = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
ormdata = SiteInfo.objects.filter(Q(siteconfig_name__icontains=searchValue)|Q(siteconfig_name_cn__icontains=searchValue)).order_by(order_by)[pageSize*(pageIndex-1):pageIndex * pageSize]
for i in ormdata:
siteconfig_ormdata = SiteConfig.objects.get(name=i.siteconfig_name)
#siteconfig_ormdata = SiteConfig.objects.get(name=i.siteconfig_name)
data['data'].append({"id":i.id,
"siteconfig_name":i.siteconfig_name,
'siteconfig_name_cn':siteconfig_ormdata.name_cn,
'siteconfig_name_cn':i.siteconfig_name_cn,
"cookie":i.cookie,
"passkey":i.passkey,
})
@@ -392,6 +393,7 @@ def siteinfoImport(request):
return JsonResponse(response_data)
#==================
@login_required
def siteconfigname(request):
@@ -405,6 +407,9 @@ def siteconfigname(request):
data['msg'] = ""
data['data'] = []
#获取数据标志 1 返回所有 其它返回未配置站点
flag = request.GET.get('flag', 0)
#得到排序字段
sort = request.GET.get('sort','name')
#得到排序规则
@@ -419,47 +424,12 @@ def siteconfigname(request):
data['count'] = SiteConfig.objects.count()
ormdata = SiteConfig.objects.order_by(order_by).all()
for i in ormdata:
data['data'].append({
"name":i.name,
"name_en_cn":i.name + "(" + i.name_cn + ")"
})
return JsonResponse(data)
#==================
@login_required
def siteconfigname2siteinfo(request):
"""
网站集合,去掉已经创建的站点名称
xmSelect
"""
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
#得到排序字段
sort = request.GET.get('sort','name')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
if int(flag) == 0:
#获取siteinfo中配置的站点名
ormdata_siteinfo = list(SiteInfo.objects.values_list('siteconfig_name', flat=True))
else:
order_by = '-' + sort
#获取所有记录
data['count'] = SiteConfig.objects.count()
ormdata = SiteConfig.objects.order_by(order_by).all()
#获取siteinfo中配置的站点名
ormdata_siteinfo = list(SiteInfo.objects.values_list('siteconfig_name', flat=True))
ormdata_siteinfo = []
for i in ormdata:
#忽略已经配置的站点
@@ -469,4 +439,52 @@ def siteconfigname2siteinfo(request):
"name_en_cn":i.name + "(" + i.name_cn + ")"
})
return JsonResponse(data)
#==================
@login_required
def select_siteinfo(request):
"""
RSS订阅
xmSelect
"""
data = {}
data['code'] = 0
data['msg'] = ""
data['data'] = []
#获取数据标志 1 返回所有 其它返回未配置站点
flag = request.GET.get('flag', 0)
#得到排序字段
sort = request.GET.get('sort','id')
#得到排序规则
order_by_type = request.GET.get('order','')
if order_by_type == 'asc':
order_by = sort
else:
order_by = '-' + sort
#获取所有记录
data['count'] = SiteInfo.objects.count()
ormdata = SiteInfo.objects.order_by(order_by).all()
if int(flag) == 0:
#获取RSS配置的站点ID
ormdata_config = list(Config.objects.values_list('siteinfo_id', flat=True))
else:
ormdata_config = []
for i in ormdata:
#只返回未配置rss的站点
if i.id not in ormdata_config:
data['data'].append({
"id":i.id,
"name":i.siteconfig_name + "(" + i.siteconfig_name_cn + ")"
})
return JsonResponse(data)

View File

@@ -17,6 +17,8 @@ thefuzz
#thefuzz[speedup]
#paddlepaddle
paddleocr
transmission-rpc
qbittorrent-api
#本地测试使用
#PyMySQL >= 1.0.2
#使用后台运行

View File

@@ -49,6 +49,17 @@
}
]
},
{
"title": "下载工具管理",
"icon": "layui-icon layui-icon-set",
"path": "javascript:;",
"children": [
{
"title": "下载器配置",
"path": "#/download/tools"
}
]
},
{
"title": "消息推送配置",
"icon": "layui-icon layui-icon-set",
@@ -72,6 +83,25 @@
}
]
},
{
"title": "RSS订阅管理",
"icon": "layui-icon layui-icon-set",
"path": "javascript:;",
"children": [
{
"title": "RSS配置",
"path": "#/rss/config"
},
{
"title": "RSS规则",
"path": "#/rss/rule"
},
{
"title": "RSS种子详情",
"path": "#/rss/seedinfo"
}
]
},
{
"title": "任务管理",
"icon": "layui-icon layui-icon-set",

BIN
static/screenshot/rss.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB