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