diff --git a/xb_seed_status.py b/xb_seed_status.py index 1865263..0864c76 100644 --- a/xb_seed_status.py +++ b/xb_seed_status.py @@ -16,12 +16,10 @@ Requirements: Usage: python seed_tracker.py --id-folder ./torrents --bt-backup /path/to/BT_backup \\ - --nocodb-url https://noco.example.com --table-id tblXXX --api-token xc-xxx \\ - --id-field cXXX --seeding-field cYYY + --nocodb-url https://noco.example.com --table-id tblXXX --api-token xc-xxx To find NocoDB IDs: - Table ID: Click ... next to table name → Copy Table ID - - Field IDs: Click field header dropdown → Copy Field ID """ import argparse @@ -209,38 +207,40 @@ def get_bt_backup_data(bt_backup_path: Path) -> tuple[set[str], dict[tuple[str, class NocoDBClient: """Simple NocoDB API client.""" - def __init__(self, base_url: str, table_id: str, api_token: str, - id_field: str, seeding_field: str, debug: bool = False): + ID_FIELD = "Id" + SEEDING_FIELD = "Seeding Users" + + def __init__(self, base_url: str, table_id: str, api_token: str, + debug: bool = False): self.base_url = base_url.rstrip('/') self.table_id = table_id self.api_token = api_token - self.id_field = id_field - self.seeding_field = seeding_field self.debug = debug self.endpoint = f"{self.base_url}/api/v2/tables/{table_id}/records" - - def _request(self, method: str, data: dict | None = None, params: dict | None = None) -> dict: + + def _request(self, method: str, url: str | None = None, data: dict | None = None, params: dict | None = None) -> dict: """Make an API request.""" - url = self.endpoint + if url is None: + url = self.endpoint if params: query = "&".join(f"{k}={urllib.request.quote(str(v))}" for k, v in params.items()) url = f"{url}?{query}" - + headers = { "xc-token": self.api_token, "Content-Type": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "application/json", } - + body = json.dumps(data).encode('utf-8') if data else None req = urllib.request.Request(url, data=body, headers=headers, method=method) - + if self.debug: print(f" DEBUG: {method} {url}", file=sys.stderr) if body: print(f" DEBUG: Body: {body.decode()}", file=sys.stderr) - + try: with urllib.request.urlopen(req, timeout=30) as response: result = json.loads(response.read().decode('utf-8')) @@ -254,7 +254,7 @@ class NocoDBClient: def get_record(self, record_id: int | str) -> dict | None: """Get a single record by ID.""" try: - params = {"where": f"({self.id_field},eq,{record_id})", "limit": "1"} + params = {"where": f"({self.ID_FIELD},eq,{record_id})", "limit": "1"} result = self._request("GET", params=params) records = result.get("list", []) return records[0] if records else None @@ -266,7 +266,7 @@ class NocoDBClient: def update_record(self, row_id: int, value: str) -> bool: """Update the seeding_users field on a record.""" try: - data = {"Id": row_id, self.seeding_field: value} + data = {"Id": row_id, self.SEEDING_FIELD: value} self._request("PATCH", data=data) return True except Exception as e: @@ -302,13 +302,10 @@ def main(): Examples: # API mode - update NocoDB directly: %(prog)s --id-folder ./torrents --bt-backup ~/.local/share/qBittorrent/BT_backup \\ - --nocodb-url https://noco.example.com --table-id tblXXXXX --api-token xc-xxxx \\ - --id-field cXXXXX --seeding-field cYYYYY - + --nocodb-url https://noco.example.com --table-id tblXXXXX --api-token xc-xxxx + # CSV mode - just output a file: %(prog)s --id-folder ./torrents --bt-backup /path/to/BT_backup --csv-only - -To find field IDs in NocoDB: click field header dropdown → Copy Field ID (starts with "c") """ ) @@ -324,12 +321,7 @@ To find field IDs in NocoDB: click field header dropdown → Copy Field ID (star help='NocoDB table ID (starts with "tbl")') parser.add_argument('--api-token', type=str, default=None, help='NocoDB API token (xc-token)') - parser.add_argument('--id-field', type=str, default=None, - help='Field ID for the Id column (starts with "c")') - parser.add_argument('--seeding-field', type=str, default=None, - help='Field ID for the seeding_users column (starts with "c")') - - # CSV fallback +# CSV fallback parser.add_argument('--csv-only', action='store_true', help='Skip API, just output CSV') parser.add_argument('--output', type=Path, default=Path('seeding_update.csv'), @@ -357,13 +349,13 @@ To find field IDs in NocoDB: click field header dropdown → Copy Field ID (star print(f"Mode: {'NocoDB API' if use_api else 'CSV output'}") if use_api: - if not all([args.nocodb_url, args.table_id, args.api_token, args.id_field, args.seeding_field]): - print("Error: API mode requires --nocodb-url, --table-id, --api-token, --id-field, and --seeding-field", file=sys.stderr) + if not all([args.nocodb_url, args.table_id, args.api_token]): + print("Error: API mode requires --nocodb-url, --table-id, and --api-token", file=sys.stderr) print(" Use --csv-only to skip API and just output CSV", file=sys.stderr) sys.exit(1) noco = NocoDBClient( args.nocodb_url, args.table_id, args.api_token, - args.id_field, args.seeding_field, args.debug + args.debug ) # Test connection @@ -446,7 +438,7 @@ To find field IDs in NocoDB: click field header dropdown → Copy Field ID (star continue # Parse current seeders - current_seeders = parse_multiselect(record.get(noco.seeding_field)) + current_seeders = parse_multiselect(record.get(noco.SEEDING_FIELD)) if username in current_seeders: print(f" = {torrent_id}: already listed")