sane-bt-search: add a flag to sort by tracker reputation

This commit is contained in:
Colin 2023-08-20 09:14:19 +00:00
parent 6894d5828b
commit 1d8bee2856

View File

@ -67,6 +67,22 @@ def parse_time(t: str) -> datetime:
return try_parse_time(t).astimezone() or epoch
# preference, best to worst
TRACKER_RANKS = [
'bakabt',
'nyaa.si',
'yts',
'internet archive',
# haven't sorted these
'1337x',
'kickasstorrents.to',
]
def tracker_rank(tracker: str):
if tracker.lower() in TRACKER_RANKS:
return TRACKER_RANKS.index(tracker.lower())
logger.warn(f"unknown tracker: {tracker!r}")
return len(TRACKER_RANKS)
DROP_CATS = { "dvd", "hd", "misc", "other", "sd", "uhd" }
MANGA_CATS = { "books", "comics", "ebook" }
VIDEO_CATS = { "anime", "movies", "tv" }
@ -95,26 +111,29 @@ class Filter:
self.manga = manga
self.video = video
def filter(self, t: 'Torrent', default: bool = False) -> bool:
def filter(self, torrents: list['Torrent']) -> list['Torrent']:
return [t for t in torrents if self.is_match(t)]
def is_match(self, t: 'Torrent', default: bool = False) -> bool:
valid = True
valid = valid and (not self.h265 or self.filter_h265(t))
valid = valid and (not self.manga or self.filter_manga(t, default))
valid = valid and (not self.video or self.filter_video(t, default))
valid = valid and (not self.h265 or self.is_h265(t))
valid = valid and (not self.manga or self.is_manga(t, default))
valid = valid and (not self.video or self.is_video(t, default))
return valid
@staticmethod
def filter_h265(t: 'Torrent') -> bool:
def is_h265(t: 'Torrent') -> bool:
meta = t.title.lower()
return "h265" in meta \
or "x265" in meta \
or "HEVC" in meta
@staticmethod
def filter_manga(t: 'Torrent', default: bool = False) -> bool:
def is_manga(t: 'Torrent', default: bool = False) -> bool:
return is_cat(t.categories, MANGA_CATS, default)
@staticmethod
def filter_video(t: 'Torrent', default: bool = False) -> bool:
def is_video(t: 'Torrent', default: bool = False) -> bool:
return is_cat(t.categories, VIDEO_CATS, default)
@ -208,15 +227,12 @@ class Client:
return sorted(torrents, reverse=True)
def filter_results(results: list[Torrent], filter: Filter, top: int | None) -> list[Torrent]:
"""
take the complete query and filter further based on CLI options
"""
results = [t for t in results if filter.filter(t)]
if top is not None:
results = results[:top]
return results
def sort_results(torrents: list[Torrent], by: str) -> list[Torrent]:
if by == 'seeders':
return sorted(torrents, key=lambda t: (t.seeders, t), reverse=True)
elif by == 'source':
return sorted(torrents, key=lambda t: (-tracker_rank(t.tracker), t), reverse=True)
assert False, f"unknown sort method: {by}"
def format_results(all_results: list[Torrent], filtered_results: list[Torrent], json: bool):
if json:
@ -236,6 +252,7 @@ def main(args: list[str]):
parser = argparse.ArgumentParser(description='search torrent trackers')
parser.add_argument('--full', action='store_true', help='show all results')
parser.add_argument('--top', help='how many results to show (default: 5)')
parser.add_argument('--sort-by', default='seeders', help='how to rank matches (seeders, source)')
parser.add_argument('--json', action='store_true', help='output results in json')
parser.add_argument('--verbose', action='store_true')
parser.add_argument('--h265', action='store_true', help='show only H.265/HEVC results (might cause false negatives)')
@ -252,13 +269,13 @@ def main(args: list[str]):
all_results = client.query(args.query)
filter = Filter(h265=args.h265, manga=args.manga, video=args.video)
filtered_results = filter_results(
all_results,
filter,
None if args.full else int(args.top or "5"),
)
filtered_results = filter.filter(all_results)
ordered_results = sort_results(filtered_results, args.sort_by)
format_results(all_results, filtered_results, args.json)
if not args.full:
ordered_results = ordered_results[:int(args.top or "5")]
format_results(all_results, ordered_results, args.json)
if __name__ == "__main__":
main(sys.argv[1:])