def _add_new_db(self, season=0, episode=0): if self.db.show_exists(self.id): sql = '''UPDATE shows SET status="active", episode=:episode, season=:season WHERE thetvdb_series_id=:thetvdb_id;''' values = { 'thetvdb_id': self.id, 'episode': episode, 'season': season } msg = '%s is already in the db. Its status is now set to "active"' % self.seriesname else: sql = ''' INSERT INTO shows ( network_status, status, thetvdb_series_id, name, season, episode) VALUES (:network_status, :status, :thetvdb_id, :name, :season, :episode)''' values = {'network_status': self.status, 'status': 'active', 'thetvdb_id': self.id, 'name': self.seriesname, 'season': season, 'episode': episode} episode += 1 episode = str(episode) if season == 0: season += 1 season = str(season) msg = '%s %s added.' % (self.seriesname, sxxexx(season, episode)) DB.run_sql(sql, values) return msg
def tvol(no_cache, config_name): """Download and manage tv shows. Use `tvol COMMAND -h` to get help for each command. \b \/ TVOverlord source code is available at: [. ] https://github.com/8cylinder/tv-overlord ^ /^\\ Any feature requests or bug reports should go there. //^\\\\ -^-._.--.-^^-.____._^-.^._ """ Config.get_config_data(config_name) DB.configure() if Config.version_notification: if v.new_version(): msg = tvu.style(v.message, fg='green') click.secho(msg, err=True) te.ask() if no_cache: Config.use_cache = False else: Config.use_cache = True
def _update_db(self, season, episode): sql = """UPDATE shows SET season=:season, episode=:episode WHERE thetvdb_series_id=:tvdb_id""" values = {'season': season, 'episode': episode, 'tvdb_id': self.db_thetvdb_series_id} DB.run_sql(sql, values)
def save(self, show_title, season, episode, data, chosen_url, nondbshow=False): magnet_hash = self._extract_hash(chosen_url) data = json.dumps(data) now = datetime.datetime.today() date = now.isoformat() # oneoff is a show that was downloaded via 'nondbshow' oneoff = 1 if nondbshow else 0 sql = ''' INSERT INTO tracking( download_date, show_title, season, episode, chosen, chosen_hash, one_off) VALUES( :date, :show_title, :season, :episode, :chosen, :hash, :one_off);''' values = { 'date': date, 'show_title': show_title, 'season': season, 'episode': episode, 'chosen': chosen_url, 'hash': magnet_hash, 'one_off': oneoff, } DB.run_sql(sql, values)
def new_version(self): """ compare current version with version in db """ self.remote_version = DB.get_config('version_remote') self.message = DB.get_config('version_msg') if not self.remote_version: return False elif LV(self.local_version) < LV(self.remote_version): return True else: return False
def qbittorrent(info_hash, torrent_name, torrent_dir, debug): """Manage torrents downloaded by qBittorrent. \b In tools > options > downloads > Run external program Add: /absolute/path/to/qbittorrent_done %I %N %D """ Config.get_config_data() DB.configure() telemetry('qbittorrent_done') if debug: click.echo('info_hash: %s' % info_hash) click.echo('torrent_dir: %s' % torrent_dir) click.echo('torrent_name: %s' % torrent_name) DownloadManager(info_hash, torrent_dir, torrent_name, debug=debug)
def deluge(torrent_hash, torrent_name, torrent_dir, debug): """Manage torrents downloaded by deluge. Deluge will call this script when the torrent has been downloaded. It will pass TORRENT_HASH, TORRENT_NAME and TORRENT_DIR as arguements. \b The execute plugin is needed for this to work. http://dev.deluge-torrent.org/wiki/Plugins/Execute """ Config.get_config_data() DB.configure() telemetry('deluge_done') if debug: click.echo('torrent_hash: %s' % torrent_hash) click.echo('torrent_dir: %s' % torrent_dir) click.echo('torrent_name: %s' % torrent_name) DownloadManager(torrent_hash, torrent_dir, torrent_name, debug=debug)
def ask(self): if not DB.get_config('telemetry_asked'): question = format_paragraphs(""" May TV Overlord report anonymous usage statistics? This is the single most useful thing a user can do. This data will help decide the features that get the most attention and the future direction of TV Overlord. More information at: https://github.com/8cylinder/tv-overlord/wiki/telemetry-data """) question = click.style(question, fg='green') click.echo() telemetry_ok = click.confirm(question, default=True) click.echo() DB.set_config('telemetry_ok', telemetry_ok) DB.set_config('telemetry_asked', True) if not DB.get_config('uuid4'): DB.set_config('uuid4', str(uuid.uuid4())) DB.set_config('uuid1', str(uuid.uuid1()))
def transmission(debug): """This script passes the enviroment variables from transmission to tvoverlord. Transmission exports these enviroment variables: \b X TR_TORRENT_DIR X TR_TORRENT_HASH X TR_TORRENT_NAME TR_APP_VERSION TR_TIME_LOCALTIME TR_TORRENT_ID This script uses the ones marked with an X. This information is used to manage the torrent after its been downloaded. """ Config.get_config_data() DB.configure() telemetry('transmission_done') try: torrent_dir = os.environ['TR_TORRENT_DIR'] torrent_hash = os.environ['TR_TORRENT_HASH'] torrent_name = os.environ['TR_TORRENT_NAME'] except KeyError: sys.exit('Enviroment variables not set') if debug: click.echo('torrent_hash: %s' % torrent_hash) click.echo('torrent_dir: %s' % torrent_dir) click.echo('torrent_name: %s' % torrent_name) DownloadManager(torrent_hash, torrent_dir, torrent_name, debug=debug)
def _query_db(self, sqlfilter, statusfilter): # print('>>>',sqlfilter, '|', statusfilter) # if sqlfilter: # sqlfilter = 'AND %s' % sqlfilter # print('sqlfilter: %s, statusfilter: %s' % (sqlfilter, statusfilter)) if statusfilter and sqlfilter: where = '%s AND %s' % (statusfilter, sqlfilter) elif sqlfilter: where = sqlfilter elif statusfilter: where = statusfilter sql = """ SELECT name, season, episode, search_by_date, date_format, thetvdb_series_id, ragetv_series_id, search_engine_name, status FROM shows WHERE %s ORDER BY %s;""" % ( where, self.sort_field ) ddata = DB.run_sql(sql, named_fields=True) data = [] for i in ddata: data.append(i) self.show_count = len(data) return data
def delete(self): sql = '''DELETE from shows WHERE thetvdb_series_id=:tvdb_id''' values = {'tvdb_id': self.db_thetvdb_series_id} DB.run_sql(sql, values)
def set_inactive(self): sql = '''UPDATE shows SET status="inactive" WHERE thetvdb_series_id=:tvdb_id''' values = {'tvdb_id': self.db_thetvdb_series_id} DB.run_sql(sql, values)
def set_next_episode(self, next_date): sql = 'UPDATE shows SET next_episode=:next_date WHERE name=:show_name' values = {'next_date': next_date.isoformat(), 'show_name': self.db_name} DB.run_sql(sql, values)
def __init__(self, torrent_hash, path, filename, debug=False): if debug: console = Config.logging.StreamHandler() formater = Config.logging.Formatter(">>> %(message)s") console.setFormatter(formater) Config.logging.getLogger("").addHandler(console) Config.logging.info("-" * 50) Config.logging.info("hash: %s", torrent_hash) Config.logging.info("path: %s", path) Config.logging.info("filename: %s", filename) filename = os.path.join(path, filename) DB.save_info(torrent_hash, filename) debug_command = """export TR_TORRENT_NAME='%s'; export TR_TORRENT_DIR='%s'; export TR_TORRENT_HASH='%s'; transmission_done""" Config.logging.info(debug_command, filename, path, torrent_hash) if DB.is_oneoff(torrent_hash): Config.logging.info("Download is a one off, doing nothing.") return if not os.path.exists(Config.tv_dir): msg = 'Destination: "{}" does not exist'.format(Config.tv_dir) Config.logging.error(msg) raise OSError(msg) # sys.exit(msg) if not os.path.exists(filename): msg = "Source does not exist".format(filename) Config.logging.error(msg) raise OSError(msg) # sys.exit(msg) source = filename if Config.single_file: # extract largest file from dir source = self.get_show_file(filename) if Config.template: template = Config.template else: template = "{show}/{original}" dest_filename = self.pretty_names(source, torrent_hash, template) Config.logging.info("Destination filename: %s" % dest_filename) dest = os.path.join(Config.tv_dir, dest_filename) dest_path = os.path.dirname(dest) if not os.path.exists(dest_path): os.makedirs(dest_path, exist_ok=True) Config.logging.info("creating dir: %s" % dest) DB.save_dest(torrent_hash, dest) Config.logging.info("copying %s to %s" % (source, dest)) if self.copy(source, dest): Tell("%s done" % os.path.basename(dest)) DB.set_torrent_complete(torrent_hash) else: Config.logging.info("Destination full") Tell("Destination full") sys.exit("Destination full")
def pretty_names(self, filename, torrent_hash, template=None): if not template: template = "{show}/{original}" fields = {} (fields["show"], fields["searchname"], fields["season"], fields["episode"]) = DB.get_show_info(torrent_hash) fields["original"] = os.path.basename(filename) fields["s00e00"] = sxxexx(fields["season"], fields["episode"]) fields["0x00"] = sxee(fields["season"], fields["episode"]) fields["season"] = fields["season"].rjust(2, "0") fields["episode"] = fields["episode"].rjust(2, "0") if fields["searchname"] is None: fields["searchname"] = "" # search original filename for info fields["resolution"] = "" for res in Config.categories.resolution: if res.lower() in filename.lower(): fields["resolution"] = res fields["source"] = "" for srs in Config.categories.sources: if srs.lower() in filename.lower(): fields["source"] = srs fields["codec"] = "" for cod in Config.categories.codecs: if cod.lower() in filename.lower(): fields["codec"] = cod fields["audio"] = "" for aud in Config.categories.audio: if aud.lower() in filename.lower(): fields["audio"] = aud # short cut tags all = [ fields["show"], fields["s00e00"], fields["resolution"], fields["source"], fields["codec"], fields["audio"], ] all = [i for i in all if i] # remove empty fields["all"] = " ".join(all) ext = "" if os.path.isfile(filename): ext = os.path.splitext(filename)[-1] broke = re.split("({.*?})", template) if not broke[-1]: broke = broke[:-1] new = [] is_section = False for section in broke: if section.startswith("{"): chunks = section.strip("{}").split("|") field = chunks[0].lower() filters = [i.lower() for i in chunks[1:]] try: if not fields[field]: continue except KeyError: continue complete = self.format(fields[field], filters) new.append(complete) is_section = True else: new.append(section) is_section = False # remove adjacent duplicates new = [i[0] for i in itertools.groupby(new)] if not is_section: new = new[:-1] new.append(ext) full = "".join(new) full = os.path.normpath(full) return full
def config(edit, test_se, show, create_config_name): """tvol's config information. Show information of where various files are, (config.ini, database) and a list of the search engines and the url's they use. """ send(te, v) if create_config_name: Config.create_config(create_config_name, create=True) return if edit: click.edit(filename=Config.user_config) return if test_se: search = Search() search.test_each(test_se, show) return import shutil title = 'green' bold = True ul = True # file locations click.echo() click.secho('File locations:', fg=title, bold=bold, underline=ul) click.echo() click.echo('config file: %s' % Config.user_config) click.echo('Database file: %s' % Config.db_file) click.echo('NZB staging dir: %s' % Config.staging) click.echo('TV dir: %s' % Config.tv_dir) click.echo('Alt client: %s' % Config.client) click.echo('Magnet dir: %s' % Config.magnet_dir) click.echo('Template: %s' % Config.template) click.echo() for script in ['tvol', 'transmission_done', 'deluge_done']: loc = shutil.which(script) script = script + ':' click.echo('%s %s' % (script.ljust(18), loc)) # config sets files = [f for f in os.listdir(Config.user_dir) if re.match('^.*\..*\.(sqlite3|ini)', f)] files.sort(key=lambda f: f.split('.')[1]) if files: click.echo() click.secho('Config sets', fg=title, bold=bold, underline=ul) click.echo() count = 1 for gr, items in groupby(files, key=lambda f: f.split('.')[1]): config_set = ', '.join(list(items)) click.echo('%s. %s' % (count, config_set)) count += 1 # search engines click.echo() click.secho('Search engines:', fg=title, bold=bold, underline=ul) search = Search() engines_types = [search.torrent_engines, search.newsgroup_engines] for engines in engines_types: for engine in engines: click.echo() click.secho(engine.Provider.name, bold=True, nl=False) click.echo(' (%s)' % engine.Provider.shortname) for url in engine.Provider.provider_urls: click.echo(' %s' % url) # blacklisted search engines if Config.blacklist: click.echo() click.secho('Search engine blacklist:', fg=title, bold=bold, underline=ul) click.echo() for bl in Config.blacklist: click.echo(bl) # ip addresses click.echo() click.secho('Ip address information:', fg=title, bold=bold, underline=ul) click.echo() l = Location() click.echo('Your public ip address:') click.secho(' %s' % l.ip, bold=True) if Config.warnvpn: click.echo() click.echo('Your whitelisted ip addresses:') whitelist = DB.get_config('ip_whitelist') short = '.'.join(l.ip.split('.')[:Config.parts_to_match]) for ip in whitelist: color = None if ip.startswith(short): color = 'green' click.secho(' %s' % ip, fg=color)
def display(self): sql = ''' SELECT * FROM tracking; ''' rows = DB.run_sql(sql, named_fields=True) return rows