2021-01-03 19:41:58 +00:00
|
|
|
from asyncio import get_event_loop, set_event_loop, SelectorEventLoop
|
2020-04-24 19:01:44 +00:00
|
|
|
from functools import wraps
|
2020-04-09 19:02:34 +00:00
|
|
|
from operator import attrgetter, itemgetter
|
|
|
|
|
2021-01-29 20:32:28 +00:00
|
|
|
import requests
|
|
|
|
from flask import redirect, render_template, request, url_for, abort, json
|
|
|
|
from requests import RequestException
|
2020-04-09 19:02:34 +00:00
|
|
|
|
2020-12-08 19:18:17 +00:00
|
|
|
from . import utils
|
2021-01-29 20:32:28 +00:00
|
|
|
from .config import app, auth, logger, scheduler, ADMIN_USERNAME, ADMIN_PASSWORD, MYSQL_ENABLED, APP_PORT, IS_DEBUG, \
|
2021-07-10 21:15:06 +00:00
|
|
|
CLOUDPROXY_ENDPOINT, TRANSMISSION_ENABLED, transmission
|
|
|
|
from .connectors import get_instance, run_all, Nyaa
|
2020-12-08 16:17:24 +00:00
|
|
|
from .connectors.core import ConnectorLang, ConnectorReturn
|
2021-07-10 18:59:36 +00:00
|
|
|
from .forms import SearchForm, DeleteForm, EditForm, FolderDeleteForm, FolderEditForm
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
if MYSQL_ENABLED:
|
2020-04-20 17:40:11 +00:00
|
|
|
from .config import db
|
|
|
|
from .models import AnimeFolder, AnimeTitle, AnimeLink
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
def mysql_required(f):
|
|
|
|
@wraps(f)
|
|
|
|
def decorated_function(*args, **kwargs):
|
|
|
|
if not MYSQL_ENABLED:
|
|
|
|
return abort(404)
|
|
|
|
return f(*args, **kwargs)
|
|
|
|
|
|
|
|
return decorated_function
|
|
|
|
|
|
|
|
|
2021-07-11 08:10:25 +00:00
|
|
|
def clean_titles():
|
|
|
|
db.engine.execute("""
|
|
|
|
DELETE
|
|
|
|
FROM anime_title
|
|
|
|
WHERE id IN (
|
|
|
|
SELECT anime_title.id
|
|
|
|
FROM anime_title
|
|
|
|
LEFT JOIN anime_link ON anime_title.id = anime_link.title_id
|
|
|
|
WHERE anime_link.id IS NULL
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
2020-04-09 19:02:34 +00:00
|
|
|
@auth.verify_password
|
|
|
|
def verify_password(username, password):
|
2020-12-08 16:17:24 +00:00
|
|
|
return username == ADMIN_USERNAME and ADMIN_PASSWORD == password
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.template_filter('boldify')
|
|
|
|
def boldify(name):
|
|
|
|
query = request.args.get('q', '')
|
2020-04-24 19:01:44 +00:00
|
|
|
name = utils.boldify(name, query)
|
2020-04-09 19:02:34 +00:00
|
|
|
if MYSQL_ENABLED:
|
|
|
|
for keyword in db.session.query(AnimeTitle.keyword.distinct()).all():
|
|
|
|
if keyword[0].lower() != query.lower():
|
2020-04-24 19:01:44 +00:00
|
|
|
name = utils.boldify(name, keyword[0])
|
2020-04-09 19:02:34 +00:00
|
|
|
return name
|
|
|
|
|
|
|
|
|
|
|
|
@app.template_filter('flagify')
|
|
|
|
def flagify(is_vf):
|
|
|
|
return ConnectorLang.FR.value if is_vf else ConnectorLang.JP.value
|
|
|
|
|
|
|
|
|
|
|
|
@app.template_filter('colorify')
|
|
|
|
def colorify(model):
|
2020-04-24 19:01:44 +00:00
|
|
|
return get_instance(model.link, model.title.keyword).color
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
|
2021-01-03 19:41:58 +00:00
|
|
|
@app.context_processor
|
|
|
|
def inject_user():
|
|
|
|
return dict(mysql_disabled=not MYSQL_ENABLED)
|
|
|
|
|
|
|
|
|
2020-04-09 19:02:34 +00:00
|
|
|
@app.route('/')
|
|
|
|
def home():
|
2021-07-10 21:15:06 +00:00
|
|
|
return render_template('layout.html', search_form=SearchForm(), title='Anime torrents search engine')
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.route('/search')
|
|
|
|
def search():
|
|
|
|
query = request.args.get('q')
|
|
|
|
if not query:
|
|
|
|
return redirect(url_for('home'))
|
|
|
|
|
2021-01-03 19:41:58 +00:00
|
|
|
set_event_loop(SelectorEventLoop())
|
2021-07-11 08:10:25 +00:00
|
|
|
torrents = get_event_loop().run_until_complete(run_all(query))
|
|
|
|
return render_template('search.html', search_form=SearchForm(), connectors=torrents)
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.route('/latest')
|
|
|
|
@app.route('/latest/<int:page>')
|
|
|
|
def latest(page=1):
|
2021-01-03 19:41:58 +00:00
|
|
|
set_event_loop(SelectorEventLoop())
|
|
|
|
torrents = get_event_loop().run_until_complete(run_all('', return_type=ConnectorReturn.HISTORY, page=page))
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
results = []
|
|
|
|
for torrent in torrents:
|
|
|
|
results = results + torrent.data
|
|
|
|
for result in results:
|
2021-01-30 18:40:36 +00:00
|
|
|
result['self'] = get_instance(result['href'])
|
2020-04-09 19:02:34 +00:00
|
|
|
results.sort(key=itemgetter('date'), reverse=True)
|
|
|
|
|
2021-01-03 19:41:58 +00:00
|
|
|
return render_template('latest.html', search_form=SearchForm(), torrents=results, page=page)
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
@app.route('/list')
|
|
|
|
@app.route('/list/<url_filters>')
|
|
|
|
@mysql_required
|
|
|
|
def list_animes(url_filters='nyaa,yggtorrent'):
|
|
|
|
filters = None
|
|
|
|
for i, to_filter in enumerate(url_filters.split(',')):
|
|
|
|
if not i:
|
|
|
|
filters = AnimeLink.link.contains(to_filter)
|
|
|
|
else:
|
|
|
|
filters = filters | AnimeLink.link.contains(to_filter)
|
|
|
|
|
|
|
|
titles = db.session.query(AnimeTitle, AnimeLink).join(AnimeLink).filter(filters).order_by(AnimeTitle.name).all()
|
|
|
|
|
|
|
|
results = {}
|
|
|
|
for title, link in titles:
|
|
|
|
if title.id not in results:
|
|
|
|
results[title.id] = [link]
|
|
|
|
else:
|
|
|
|
results[title.id].append(link)
|
|
|
|
|
|
|
|
return render_template('list.html', search_form=SearchForm(), titles=results)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/admin', methods=['GET', 'POST'])
|
|
|
|
@mysql_required
|
|
|
|
@auth.login_required
|
|
|
|
def admin():
|
|
|
|
form = DeleteForm(request.form)
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
link = AnimeLink.query.filter_by(id=form.id.data).first()
|
|
|
|
if link:
|
2020-10-02 12:15:13 +00:00
|
|
|
form.message = '%s (%s) has been successfully deleted' % (link.title.name, link.season)
|
2020-04-09 19:02:34 +00:00
|
|
|
db.session.delete(link)
|
|
|
|
db.session.commit()
|
2021-07-10 21:51:07 +00:00
|
|
|
|
|
|
|
title = link.title
|
|
|
|
if title and not len(title.links):
|
|
|
|
db.session.delete(title)
|
|
|
|
db.session.commit()
|
2020-04-09 19:02:34 +00:00
|
|
|
else:
|
|
|
|
form._errors = {'id': ['Id %s was not found in the database' % form.id.data]}
|
|
|
|
|
2020-09-29 11:35:19 +00:00
|
|
|
folders = AnimeFolder.query.all()
|
|
|
|
for folder in folders:
|
|
|
|
for title in folder.titles:
|
|
|
|
title.links.sort(key=attrgetter('season'))
|
|
|
|
folder.titles.sort(key=attrgetter('name'))
|
|
|
|
|
2020-04-09 19:02:34 +00:00
|
|
|
return render_template('admin/list.html', search_form=SearchForm(), folders=folders, action_form=form)
|
|
|
|
|
|
|
|
|
2021-07-10 18:59:36 +00:00
|
|
|
@app.route('/admin/folder', methods=['GET', 'POST'])
|
|
|
|
@mysql_required
|
|
|
|
@auth.login_required
|
|
|
|
def folder_list():
|
|
|
|
form = FolderDeleteForm(request.form)
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
folder = AnimeFolder.query.filter_by(id=form.id.data).first()
|
|
|
|
if folder:
|
|
|
|
form.message = '%s has been successfully deleted' % folder.name
|
|
|
|
db.session.delete(folder)
|
|
|
|
db.session.commit()
|
|
|
|
else:
|
|
|
|
form._errors = {'id': ['Id %s was not found in the database' % form.id.data]}
|
|
|
|
|
|
|
|
folders = AnimeFolder.query.all()
|
|
|
|
|
|
|
|
return render_template('admin/folder/list.html', search_form=SearchForm(), folders=folders, action_form=form)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/admin/folder/edit', methods=['GET', 'POST'])
|
|
|
|
@app.route('/admin/folder/edit/<int:folder_id>', methods=['GET', 'POST'])
|
|
|
|
@mysql_required
|
|
|
|
@auth.login_required
|
|
|
|
def folder_edit(folder_id=None):
|
2021-07-10 21:15:06 +00:00
|
|
|
folder = AnimeFolder.query.filter_by(id=folder_id).first()
|
|
|
|
folder = folder if folder else AnimeFolder()
|
2021-07-10 18:59:36 +00:00
|
|
|
form = FolderEditForm(request.form, id=folder.id, name=folder.name, path=folder.path)
|
|
|
|
|
|
|
|
if form.validate_on_submit():
|
|
|
|
# Folder
|
|
|
|
folder.name = form.name.data
|
|
|
|
folder.path = form.path.data
|
|
|
|
db.session.add(folder)
|
|
|
|
db.session.commit()
|
|
|
|
return redirect(url_for('folder_list'))
|
|
|
|
|
|
|
|
return render_template('admin/folder/edit.html', search_form=SearchForm(), action_form=form)
|
|
|
|
|
|
|
|
|
2020-04-09 19:02:34 +00:00
|
|
|
@app.route('/admin/edit', methods=['GET', 'POST'])
|
|
|
|
@app.route('/admin/edit/<int:link_id>', methods=['GET', 'POST'])
|
|
|
|
@mysql_required
|
|
|
|
@auth.login_required
|
|
|
|
def admin_edit(link_id=None):
|
2021-07-10 21:15:06 +00:00
|
|
|
link = AnimeLink.query.filter_by(id=link_id).first()
|
|
|
|
link = link if link else AnimeLink()
|
2021-07-10 15:28:45 +00:00
|
|
|
|
2020-04-09 19:02:34 +00:00
|
|
|
folders = AnimeFolder.query.all()
|
2021-07-10 18:59:36 +00:00
|
|
|
form = EditForm(
|
|
|
|
request.form,
|
|
|
|
id=link.id,
|
2021-07-10 21:15:06 +00:00
|
|
|
folder=link.title.folder.id if link.title else None,
|
|
|
|
name=link.title.name if link.title else None,
|
2021-07-10 18:59:36 +00:00
|
|
|
link=link.link,
|
|
|
|
season=link.season,
|
2021-07-10 21:15:06 +00:00
|
|
|
keyword=link.title.keyword if link.title else None
|
2021-07-10 18:59:36 +00:00
|
|
|
)
|
2021-07-10 15:28:45 +00:00
|
|
|
form.folder.choices = [('', '')] + [(g.id, g.name) for g in folders]
|
2020-04-09 19:02:34 +00:00
|
|
|
|
|
|
|
if form.validate_on_submit():
|
2021-01-30 18:40:36 +00:00
|
|
|
# Instance for VF tag
|
|
|
|
instance = get_instance(form.link.data)
|
2021-07-10 21:15:06 +00:00
|
|
|
|
2020-08-15 07:39:45 +00:00
|
|
|
# Title
|
2021-07-10 21:45:48 +00:00
|
|
|
title = AnimeTitle.query.filter_by(id=link.title_id).first()
|
|
|
|
title = title if title else AnimeTitle.query.filter_by(name=form.name.data).first()
|
2020-04-09 19:02:34 +00:00
|
|
|
title = title if title else AnimeTitle()
|
2021-07-10 15:28:45 +00:00
|
|
|
title.folder_id = form.folder.data
|
2020-04-09 19:02:34 +00:00
|
|
|
title.name = form.name.data
|
2021-07-10 18:59:36 +00:00
|
|
|
title.keyword = form.keyword.data.lower()
|
2020-04-09 19:02:34 +00:00
|
|
|
db.session.add(title)
|
2020-09-29 11:26:59 +00:00
|
|
|
db.session.commit()
|
2021-07-10 21:15:06 +00:00
|
|
|
|
2020-08-15 07:39:45 +00:00
|
|
|
# Link
|
2020-04-09 19:02:34 +00:00
|
|
|
link.title_id = title.id
|
|
|
|
link.link = form.link.data
|
|
|
|
link.season = form.season.data
|
|
|
|
link.comment = form.comment.data
|
2021-01-30 18:40:36 +00:00
|
|
|
link.vf = instance.is_vf(form.link.data)
|
2021-07-10 21:15:06 +00:00
|
|
|
|
|
|
|
# Database
|
2020-04-09 19:02:34 +00:00
|
|
|
db.session.add(link)
|
|
|
|
db.session.commit()
|
2021-07-11 08:10:25 +00:00
|
|
|
clean_titles()
|
2021-07-10 21:15:06 +00:00
|
|
|
|
|
|
|
# Transmission
|
2021-07-11 08:30:28 +00:00
|
|
|
if TRANSMISSION_ENABLED and title.folder.path is not None and isinstance(instance, Nyaa):
|
2021-07-10 21:15:06 +00:00
|
|
|
download_url = link.link.replace('/view/', '/download/') + '.torrent'
|
|
|
|
torrent_path = '%s/%s' % (title.folder.path, title.name)
|
|
|
|
torrent = transmission.add_torrent(download_url, download_dir=torrent_path)
|
|
|
|
transmission.move_torrent_data(torrent.id, torrent_path)
|
|
|
|
|
2020-04-09 19:02:34 +00:00
|
|
|
return redirect(url_for('admin'))
|
|
|
|
|
2021-07-10 18:59:36 +00:00
|
|
|
return render_template('admin/edit.html', search_form=SearchForm(), folders=folders, action_form=form)
|
2020-04-26 11:55:18 +00:00
|
|
|
|
|
|
|
|
2021-01-29 20:32:28 +00:00
|
|
|
@scheduler.task('interval', id='flaredestroyy', days=1)
|
|
|
|
def flaredestroyy():
|
|
|
|
if CLOUDPROXY_ENDPOINT:
|
|
|
|
try:
|
|
|
|
json_session = requests.post(CLOUDPROXY_ENDPOINT, data=json.dumps({
|
|
|
|
'cmd': 'sessions.list'
|
|
|
|
}))
|
|
|
|
response = json.loads(json_session.text)
|
|
|
|
sessions = response['sessions']
|
|
|
|
|
|
|
|
for session in sessions:
|
|
|
|
requests.post(CLOUDPROXY_ENDPOINT, data=json.dumps({
|
|
|
|
'cmd': 'sessions.destroy',
|
|
|
|
'session': session
|
|
|
|
}))
|
|
|
|
logger.info('Destroyed %s' % session)
|
|
|
|
except RequestException as e:
|
|
|
|
logger.exception(e)
|
|
|
|
|
|
|
|
|
2020-04-26 11:55:18 +00:00
|
|
|
def run():
|
2021-01-29 20:32:28 +00:00
|
|
|
scheduler.start()
|
|
|
|
flaredestroyy()
|
2021-01-03 19:41:58 +00:00
|
|
|
app.run('0.0.0.0', APP_PORT, IS_DEBUG)
|