sane-bt-search: refactor: split out Tracker details into own class
This commit is contained in:
@@ -20,7 +20,7 @@ returns select results and magnet links.
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from enum import Enum, IntEnum
|
||||
import argparse
|
||||
import logging
|
||||
import json
|
||||
@@ -46,6 +46,14 @@ class SortMethod(Enum):
|
||||
Seeders = "seeders"
|
||||
Tracker = "tracker"
|
||||
|
||||
class TrackerQuality(IntEnum):
|
||||
Authoritative = 0
|
||||
Trustworthy = 1
|
||||
Good = 2
|
||||
Mediocre = 3
|
||||
Bad = 4
|
||||
Unknown = 5
|
||||
|
||||
class BadCliArgs(Exception):
|
||||
def __init__(self, msg: str | None = None) -> None:
|
||||
helpstr = __doc__
|
||||
@@ -68,37 +76,59 @@ def try_parse_time(t: str) -> datetime:
|
||||
def parse_time(t: str) -> datetime:
|
||||
return try_parse_time(t).astimezone() or epoch
|
||||
|
||||
@dataclass(eq=True, order=True, unsafe_hash=True)
|
||||
class Tracker:
|
||||
name: str
|
||||
# preference: major, minor.
|
||||
# lower value = more preferable.
|
||||
# trackers with the same major are on a similar "tier"
|
||||
quality: TrackerQuality
|
||||
order: int
|
||||
|
||||
# preference, best to worst
|
||||
TRACKER_RANKS = [
|
||||
[ 'BitMagnet (Local DHT)', ],
|
||||
[
|
||||
'BakaBT',
|
||||
'SubsPlease',
|
||||
'Nyaa.si',
|
||||
'sukebei.nyaa.si',
|
||||
],
|
||||
[ 'YTS', ],
|
||||
[ 'MioBT', ],
|
||||
[ 'Internet Archive', ],
|
||||
[ 'The Pirate Bay', ],
|
||||
# haven't sorted these
|
||||
[
|
||||
'1337x',
|
||||
'Bengumi Moe',
|
||||
'kickasstorrents.to',
|
||||
'Tokyo Toshokan',
|
||||
'Torlock',
|
||||
]
|
||||
]
|
||||
# returns the tracker rank, as a tuple of (major, minor).
|
||||
# trackers with the same major rank are _roughly_ equal.
|
||||
def tracker_rank(tracker: str) -> tuple[int, int]:
|
||||
for major, trackers in enumerate(TRACKER_RANKS):
|
||||
if tracker in trackers:
|
||||
return major, trackers.index(tracker)
|
||||
logger.warning(f"unknown tracker: {tracker!r}")
|
||||
return len(TRACKER_RANKS), 0
|
||||
@staticmethod
|
||||
def by_name(name: str) -> 'Tracker':
|
||||
if name in KNOWN_TRACKERS:
|
||||
return KNOWN_TRACKERS[name]
|
||||
logger.warning(f"unknown tracker: {name!r}")
|
||||
return Tracker(name, TrackerQuality.Unknown, len(KNOWN_TRACKERS))
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def _KNOWN_TRACKERS() -> dict[str, Tracker]:
|
||||
trackers = {}
|
||||
|
||||
order = 0
|
||||
def add_tracker(*args):
|
||||
nonlocal order
|
||||
t = Tracker(*args, order)
|
||||
order += 1
|
||||
trackers[t.name] = t
|
||||
|
||||
# the order of this list (even withint sections) is significant:
|
||||
# most preferred -> least preferred
|
||||
add_tracker('BitMagnet (Local DHT)', TrackerQuality.Authoritative)
|
||||
|
||||
add_tracker('BakaBT', TrackerQuality.Trustworthy)
|
||||
add_tracker('SubsPlease', TrackerQuality.Trustworthy)
|
||||
add_tracker('Nyaa.si', TrackerQuality.Trustworthy)
|
||||
add_tracker('sukebei.nyaa.si', TrackerQuality.Trustworthy)
|
||||
|
||||
add_tracker('YTS', TrackerQuality.Good)
|
||||
add_tracker('Tokyo Toshokan', TrackerQuality.Good)
|
||||
|
||||
add_tracker('Internet Archive', TrackerQuality.Mediocre)
|
||||
add_tracker('The Pirate Bay', TrackerQuality.Mediocre)
|
||||
add_tracker('MioBT', TrackerQuality.Mediocre)
|
||||
add_tracker('Bengumi Moe', TrackerQuality.Mediocre)
|
||||
add_tracker('Torlock', TrackerQuality.Mediocre)
|
||||
|
||||
add_tracker('1337x', TrackerQuality.Bad)
|
||||
add_tracker('kickasstorrents.to', TrackerQuality.Bad)
|
||||
|
||||
return trackers
|
||||
|
||||
KNOWN_TRACKERS = _KNOWN_TRACKERS()
|
||||
|
||||
DROP_CATS = { "dvd", "hd", "misc", "other", "sd", "uhd" }
|
||||
BOOK_CATS = { "audio", "books", "ebook" }
|
||||
@@ -167,7 +197,7 @@ class Torrent:
|
||||
seeders: int
|
||||
pub_date: datetime
|
||||
size: int
|
||||
tracker: str
|
||||
tracker: Tracker
|
||||
title: str
|
||||
magnet: str | None
|
||||
http_dl_uri: str | None # probably a .torrent file but it COULD be a referral to a magnet:// URI
|
||||
@@ -226,7 +256,7 @@ class Torrent:
|
||||
|
||||
if seeders is not None and pub_date is not None and title is not None and (magnet is not None or http_dl_uri is not None):
|
||||
pub_date = parse_time(pub_date)
|
||||
return Torrent(seeders, pub_date, size, tracker, title, magnet, http_dl_uri, tracker_uri, categories=categories)
|
||||
return Torrent(seeders, pub_date, size, Tracker.by_name(tracker), title, magnet, http_dl_uri, tracker_uri, categories=categories)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
# N.B.: not all fields: needs to be kept in sync with consumers like mx-sanebot
|
||||
@@ -269,12 +299,11 @@ class Client:
|
||||
def sort_results(torrents: list[Torrent], by: SortMethod) -> list[Torrent]:
|
||||
def key(t: Torrent) -> tuple[int, int, Torrent]:
|
||||
rank_seeders = -t.seeders
|
||||
rank_tracker_major, rank_tracker_minor = tracker_rank(t.tracker)
|
||||
# TODO: `Balanced` should consider `size` and `pub_date`
|
||||
return {
|
||||
SortMethod.Balanced: (rank_tracker_major, rank_seeders, rank_tracker_minor, t),
|
||||
SortMethod.Seeders: (rank_seeders, rank_tracker_major, rank_tracker_minor, t),
|
||||
SortMethod.Tracker: (rank_tracker_major, rank_tracker_minor, rank_seeders, t),
|
||||
SortMethod.Balanced: (t.tracker.quality, rank_seeders, t.tracker.order, t),
|
||||
SortMethod.Seeders: (rank_seeders, t.tracker.quality, t.tracker.order, t),
|
||||
SortMethod.Tracker: (t.tracker.quality, t.tracker.order, rank_seeders, t),
|
||||
}[by]
|
||||
return sorted(torrents, key=key)
|
||||
|
||||
|
Reference in New Issue
Block a user