#!/usr/bin/python3 ''' Checks available MAME Torrents on PleasureDome and updates the local versions if more recent versions are detected Basically what it does is: * Get all torrents in a directory and filter MAME torrents * Get all torrents from PleasureDome RSS * Get all torrents currently active in Transmission * Intersect the three lists to get updatable torrents * And for each updatable torrent: - remove the old torrent from Transmission, - rename the local directory, - add the new torrent Work in progress… * TODO: implement some error handling Requirements: * Transmission for Bitorrent * A PleasureDome account * A proper config.json file (see config.template.json) * Python3 with the libraries below - feedparser - transmission-clutch - tabulate * Linux (untested on other OS, but it might work) Notes * This script logs in PleasureDome to get the proper cookies. It seems you can also set your cookies in Transmission using a cookies.txt file in the .config/transmission directory See: https://forum.transmissionbt.com/viewtopic.php?t=7468 /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ /!\ Provided with no warranty whatsoever. /!\ /!\ Make sure you understand what the script /!\ /!\ does and adapt it to your context /!\ /!\ Use with caution. /!\ /!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\/!\ ''' import argparse import feedparser import json import logging import os import re import requests import time import pathlib from clutch.core import Client from tabulate import tabulate from collections import defaultdict from pprint import pformat from urllib.parse import quote def open_config_file(): """Reads configuration from config.json file""" for loc in os.environ.get("PDMAMEUPDATE_CONF"), "/etc", os.path.expanduser("~"), os.curdir: if loc is not None: config_path = loc + '/config.json' if pathlib.Path(config_path).is_file(): config_file_loc = config_path break logging.info('Opening config file: '+config_file_loc) config_file = None; with open(config_file_loc, 'r') as config_file: config = json.load(config_file) return config def fetch_local_torrents(): """Fetches local torrents versions""" logging.info('Fetching current MAME versions') directories = os.listdir(config['mame-directory']) for directory in directories: match = re_mame_version.match(directory) if match: torrents[match.group(2)]['local-version'] = int(match.group(1)) torrents[match.group(2)]['local-name'] = directory logging.debug('Found the local torrent versions: %s', pformat(torrents)) def fetch_remote_torrents(): """Fetches PleasureDome torrents versions""" logging.info('Opening PleasureDome RSS feed') d = feedparser.parse('http://www.pleasuredome.org.uk/rss.xml') for post in d.entries: match = re_mame_version.match(post.title) if match: matched_version = int(match.group(1)) matched_torrent = torrents[match.group(2)] if matched_version > matched_torrent.get('remote-version', 0): matched_torrent['remote-version'] = matched_version matched_torrent['remote-link'] = post.link matched_torrent['remote-name'] = post.title else: logging.info("Skipping '{}' version '{}'".format( match.group(0), matched_version )) logging.debug('Found the remote torrent versions: %s', pformat(torrents)) def filter_updatable_torrents(): """Checks if newer versions are available and prompt for update""" for torrent, data in list(torrents.items()): keys_to_check = {'local-version', 'remote-version', 'transmission-id'} if ( keys_to_check.issubset(data.keys()) and data['local-version'] < data['remote-version'] ): check_and_rewrite_download_url(data) else: del torrents[torrent] logging.debug( 'The following torrents can be updated: %s', [t for t in torrents.keys()] ) def check_and_rewrite_download_url(torrent_data): url_match = re.compile( r"https?://www.pleasuredome.org.uk/details.php\?id=(.*)" ) match = url_match.match(torrent_data['remote-link']) if match: url = ("http://www.pleasuredome.org.uk/download.php" "?id={}&f={}.torrent&secure=no").format( match.group(1), quote('+'.join(torrent_data['remote-name'].split(' ')), safe='+') ) logging.info('Changed url {} to {}'.format( torrent_data['remote-link'], url )) torrent_data['remote-link'] = url def prompt_for_update(): """Ask for user confirmation before updating""" output_header = ["Torrent", "from", "to"] output = [] if len(torrents) > 0: for torrent, data in torrents.items(): output.append([ torrent, data['local-version'], data['remote-version'] ]) print(tabulate(output, headers=output_header, tablefmt="simple")) print('Should I update the torrents listed above? (y/N)') answer = input() if answer.lower() != 'y': logging.info('Quitting: user cancelled update') print('Quitting…') exit(0) else: logging.info('Quitting: no update candidate') print('No update found…') exit(0) logging.info('User chose to update torrents') def get_cookies_from_pleasuredome(): """Connects to PleasureDome to retrieve Cookies""" logging.info('Logging in PleasureDome') data = { 'uid': config['pleasuredome-user'], 'pwd': config['pleasuredome-password'] } r = requests.post('http://www.pleasuredome.org.uk/login2.php', data=data) if r.status_code == 200: logging.info('Connected to PleasureDome') logging.info('Logging out') requests.get('http://www.pleasuredome.org.uk/logout.php') else: logging.error( 'Connection to PleasureDome failed with status %s', r.status_code ) exit(1) return {k: r.cookies[k] for k in ('uid', 'pass')} def connect_to_transmission(): """Connects to Transmission and return a Client object""" logging.info('Connecting to Transmission Remote Control') return Client( username=config['transmission-user'], password=config['transmission-password'], port=config['transmission-port'] ) def fetch_transmission_torrents(): """Gets the torrents id from Transmission""" logging.info('Listing Transmission torrents') for torrent in client.list().values(): match = re_mame_version.match(torrent['name']) if match: torrents[match.group(2)]['transmission-id'] = torrent['id'] logging.debug('Found the Transmission torrent ids: %s', pformat(torrents)) def update_torrents(): """ Updates torrents: * remove it from Transmission, * rename the local directory, * and add the new torrent """ logging.info('Updating torrents') for torrent in torrents.values(): old_name = os.path.join( config['mame-directory'], torrent['local-name'] ) new_name = os.path.join( config['mame-directory'], torrent['remote-name'] ) new_torrent = os.path.join( config['torrent-directory'], torrent['remote-name']+'.torrent' ) client.torrent.remove(torrent['transmission-id']) os.rename(old_name, new_name) client.torrent.add( filename=torrent['remote-link'], download_dir=config['mame-directory'], cookies=cookies, paused=False ) if args.keep: t = requests.get(torrent['remote-link'], verify=False) open(new_torrent, 'wb').write(t.content) if __name__ == '__main__': logging.basicConfig( level=logging.WARNING, format=' %(asctime)s - %(levelname)s - %(message)s' ) parser = argparse.ArgumentParser( description='Update PleasureDome MAME Torrents' ) parser.add_argument( '-l', '--log', action='store_true', help='Display more log messages' ) parser.add_argument( '-d', '--debug', action='store_true', help='Display debugging messages' ) parser.add_argument( '-k', '--keep', action='store_true', help='Keep torrent files localy' ) parser.add_argument( '-c', '--countdown', action='store_true', help='Start with a 5 second countdown' ) args = parser.parse_args() if args.log: logging.getLogger().setLevel(logging.INFO) if args.debug: logging.getLogger().setLevel(logging.DEBUG) if args.countdown: print('PDMameUpdate is about to start') # Useful if you run this script when your machine boots for i in range(5, 0, -1): print('{}\r'.format(i), end=''), time.sleep(1) re_mame_version = re.compile(r'MAME 0.(\d+) (.*)') config = open_config_file() torrents = defaultdict(dict) client = connect_to_transmission() cookies = get_cookies_from_pleasuredome() fetch_local_torrents() fetch_remote_torrents() fetch_transmission_torrents() filter_updatable_torrents() prompt_for_update() update_torrents()