mirror of
https://gitlab.com/SIGBUS/nyaa.git
synced 2024-12-22 13:49:59 +00:00
commit
ad17558ec3
|
@ -60,4 +60,4 @@ assets = Environment(app)
|
||||||
# output='style.css', depends='**/*.scss')
|
# output='style.css', depends='**/*.scss')
|
||||||
# assets.register('style_all', css)
|
# assets.register('style_all', css)
|
||||||
|
|
||||||
from nyaa import routes
|
from nyaa import routes # noqa
|
||||||
|
|
|
@ -10,7 +10,7 @@ from orderedset import OrderedSet
|
||||||
from werkzeug import secure_filename
|
from werkzeug import secure_filename
|
||||||
|
|
||||||
DEBUG_API = False
|
DEBUG_API = False
|
||||||
#################################### API ROUTES ####################################
|
# #################################### API ROUTES ####################################
|
||||||
CATEGORIES = [
|
CATEGORIES = [
|
||||||
('Anime', ['Anime Music Video', 'English-translated', 'Non-English-translated', 'Raw']),
|
('Anime', ['Anime Music Video', 'English-translated', 'Non-English-translated', 'Raw']),
|
||||||
('Audio', ['Lossless', 'Lossy']),
|
('Audio', ['Lossless', 'Lossy']),
|
||||||
|
@ -30,7 +30,7 @@ def validate_main_sub_cat(main_cat_name, sub_cat_name):
|
||||||
cat_id = main_cat.id_as_string
|
cat_id = main_cat.id_as_string
|
||||||
sub_cat_id = sub_cat.id_as_string
|
sub_cat_id = sub_cat.id_as_string
|
||||||
cat_sub_cat = sub_cat_id.split('_')
|
cat_sub_cat = sub_cat_id.split('_')
|
||||||
#print('cat: {0} sub_cat: {1}'.format(cat_sub_cat[0], cat_sub_cat[1]))
|
# print('cat: {0} sub_cat: {1}'.format(cat_sub_cat[0], cat_sub_cat[1]))
|
||||||
|
|
||||||
return True, cat_sub_cat[0], cat_sub_cat[1]
|
return True, cat_sub_cat[0], cat_sub_cat[1]
|
||||||
|
|
||||||
|
@ -112,17 +112,22 @@ def api_upload(upload_request):
|
||||||
if DEBUG_API:
|
if DEBUG_API:
|
||||||
print(json.dumps(j, indent=4))
|
print(json.dumps(j, indent=4))
|
||||||
|
|
||||||
_json_keys = ['username', 'password',
|
_json_keys = ['username',
|
||||||
'display_name', 'main_cat', 'sub_cat', 'flags'] # 'information' and 'description' are not required
|
'password',
|
||||||
|
'display_name',
|
||||||
|
'main_cat',
|
||||||
|
'sub_cat',
|
||||||
|
'flags'] # 'information' and 'description' are not required
|
||||||
# Check that required fields are present
|
# Check that required fields are present
|
||||||
for _k in _json_keys:
|
for _k in _json_keys:
|
||||||
if _k not in j.keys():
|
if _k not in j.keys():
|
||||||
return flask.make_response(flask.jsonify({"Error": "Missing JSON field: {0}.".format(_k)}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Missing JSON field: {0}.".format(_k)}), 400)
|
||||||
# Check that no extra fields are present
|
# Check that no extra fields are present
|
||||||
for k in j.keys():
|
for k in j.keys():
|
||||||
if k not in ['username', 'password',
|
if k not in set(_json_keys + ['information', 'description']):
|
||||||
'display_name', 'main_cat', 'sub_cat', 'information', 'description', 'flags']:
|
return flask.make_response(flask.jsonify(
|
||||||
return flask.make_response(flask.jsonify({"Error": "Incorrect JSON field(s)."}), 400)
|
{"Error": "Incorrect JSON field(s)."}), 400)
|
||||||
else:
|
else:
|
||||||
return flask.make_response(flask.jsonify({"Error": "No metadata."}), 400)
|
return flask.make_response(flask.jsonify({"Error": "No metadata."}), 400)
|
||||||
if 'torrent' in upload_request.files:
|
if 'torrent' in upload_request.files:
|
||||||
|
@ -143,14 +148,17 @@ def api_upload(upload_request):
|
||||||
if not user:
|
if not user:
|
||||||
user = models.User.by_email(username)
|
user = models.User.by_email(username)
|
||||||
|
|
||||||
if not user or password != user.password_hash or user.status == models.UserStatusType.INACTIVE:
|
if (not user or password != user.password_hash
|
||||||
return flask.make_response(flask.jsonify({"Error": "Incorrect username or password"}), 403)
|
or user.status == models.UserStatusType.INACTIVE):
|
||||||
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Incorrect username or password"}), 403)
|
||||||
|
|
||||||
current_user = user
|
current_user = user
|
||||||
|
|
||||||
display_name = j['display_name']
|
display_name = j['display_name']
|
||||||
if (len(display_name) < 3) or (len(display_name) > 1024):
|
if (len(display_name) < 3) or (len(display_name) > 1024):
|
||||||
return flask.make_response(flask.jsonify({"Error": "Torrent name must be between 3 and 1024 characters."}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Torrent name must be between 3 and 1024 characters."}), 400)
|
||||||
|
|
||||||
main_cat_name = j['main_cat']
|
main_cat_name = j['main_cat']
|
||||||
sub_cat_name = j['sub_cat']
|
sub_cat_name = j['sub_cat']
|
||||||
|
@ -158,14 +166,16 @@ def api_upload(upload_request):
|
||||||
cat_subcat_status, cat_id, sub_cat_id = validate_main_sub_cat(
|
cat_subcat_status, cat_id, sub_cat_id = validate_main_sub_cat(
|
||||||
main_cat_name, sub_cat_name)
|
main_cat_name, sub_cat_name)
|
||||||
if not cat_subcat_status:
|
if not cat_subcat_status:
|
||||||
return flask.make_response(flask.jsonify({"Error": "Incorrect Category / Sub-Category."}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Incorrect Category / Sub-Category."}), 400)
|
||||||
|
|
||||||
# TODO Sanitize information
|
# TODO Sanitize information
|
||||||
information = None
|
information = None
|
||||||
try:
|
try:
|
||||||
information = j['information']
|
information = j['information']
|
||||||
if len(information) > 255:
|
if len(information) > 255:
|
||||||
return flask.make_response(flask.jsonify({"Error": "Information is limited to 255 characters."}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Information is limited to 255 characters."}), 400)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
information = ''
|
information = ''
|
||||||
|
|
||||||
|
@ -173,8 +183,10 @@ def api_upload(upload_request):
|
||||||
description = None
|
description = None
|
||||||
try:
|
try:
|
||||||
description = j['description']
|
description = j['description']
|
||||||
if len(description) > (10 * 1024):
|
limit = 10 * 1024
|
||||||
return flask.make_response(flask.jsonify({"Error": "Description is limited to {0} characters.".format(10 * 1024)}), 403)
|
if len(description) > limit:
|
||||||
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Description is limited to {0} characters.".format(limit)}), 403)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
description = ''
|
description = ''
|
||||||
|
|
||||||
|
@ -182,13 +194,15 @@ def api_upload(upload_request):
|
||||||
if v_flags:
|
if v_flags:
|
||||||
torrent_flags = j['flags']
|
torrent_flags = j['flags']
|
||||||
else:
|
else:
|
||||||
return flask.make_response(flask.jsonify({"Error": "Incorrect torrent flags."}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Incorrect torrent flags."}), 400)
|
||||||
|
|
||||||
torrent_status, torrent_data = validate_torrent_file(
|
torrent_status, torrent_data = validate_torrent_file(
|
||||||
torrent_file.filename, torrent_file.read()) # Needs validation
|
torrent_file.filename, torrent_file.read()) # Needs validation
|
||||||
|
|
||||||
if not torrent_status:
|
if not torrent_status:
|
||||||
return flask.make_response(flask.jsonify({"Error": "Invalid or Duplicate torrent file."}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Invalid or Duplicate torrent file."}), 400)
|
||||||
|
|
||||||
# The torrent has been validated and is safe to access with ['foo'] etc - all relevant
|
# The torrent has been validated and is safe to access with ['foo'] etc - all relevant
|
||||||
# keys and values have been checked for (see UploadForm in forms.py for details)
|
# keys and values have been checked for (see UploadForm in forms.py for details)
|
||||||
|
@ -297,21 +311,24 @@ def api_upload(upload_request):
|
||||||
# Store tracker refs in DB
|
# Store tracker refs in DB
|
||||||
for order, tracker in enumerate(db_trackers):
|
for order, tracker in enumerate(db_trackers):
|
||||||
torrent_tracker = models.TorrentTrackers(torrent_id=torrent.id,
|
torrent_tracker = models.TorrentTrackers(torrent_id=torrent.id,
|
||||||
tracker_id=tracker.id, order=order)
|
tracker_id=tracker.id, order=order)
|
||||||
db.session.add(torrent_tracker)
|
db.session.add(torrent_tracker)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
if app.config.get('BACKUP_TORRENT_FOLDER'):
|
if app.config.get('BACKUP_TORRENT_FOLDER'):
|
||||||
torrent_file.seek(0, 0)
|
torrent_file.seek(0, 0)
|
||||||
torrent_path = os.path.join(app.config['BACKUP_TORRENT_FOLDER'], '{}.{}'.format(torrent.id, secure_filename(torrent_file.filename)))
|
torrent_path = os.path.join(app.config['BACKUP_TORRENT_FOLDER'], '{}.{}'.format(
|
||||||
|
torrent.id, secure_filename(torrent_file.filename)))
|
||||||
torrent_file.save(torrent_path)
|
torrent_file.save(torrent_path)
|
||||||
torrent_file.close()
|
torrent_file.close()
|
||||||
|
|
||||||
#print('Success? {0}'.format(torrent.id))
|
# print('Success? {0}'.format(torrent.id))
|
||||||
return flask.make_response(flask.jsonify({"Success": "Request was processed {0}".format(torrent.id)}), 200)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Success": "Request was processed {0}".format(torrent.id)}), 200)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Exception: {0}'.format(e))
|
print('Exception: {0}'.format(e))
|
||||||
return flask.make_response(flask.jsonify({"Error": "Incorrect JSON. Please see HELP page for examples."}), 400)
|
return flask.make_response(flask.jsonify(
|
||||||
|
{"Error": "Incorrect JSON. Please see HELP page for examples."}), 400)
|
||||||
else:
|
else:
|
||||||
return flask.make_response(flask.jsonify({"Error": "Bad request"}), 400)
|
return flask.make_response(flask.jsonify({"Error": "Bad request"}), 400)
|
||||||
|
|
|
@ -72,7 +72,8 @@ def handle_torrent_upload(upload_form, uploading_user=None):
|
||||||
models.UserLevelType.TRUSTED) if uploading_user else False
|
models.UserLevelType.TRUSTED) if uploading_user else False
|
||||||
|
|
||||||
# Set category ids
|
# Set category ids
|
||||||
torrent.main_category_id, torrent.sub_category_id = upload_form.category.parsed_data.get_category_ids()
|
torrent.main_category_id, torrent.sub_category_id = \
|
||||||
|
upload_form.category.parsed_data.get_category_ids()
|
||||||
# print('Main cat id: {0}, Sub cat id: {1}'.format(
|
# print('Main cat id: {0}, Sub cat id: {1}'.format(
|
||||||
# torrent.main_category_id, torrent.sub_category_id))
|
# torrent.main_category_id, torrent.sub_category_id))
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@ def handle_torrent_upload(upload_form, uploading_user=None):
|
||||||
# Store tracker refs in DB
|
# Store tracker refs in DB
|
||||||
for order, tracker in enumerate(db_trackers):
|
for order, tracker in enumerate(db_trackers):
|
||||||
torrent_tracker = models.TorrentTrackers(torrent_id=torrent.id,
|
torrent_tracker = models.TorrentTrackers(torrent_id=torrent.id,
|
||||||
tracker_id=tracker.id, order=order)
|
tracker_id=tracker.id, order=order)
|
||||||
db.session.add(torrent_tracker)
|
db.session.add(torrent_tracker)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
@ -156,8 +157,9 @@ def handle_torrent_upload(upload_form, uploading_user=None):
|
||||||
if not os.path.exists(torrent_dir):
|
if not os.path.exists(torrent_dir):
|
||||||
os.makedirs(torrent_dir)
|
os.makedirs(torrent_dir)
|
||||||
|
|
||||||
torrent_path = os.path.join(torrent_dir, '{}.{}'.format(torrent.id, secure_filename(torrent_file.filename)))
|
torrent_path = os.path.join(torrent_dir, '{}.{}'.format(
|
||||||
|
torrent.id, secure_filename(torrent_file.filename)))
|
||||||
torrent_file.save(torrent_path)
|
torrent_file.save(torrent_path)
|
||||||
torrent_file.close()
|
torrent_file.close()
|
||||||
|
|
||||||
return torrent
|
return torrent
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from flask_sqlalchemy import Pagination, BaseQuery
|
from flask_sqlalchemy import Pagination, BaseQuery
|
||||||
from flask import abort
|
from flask import abort
|
||||||
|
|
||||||
|
|
||||||
def paginate_faste(self, page=1, per_page=50, max_page=None, step=5):
|
def paginate_faste(self, page=1, per_page=50, max_page=None, step=5):
|
||||||
if page < 1:
|
if page < 1:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -25,4 +26,5 @@ def paginate_faste(self, page=1, per_page=50, max_page=None, step=5):
|
||||||
|
|
||||||
return Pagination(self, page, per_page, total, items)
|
return Pagination(self, page, per_page, total, items)
|
||||||
|
|
||||||
|
|
||||||
BaseQuery.paginate_faste = paginate_faste
|
BaseQuery.paginate_faste = paginate_faste
|
||||||
|
|
|
@ -126,7 +126,8 @@ class DisabledSelectField(SelectField):
|
||||||
class EditForm(FlaskForm):
|
class EditForm(FlaskForm):
|
||||||
display_name = TextField('Torrent display name', [
|
display_name = TextField('Torrent display name', [
|
||||||
Length(min=3, max=255,
|
Length(min=3, max=255,
|
||||||
message='Torrent display name must be at least %(min)d characters long and %(max)d at most.')
|
message='Torrent display name must be at least %(min)d characters long '
|
||||||
|
'and %(max)d at most.')
|
||||||
])
|
])
|
||||||
|
|
||||||
category = DisabledSelectField('Category')
|
category = DisabledSelectField('Category')
|
||||||
|
@ -172,7 +173,8 @@ class UploadForm(FlaskForm):
|
||||||
display_name = TextField('Torrent display name (optional)', [
|
display_name = TextField('Torrent display name (optional)', [
|
||||||
Optional(),
|
Optional(),
|
||||||
Length(min=3, max=255,
|
Length(min=3, max=255,
|
||||||
message='Torrent display name must be at least %(min)d characters long and %(max)d at most.')
|
message='Torrent display name must be at least %(min)d characters long and '
|
||||||
|
'%(max)d at most.')
|
||||||
])
|
])
|
||||||
|
|
||||||
# category = SelectField('Category')
|
# category = SelectField('Category')
|
||||||
|
@ -209,7 +211,7 @@ class UploadForm(FlaskForm):
|
||||||
# Decode and ensure data is bencoded data
|
# Decode and ensure data is bencoded data
|
||||||
try:
|
try:
|
||||||
torrent_dict = bencode.decode(field.data)
|
torrent_dict = bencode.decode(field.data)
|
||||||
#field.data.close()
|
# field.data.close()
|
||||||
except (bencode.MalformedBencodeException, UnicodeError):
|
except (bencode.MalformedBencodeException, UnicodeError):
|
||||||
raise ValidationError('Malformed torrent file')
|
raise ValidationError('Malformed torrent file')
|
||||||
|
|
||||||
|
@ -221,7 +223,6 @@ class UploadForm(FlaskForm):
|
||||||
except AssertionError as e:
|
except AssertionError as e:
|
||||||
raise ValidationError('Malformed torrent metadata ({})'.format(e.args[0]))
|
raise ValidationError('Malformed torrent metadata ({})'.format(e.args[0]))
|
||||||
|
|
||||||
|
|
||||||
site_tracker = app.config.get('MAIN_ANNOUNCE_URL')
|
site_tracker = app.config.get('MAIN_ANNOUNCE_URL')
|
||||||
ensure_tracker = app.config.get('ENFORCE_MAIN_ANNOUNCE_URL')
|
ensure_tracker = app.config.get('ENFORCE_MAIN_ANNOUNCE_URL')
|
||||||
|
|
||||||
|
@ -233,11 +234,12 @@ class UploadForm(FlaskForm):
|
||||||
# Ensure private torrents are using our tracker
|
# Ensure private torrents are using our tracker
|
||||||
if torrent_dict['info'].get('private') == 1:
|
if torrent_dict['info'].get('private') == 1:
|
||||||
if torrent_dict['announce'].decode('utf-8') != site_tracker:
|
if torrent_dict['announce'].decode('utf-8') != site_tracker:
|
||||||
raise ValidationError('Private torrent: please set {} as the main tracker'.format(site_tracker))
|
raise ValidationError(
|
||||||
|
'Private torrent: please set {} as the main tracker'.format(site_tracker))
|
||||||
|
|
||||||
elif ensure_tracker and not tracker_found:
|
elif ensure_tracker and not tracker_found:
|
||||||
raise ValidationError('Please include {} in the trackers of the torrent'.format(site_tracker))
|
raise ValidationError(
|
||||||
|
'Please include {} in the trackers of the torrent'.format(site_tracker))
|
||||||
|
|
||||||
# Note! bencode will sort dict keys, as per the spec
|
# Note! bencode will sort dict keys, as per the spec
|
||||||
# This may result in a different hash if the uploaded torrent does not match the
|
# This may result in a different hash if the uploaded torrent does not match the
|
||||||
|
@ -266,11 +268,13 @@ class TorrentFileData(object):
|
||||||
|
|
||||||
# https://wiki.theory.org/BitTorrentSpecification#Metainfo_File_Structure
|
# https://wiki.theory.org/BitTorrentSpecification#Metainfo_File_Structure
|
||||||
|
|
||||||
|
|
||||||
def _validate_trackers(torrent_dict, tracker_to_check_for=None):
|
def _validate_trackers(torrent_dict, tracker_to_check_for=None):
|
||||||
announce = torrent_dict.get('announce')
|
announce = torrent_dict.get('announce')
|
||||||
announce_string = _validate_bytes(announce, 'announce', 'utf-8')
|
announce_string = _validate_bytes(announce, 'announce', 'utf-8')
|
||||||
|
|
||||||
tracker_found = tracker_to_check_for and (announce_string.lower() == tracker_to_check_for.lower()) or False
|
tracker_found = tracker_to_check_for and (
|
||||||
|
announce_string.lower() == tracker_to_check_for.lower()) or False
|
||||||
|
|
||||||
announce_list = torrent_dict.get('announce-list')
|
announce_list = torrent_dict.get('announce-list')
|
||||||
if announce_list is not None:
|
if announce_list is not None:
|
||||||
|
|
|
@ -41,8 +41,10 @@ class TorrentFlags(IntEnum):
|
||||||
COMPLETE = 16
|
COMPLETE = 16
|
||||||
DELETED = 32
|
DELETED = 32
|
||||||
|
|
||||||
|
|
||||||
DB_TABLE_PREFIX = app.config['TABLE_PREFIX']
|
DB_TABLE_PREFIX = app.config['TABLE_PREFIX']
|
||||||
|
|
||||||
|
|
||||||
class Torrent(db.Model):
|
class Torrent(db.Model):
|
||||||
__tablename__ = DB_TABLE_PREFIX + 'torrents'
|
__tablename__ = DB_TABLE_PREFIX + 'torrents'
|
||||||
|
|
||||||
|
@ -83,8 +85,9 @@ class Torrent(db.Model):
|
||||||
main_category = db.relationship('MainCategory', uselist=False,
|
main_category = db.relationship('MainCategory', uselist=False,
|
||||||
back_populates='torrents', lazy="joined")
|
back_populates='torrents', lazy="joined")
|
||||||
sub_category = db.relationship('SubCategory', uselist=False, backref='torrents', lazy="joined",
|
sub_category = db.relationship('SubCategory', uselist=False, backref='torrents', lazy="joined",
|
||||||
primaryjoin="and_(SubCategory.id == foreign(Torrent.sub_category_id), "
|
primaryjoin=(
|
||||||
"SubCategory.main_category_id == Torrent.main_category_id)")
|
"and_(SubCategory.id == foreign(Torrent.sub_category_id), "
|
||||||
|
"SubCategory.main_category_id == Torrent.main_category_id)"))
|
||||||
info = db.relationship('TorrentInfo', uselist=False, back_populates='torrent')
|
info = db.relationship('TorrentInfo', uselist=False, back_populates='torrent')
|
||||||
filelist = db.relationship('TorrentFilelist', uselist=False, back_populates='torrent')
|
filelist = db.relationship('TorrentFilelist', uselist=False, back_populates='torrent')
|
||||||
stats = db.relationship('Statistic', uselist=False, back_populates='torrent', lazy='joined')
|
stats = db.relationship('Statistic', uselist=False, back_populates='torrent', lazy='joined')
|
||||||
|
@ -118,7 +121,6 @@ class Torrent(db.Model):
|
||||||
# Escaped
|
# Escaped
|
||||||
return escape_markup(self.information)
|
return escape_markup(self.information)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def magnet_uri(self):
|
def magnet_uri(self):
|
||||||
return create_magnet(self)
|
return create_magnet(self)
|
||||||
|
@ -224,7 +226,8 @@ class Trackers(db.Model):
|
||||||
__tablename__ = 'trackers'
|
__tablename__ = 'trackers'
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
uri = db.Column(db.String(length=255, collation=COL_UTF8_GENERAL_CI), nullable=False, unique=True)
|
uri = db.Column(db.String(length=255, collation=COL_UTF8_GENERAL_CI),
|
||||||
|
nullable=False, unique=True)
|
||||||
disabled = db.Column(db.Boolean, nullable=False, default=False)
|
disabled = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -235,8 +238,10 @@ class Trackers(db.Model):
|
||||||
class TorrentTrackers(db.Model):
|
class TorrentTrackers(db.Model):
|
||||||
__tablename__ = DB_TABLE_PREFIX + 'torrent_trackers'
|
__tablename__ = DB_TABLE_PREFIX + 'torrent_trackers'
|
||||||
|
|
||||||
torrent_id = db.Column(db.Integer, db.ForeignKey(DB_TABLE_PREFIX + 'torrents.id', ondelete="CASCADE"), primary_key=True)
|
torrent_id = db.Column(db.Integer, db.ForeignKey(
|
||||||
tracker_id = db.Column(db.Integer, db.ForeignKey('trackers.id', ondelete="CASCADE"), primary_key=True)
|
DB_TABLE_PREFIX + 'torrents.id', ondelete="CASCADE"), primary_key=True)
|
||||||
|
tracker_id = db.Column(db.Integer, db.ForeignKey(
|
||||||
|
'trackers.id', ondelete="CASCADE"), primary_key=True)
|
||||||
order = db.Column(db.Integer, nullable=False, index=True)
|
order = db.Column(db.Integer, nullable=False, index=True)
|
||||||
|
|
||||||
tracker = db.relationship('Trackers', uselist=False, lazy='joined')
|
tracker = db.relationship('Trackers', uselist=False, lazy='joined')
|
||||||
|
|
|
@ -31,8 +31,9 @@ from flask_paginate import Pagination
|
||||||
DEBUG_API = False
|
DEBUG_API = False
|
||||||
DEFAULT_MAX_SEARCH_RESULT = 1000
|
DEFAULT_MAX_SEARCH_RESULT = 1000
|
||||||
DEFAULT_PER_PAGE = 75
|
DEFAULT_PER_PAGE = 75
|
||||||
SERACH_PAGINATE_DISPLAY_MSG = '''Displaying results {start}-{end} out of {total} results.<br>
|
SERACH_PAGINATE_DISPLAY_MSG = ('Displaying results {start}-{end} out of {total} results.<br>\n'
|
||||||
Please refine your search results if you can't find what you were looking for.'''
|
'Please refine your search results if you can\'t find '
|
||||||
|
'what you were looking for.')
|
||||||
|
|
||||||
|
|
||||||
def redirect_url():
|
def redirect_url():
|
||||||
|
@ -76,7 +77,7 @@ def before_request():
|
||||||
|
|
||||||
flask.g.user = user
|
flask.g.user = user
|
||||||
|
|
||||||
if not 'timeout' in flask.session or flask.session['timeout'] < datetime.now():
|
if not 'timeout' not in flask.session or flask.session['timeout'] < datetime.now():
|
||||||
flask.session['timeout'] = datetime.now() + timedelta(days=7)
|
flask.session['timeout'] = datetime.now() + timedelta(days=7)
|
||||||
flask.session.permanent = True
|
flask.session.permanent = True
|
||||||
flask.session.modified = True
|
flask.session.modified = True
|
||||||
|
@ -160,7 +161,8 @@ def home(rss):
|
||||||
if not max_search_results:
|
if not max_search_results:
|
||||||
max_search_results = DEFAULT_MAX_SEARCH_RESULT
|
max_search_results = DEFAULT_MAX_SEARCH_RESULT
|
||||||
|
|
||||||
max_page = min(query_args['page'], int(math.ceil(max_search_results / float(per_page)))) # Only allow up to (max_search_results / page) pages
|
# Only allow up to (max_search_results / page) pages
|
||||||
|
max_page = min(query_args['page'], int(math.ceil(max_search_results / float(per_page))))
|
||||||
|
|
||||||
query_args['page'] = max_page
|
query_args['page'] = max_page
|
||||||
query_args['max_search_results'] = max_search_results
|
query_args['max_search_results'] = max_search_results
|
||||||
|
@ -186,7 +188,7 @@ def home(rss):
|
||||||
# If ES is enabled, default to db search for browsing
|
# If ES is enabled, default to db search for browsing
|
||||||
if use_elastic:
|
if use_elastic:
|
||||||
query_args['term'] = ''
|
query_args['term'] = ''
|
||||||
else: # Otherwise, use db search for everything
|
else: # Otherwise, use db search for everything
|
||||||
query_args['term'] = term or ''
|
query_args['term'] = term or ''
|
||||||
|
|
||||||
query = search_db(**query_args)
|
query = search_db(**query_args)
|
||||||
|
@ -251,7 +253,8 @@ def view_user(user_name):
|
||||||
if not max_search_results:
|
if not max_search_results:
|
||||||
max_search_results = DEFAULT_MAX_SEARCH_RESULT
|
max_search_results = DEFAULT_MAX_SEARCH_RESULT
|
||||||
|
|
||||||
max_page = min(query_args['page'], int(math.ceil(max_search_results / float(per_page)))) # Only allow up to (max_search_results / page) pages
|
# Only allow up to (max_search_results / page) pages
|
||||||
|
max_page = min(query_args['page'], int(math.ceil(max_search_results / float(per_page))))
|
||||||
|
|
||||||
query_args['page'] = max_page
|
query_args['page'] = max_page
|
||||||
query_args['max_search_results'] = max_search_results
|
query_args['max_search_results'] = max_search_results
|
||||||
|
@ -291,6 +294,7 @@ def view_user(user_name):
|
||||||
def _jinja2_filter_rfc822(date, fmt=None):
|
def _jinja2_filter_rfc822(date, fmt=None):
|
||||||
return formatdate(float(date.strftime('%s')))
|
return formatdate(float(date.strftime('%s')))
|
||||||
|
|
||||||
|
|
||||||
@app.template_filter('rfc822_es')
|
@app.template_filter('rfc822_es')
|
||||||
def _jinja2_filter_rfc822(datestr, fmt=None):
|
def _jinja2_filter_rfc822(datestr, fmt=None):
|
||||||
return formatdate(float(datetime.strptime(datestr, '%Y-%m-%dT%H:%M:%S').strftime('%s')))
|
return formatdate(float(datetime.strptime(datestr, '%Y-%m-%dT%H:%M:%S').strftime('%s')))
|
||||||
|
@ -311,7 +315,7 @@ def render_rss(label, query, use_elastic):
|
||||||
|
|
||||||
# @app.route('/about', methods=['GET'])
|
# @app.route('/about', methods=['GET'])
|
||||||
# def about():
|
# def about():
|
||||||
# return flask.render_template('about.html')
|
# return flask.render_template('about.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
@ -328,7 +332,8 @@ def login():
|
||||||
if not user:
|
if not user:
|
||||||
user = models.User.by_email(username)
|
user = models.User.by_email(username)
|
||||||
|
|
||||||
if not user or password != user.password_hash or user.status == models.UserStatusType.INACTIVE:
|
if (not user or password != user.password_hash
|
||||||
|
or user.status == models.UserStatusType.INACTIVE):
|
||||||
flask.flash(flask.Markup(
|
flask.flash(flask.Markup(
|
||||||
'<strong>Login failed!</strong> Incorrect username or password.'), 'danger')
|
'<strong>Login failed!</strong> Incorrect username or password.'), 'danger')
|
||||||
return flask.redirect(flask.url_for('login'))
|
return flask.redirect(flask.url_for('login'))
|
||||||
|
@ -511,7 +516,8 @@ def edit_torrent(torrent_id):
|
||||||
|
|
||||||
if flask.request.method == 'POST' and form.validate():
|
if flask.request.method == 'POST' and form.validate():
|
||||||
# Form has been sent, edit torrent with data.
|
# Form has been sent, edit torrent with data.
|
||||||
torrent.main_category_id, torrent.sub_category_id = form.category.parsed_data.get_category_ids()
|
torrent.main_category_id, torrent.sub_category_id = \
|
||||||
|
form.category.parsed_data.get_category_ids()
|
||||||
torrent.display_name = (form.display_name.data or '').strip()
|
torrent.display_name = (form.display_name.data or '').strip()
|
||||||
torrent.information = (form.information.data or '').strip()
|
torrent.information = (form.information.data or '').strip()
|
||||||
torrent.description = (form.description.data or '').strip()
|
torrent.description = (form.description.data or '').strip()
|
||||||
|
@ -538,7 +544,10 @@ def edit_torrent(torrent_id):
|
||||||
form.is_complete.data = torrent.complete
|
form.is_complete.data = torrent.complete
|
||||||
form.is_anonymous.data = torrent.anonymous
|
form.is_anonymous.data = torrent.anonymous
|
||||||
|
|
||||||
return flask.render_template('edit.html', form=form, torrent=torrent, admin=flask.g.user.is_admin)
|
return flask.render_template('edit.html',
|
||||||
|
form=form,
|
||||||
|
torrent=torrent,
|
||||||
|
admin=flask.g.user.is_admin)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/view/<int:torrent_id>/magnet')
|
@app.route('/view/<int:torrent_id>/magnet')
|
||||||
|
@ -590,8 +599,10 @@ def get_activation_link(user):
|
||||||
|
|
||||||
|
|
||||||
def send_verification_email(to_address, activ_link):
|
def send_verification_email(to_address, activ_link):
|
||||||
''' this is until we have our own mail server, obviously. This can be greatly cut down if on same machine.
|
''' this is until we have our own mail server, obviously.
|
||||||
probably can get rid of all but msg formatting/building, init line and sendmail line if local SMTP server '''
|
This can be greatly cut down if on same machine.
|
||||||
|
probably can get rid of all but msg formatting/building,
|
||||||
|
init line and sendmail line if local SMTP server '''
|
||||||
|
|
||||||
msg_body = 'Please click on: ' + activ_link + ' to activate your account.\n\n\nUnsubscribe:'
|
msg_body = 'Please click on: ' + activ_link + ' to activate your account.\n\n\nUnsubscribe:'
|
||||||
|
|
||||||
|
@ -611,7 +622,7 @@ def send_verification_email(to_address, activ_link):
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
||||||
|
|
||||||
#################################### STATIC PAGES ####################################
|
# #################################### STATIC PAGES ####################################
|
||||||
@app.route('/rules', methods=['GET'])
|
@app.route('/rules', methods=['GET'])
|
||||||
def site_rules():
|
def site_rules():
|
||||||
return flask.render_template('rules.html')
|
return flask.render_template('rules.html')
|
||||||
|
@ -622,9 +633,9 @@ def site_help():
|
||||||
return flask.render_template('help.html')
|
return flask.render_template('help.html')
|
||||||
|
|
||||||
|
|
||||||
#################################### API ROUTES ####################################
|
# #################################### API ROUTES ####################################
|
||||||
# DISABLED FOR NOW
|
# DISABLED FOR NOW
|
||||||
@app.route('/api/upload', methods = ['POST'])
|
@app.route('/api/upload', methods=['POST'])
|
||||||
def api_upload():
|
def api_upload():
|
||||||
api_response = api_handler.api_upload(flask.request)
|
api_response = api_handler.api_upload(flask.request)
|
||||||
return api_response
|
return api_response
|
||||||
|
|
|
@ -167,8 +167,8 @@ def search_elastic(term='', user=None, sort='id', order='desc',
|
||||||
s = s[0:per_page]
|
s = s[0:per_page]
|
||||||
else:
|
else:
|
||||||
max_page = min(page, int(math.ceil(max_search_results / float(per_page))))
|
max_page = min(page, int(math.ceil(max_search_results / float(per_page))))
|
||||||
from_idx = (max_page-1)*per_page
|
from_idx = (max_page - 1) * per_page
|
||||||
to_idx = min(max_search_results, max_page*per_page)
|
to_idx = min(max_search_results, max_page * per_page)
|
||||||
s = s[from_idx:to_idx]
|
s = s[from_idx:to_idx]
|
||||||
|
|
||||||
highlight = app.config.get('ENABLE_ELASTIC_SEARCH_HIGHLIGHT')
|
highlight = app.config.get('ENABLE_ELASTIC_SEARCH_HIGHLIGHT')
|
||||||
|
@ -188,7 +188,8 @@ def search_db(term='', user=None, sort='id', order='desc', category='0_0',
|
||||||
sort_keys = {
|
sort_keys = {
|
||||||
'id': models.Torrent.id,
|
'id': models.Torrent.id,
|
||||||
'size': models.Torrent.filesize,
|
'size': models.Torrent.filesize,
|
||||||
# 'name': models.Torrent.display_name, # Disable this because we disabled this in search_elastic, for the sake of consistency
|
# Disable this because we disabled this in search_elastic, for the sake of consistency:
|
||||||
|
# 'name': models.Torrent.display_name,
|
||||||
'seeders': models.Statistic.seed_count,
|
'seeders': models.Statistic.seed_count,
|
||||||
'leechers': models.Statistic.leech_count,
|
'leechers': models.Statistic.leech_count,
|
||||||
'downloads': models.Statistic.download_count
|
'downloads': models.Statistic.download_count
|
||||||
|
@ -267,26 +268,35 @@ def search_db(term='', user=None, sort='id', order='desc', category='0_0',
|
||||||
|
|
||||||
if not admin:
|
if not admin:
|
||||||
# Hide all DELETED torrents if regular user
|
# Hide all DELETED torrents if regular user
|
||||||
query = query.filter(models.Torrent.flags.op('&')(int(models.TorrentFlags.DELETED)).is_(False))
|
query = query.filter(models.Torrent.flags.op('&')(
|
||||||
# If logged in user is not the same as the user being viewed, show only torrents that aren't hidden or anonymous
|
int(models.TorrentFlags.DELETED)).is_(False))
|
||||||
# If logged in user is the same as the user being viewed, show all torrents including hidden and anonymous ones
|
# If logged in user is not the same as the user being viewed,
|
||||||
# On RSS pages in user view, show only torrents that aren't hidden or anonymous no matter what
|
# show only torrents that aren't hidden or anonymous
|
||||||
|
#
|
||||||
|
# If logged in user is the same as the user being viewed,
|
||||||
|
# show all torrents including hidden and anonymous ones
|
||||||
|
#
|
||||||
|
# On RSS pages in user view,
|
||||||
|
# show only torrents that aren't hidden or anonymous no matter what
|
||||||
if not same_user or rss:
|
if not same_user or rss:
|
||||||
query = query.filter(models.Torrent.flags.op('&')(int(models.TorrentFlags.HIDDEN |
|
query = query.filter(models.Torrent.flags.op('&')(
|
||||||
models.TorrentFlags.ANONYMOUS)).is_(False))
|
int(models.TorrentFlags.HIDDEN | models.TorrentFlags.ANONYMOUS)).is_(False))
|
||||||
# General view (homepage, general search view)
|
# General view (homepage, general search view)
|
||||||
else:
|
else:
|
||||||
if not admin:
|
if not admin:
|
||||||
# Hide all DELETED torrents if regular user
|
# Hide all DELETED torrents if regular user
|
||||||
query = query.filter(models.Torrent.flags.op('&')(int(models.TorrentFlags.DELETED)).is_(False))
|
query = query.filter(models.Torrent.flags.op('&')(
|
||||||
|
int(models.TorrentFlags.DELETED)).is_(False))
|
||||||
# If logged in, show all torrents that aren't hidden unless they belong to you
|
# If logged in, show all torrents that aren't hidden unless they belong to you
|
||||||
# On RSS pages, show all public torrents and nothing more.
|
# On RSS pages, show all public torrents and nothing more.
|
||||||
if logged_in_user and not rss:
|
if logged_in_user and not rss:
|
||||||
query = query.filter((models.Torrent.flags.op('&')(int(models.TorrentFlags.HIDDEN)).is_(False)) |
|
query = query.filter(
|
||||||
(models.Torrent.uploader_id == logged_in_user.id))
|
(models.Torrent.flags.op('&')(int(models.TorrentFlags.HIDDEN)).is_(False)) |
|
||||||
|
(models.Torrent.uploader_id == logged_in_user.id))
|
||||||
# Otherwise, show all torrents that aren't hidden
|
# Otherwise, show all torrents that aren't hidden
|
||||||
else:
|
else:
|
||||||
query = query.filter(models.Torrent.flags.op('&')(int(models.TorrentFlags.HIDDEN)).is_(False))
|
query = query.filter(models.Torrent.flags.op('&')(
|
||||||
|
int(models.TorrentFlags.HIDDEN)).is_(False))
|
||||||
|
|
||||||
if main_category:
|
if main_category:
|
||||||
query = query.filter(models.Torrent.main_category_id == main_cat_id)
|
query = query.filter(models.Torrent.main_category_id == main_cat_id)
|
||||||
|
@ -295,7 +305,8 @@ def search_db(term='', user=None, sort='id', order='desc', category='0_0',
|
||||||
(models.Torrent.sub_category_id == sub_cat_id))
|
(models.Torrent.sub_category_id == sub_cat_id))
|
||||||
|
|
||||||
if filter_tuple:
|
if filter_tuple:
|
||||||
query = query.filter(models.Torrent.flags.op('&')(int(filter_tuple[0])).is_(filter_tuple[1]))
|
query = query.filter(models.Torrent.flags.op('&')(
|
||||||
|
int(filter_tuple[0])).is_(filter_tuple[1]))
|
||||||
|
|
||||||
if term:
|
if term:
|
||||||
for item in shlex.split(term, posix=False):
|
for item in shlex.split(term, posix=False):
|
||||||
|
|
|
@ -54,6 +54,7 @@ def get_trackers(torrent):
|
||||||
|
|
||||||
return list(trackers)
|
return list(trackers)
|
||||||
|
|
||||||
|
|
||||||
def get_trackers_magnet():
|
def get_trackers_magnet():
|
||||||
trackers = OrderedSet()
|
trackers = OrderedSet()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue