diff --git a/README.md b/README.md index b5923d7..91ad8ce 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # π 😼た -> "PyNyaaTa", Xéfir's personal animes torrent search engine +> "PyNyaaTa", Xéfir's personal anime torrent search engine [![Build Status](https://ci.crystalyx.net/api/badges/Xefir/PyNyaaTa/status.svg)](https://ci.crystalyx.net/Xefir/PyNyaaTa) [![Docker Hub](https://img.shields.io/docker/pulls/xefir/pynyaata)](https://hub.docker.com/r/xefir/pynyaata) diff --git a/docker-compose.yml b/docker-compose.yml index 8a2ce1b..56e63c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,7 @@ services: depends_on: - db - flaresolverr + - redis env_file: - .env.dist - .env diff --git a/pynyaata/__init__.py b/pynyaata/__init__.py index 5d40ff1..306e36d 100644 --- a/pynyaata/__init__.py +++ b/pynyaata/__init__.py @@ -8,8 +8,8 @@ from requests import RequestException from . import utils from .config import app, auth, logger, scheduler, ADMIN_USERNAME, ADMIN_PASSWORD, MYSQL_ENABLED, APP_PORT, IS_DEBUG, \ - CLOUDPROXY_ENDPOINT -from .connectors import get_instance, run_all + CLOUDPROXY_ENDPOINT, TRANSMISSION_ENABLED, transmission +from .connectors import get_instance, run_all, Nyaa from .connectors.core import ConnectorLang, ConnectorReturn from .forms import SearchForm, DeleteForm, EditForm, FolderDeleteForm, FolderEditForm @@ -61,7 +61,7 @@ def inject_user(): @app.route('/') def home(): - return render_template('layout.html', search_form=SearchForm(), title='Animes torrents search engine') + return render_template('layout.html', search_form=SearchForm(), title='Anime torrents search engine') @app.route('/search') @@ -171,16 +171,12 @@ def folder_list(): @mysql_required @auth.login_required def folder_edit(folder_id=None): - if folder_id: - folder = AnimeFolder.query.filter_by(id=folder_id).first() - else: - folder = utils.clean_model(AnimeFolder()) - + folder = AnimeFolder.query.filter_by(id=folder_id).first() + folder = folder if folder else AnimeFolder() form = FolderEditForm(request.form, id=folder.id, name=folder.name, path=folder.path) if form.validate_on_submit(): # Folder - folder.id = folder_id folder.name = form.name.data folder.path = form.path.data db.session.add(folder) @@ -195,29 +191,25 @@ def folder_edit(folder_id=None): @mysql_required @auth.login_required def admin_edit(link_id=None): - if link_id: - link = AnimeLink.query.filter_by(id=link_id).first() - else: - link = utils.clean_model(AnimeLink()) - link.title = utils.clean_model(AnimeTitle()) - link.title.folder = utils.clean_model(AnimeFolder()) - link.vf = False + link = AnimeLink.query.filter_by(id=link_id).first() + link = link if link else AnimeLink() folders = AnimeFolder.query.all() form = EditForm( request.form, id=link.id, - folder=link.title.folder.id, - name=link.title.name, + folder=link.title.folder.id if link.title else None, + name=link.title.name if link.title else None, link=link.link, season=link.season, - keyword=link.title.keyword + keyword=link.title.keyword if link.title else None ) form.folder.choices = [('', '')] + [(g.id, g.name) for g in folders] if form.validate_on_submit(): # Instance for VF tag instance = get_instance(form.link.data) + # Title title = AnimeTitle.query.filter_by(name=form.name.data).first() title = title if title else AnimeTitle() @@ -226,16 +218,26 @@ def admin_edit(link_id=None): title.keyword = form.keyword.data.lower() db.session.add(title) db.session.commit() + # Link - link.id = link_id link.title_id = title.id link.link = form.link.data link.season = form.season.data link.comment = form.comment.data link.vf = instance.is_vf(form.link.data) + + # Database db.session.add(link) db.session.commit() remove_garbage(link) + + # Transmission + if TRANSMISSION_ENABLED and isinstance(instance, Nyaa): + 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) + return redirect(url_for('admin')) return render_template('admin/edit.html', search_form=SearchForm(), folders=folders, action_form=form) @@ -262,7 +264,6 @@ def flaredestroyy(): def run(): - app.config['SQLALCHEMY_ECHO'] = IS_DEBUG scheduler.start() flaredestroyy() app.run('0.0.0.0', APP_PORT, IS_DEBUG) diff --git a/pynyaata/config.py b/pynyaata/config.py index b688da4..c9cd310 100644 --- a/pynyaata/config.py +++ b/pynyaata/config.py @@ -7,6 +7,7 @@ from flask_apscheduler import APScheduler from flask_httpauth import HTTPBasicAuth from flask_sqlalchemy import SQLAlchemy from redis import Redis +from transmission_rpc.client import Client load_dotenv() @@ -20,6 +21,7 @@ BLACKLIST_WORDS = environ.get('BLACKLIST_WORDS', '').split(',') if environ.get(' CLOUDPROXY_ENDPOINT = environ.get('CLOUDPROXY_ENDPOINT') MYSQL_ENABLED = False REDIS_ENABLED = False +TRANSMISSION_ENABLED = False app = Flask(__name__) app.name = 'PyNyaaTa' @@ -44,6 +46,7 @@ if db_host: db_user, db_password, db_host, db_name ) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True + app.config['SQLALCHEMY_ECHO'] = False app.config['SQLALCHEMY_ENGINE_OPTIONS'] = { 'pool_recycle': 200 } @@ -56,3 +59,10 @@ cache_host = environ.get('REDIS_SERVER') if cache_host: REDIS_ENABLED = True cache = Redis(cache_host) + +transmission_host = environ.get('TRANSMISSION_SERVER') +if transmission_host: + TRANSMISSION_ENABLED = True + transmission_username = environ.get('TRANSMISSION_RPC_USERNAME') + transmission_password = environ.get('TRANSMISSION_RPC_PASSWORD') + transmission = Client(username=transmission_username, password=transmission_password, host=transmission_host) diff --git a/pynyaata/connectors/core.py b/pynyaata/connectors/core.py index 449ad71..bcaecaf 100644 --- a/pynyaata/connectors/core.py +++ b/pynyaata/connectors/core.py @@ -6,6 +6,7 @@ from urllib.parse import urlencode import requests from requests import RequestException +from redis.exceptions import RedisError from ..config import CACHE_TIMEOUT, REQUESTS_TIMEOUT, CLOUDPROXY_ENDPOINT, logger, REDIS_ENABLED @@ -33,7 +34,12 @@ class Cache: key = 'pynyaata.%s.%s.%s.%s' % (connector.__class__.__name__, f.__name__, connector.query, connector.page) if REDIS_ENABLED: - json = cache.get(key) + json = None + + try: + json = cache.get(key) + except RedisError: + pass if json: data = loads(json) @@ -45,10 +51,13 @@ class Cache: ret = f(*args, **kwds) if not connector.on_error and REDIS_ENABLED: - cache.set(key, dumps({ - 'data': connector.data, - 'is_more': connector.is_more - }), CACHE_TIMEOUT) + try: + cache.set(key, dumps({ + 'data': connector.data, + 'is_more': connector.is_more + }), CACHE_TIMEOUT) + except RedisError: + pass return ret diff --git a/requirements.txt b/requirements.txt index 390d347..da07ff4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,4 @@ beautifulsoup4==4.9.3 python-dotenv==0.18.0 dateparser==1.0.0 redis==3.5.3 +transmission-rpc==3.2.6