# -*- coding: utf-8 -*- """ Seren Add-on This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ import ast import hashlib import re import time import tools try: from sqlite3 import dbapi2 as db, OperationalError, IntegrityError except ImportError: from pysqlite2 import dbapi2 as db, OperationalError, IntegrityError cache_table = 'cache' def get(function, duration, *args, **kwargs): # type: (function, int, object) -> object or None """ Gets cached value for provided function with optional arguments, or executes and stores the result :param function: Function to be executed :param duration: Duration of validity of cache in hours :param args: Optional arguments for the provided function """ try: sources = False reload = False if 'seren_reload' in kwargs: reload = kwargs['seren_reload'] kwargs.pop('seren_reload') if 'seren_sources' in kwargs: sources = True kwargs.pop('seren_sources') key = _hash_function(function, args, kwargs) cache_result = cache_get(key) if not reload: if cache_result: if _is_cache_valid(cache_result['date'], duration): try: return_data = ast.literal_eval(cache_result['value']) return return_data except: return ast.literal_eval(cache_result['value']) fresh_result = repr(function(*args, **kwargs)) if fresh_result is None or fresh_result == 'None': # If the cache is old, but we didn't get fresh result, return the old cache if cache_result: return cache_result return None data = ast.literal_eval(fresh_result) # Because I'm lazy, I've added this crap code so sources won't cache if there are no results if not sources: cache_insert(key, fresh_result) elif len(data[1]) > 0: cache_insert(key, fresh_result) else: return None return data except Exception: import traceback traceback.print_exc() return None def timeout(function, *args): try: key = _hash_function(function, args) result = cache_get(key) return int(result['date']) except Exception: return None def cache_get(key): try: tools.cacheFile_lock.acquire() cursor = _get_connection_cursor(tools.cacheFile) cursor.execute("SELECT * FROM %s WHERE key = ?" % cache_table, [key]) results = cursor.fetchone() cursor.close() return results except OperationalError: return None finally: tools.try_release_lock(tools.cacheFile_lock) def cache_insert(key, value): tools.cacheFile_lock.acquire() try: cursor = _get_connection_cursor(tools.cacheFile) now = int(time.time()) cursor.execute( "CREATE TABLE IF NOT EXISTS %s (key TEXT, value TEXT, date INTEGER, UNIQUE(key))" % cache_table ) cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_%s ON %s (key)" % (cache_table, cache_table)) cursor.execute("REPLACE INTO %s (key, value, date) VALUES (?, ?, ?)" % cache_table, (key, value, now)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() pass finally: tools.try_release_lock(tools.cacheFile_lock) def cache_clear(): try: tools.cacheFile_lock.acquire() cursor = _get_connection_cursor(tools.cacheFile) for t in [cache_table, 'rel_list', 'rel_lib']: try: cursor.execute("DROP TABLE IF EXISTS %s" % t) cursor.execute("VACUUM") cursor.connection.commit() except: pass tools.showDialog.notification('{}: {}'.format(tools.addonName, tools.lang(40306)), tools.lang(32078), time=5000) except: pass finally: tools.try_release_lock(tools.cacheFile_lock) def cache_clear_all(): cache_clear() def _get_connection_cursor(filepath): conn = _get_connection(filepath) return conn.cursor() def _get_connection(filepath): tools.makeFile(tools.dataPath) conn = db.connect(filepath) conn.row_factory = _dict_factory return conn def getSearchHistory(media_type): try: tools.searchHistoryDB_lock.acquire() cursor = _get_connection_cursor(tools.searchHistoryDB) cursor.execute('CREATE TABLE IF NOT EXISTS show (value TEXT)') cursor.execute('CREATE TABLE IF NOT EXISTS movie (value TEXT)') cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_history ON movie (value)") cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_history ON show (value)") cursor.execute("SELECT * FROM %s" % media_type) history = cursor.fetchall() cursor.close() history.reverse() history = history[:50] filter = [] for i in history: if i['value'] not in filter: filter.append(i['value']) return filter except: try: cursor.close() except: pass import traceback traceback.print_exc() return [] finally: tools.try_release_lock(tools.searchHistoryDB_lock) def addSearchHistory(search_string, media_type): try: tools.searchHistoryDB_lock.acquire() cursor = _get_connection_cursor(tools.searchHistoryDB) cursor.execute('CREATE TABLE IF NOT EXISTS show (value TEXT)') cursor.execute('CREATE TABLE IF NOT EXISTS movie (value TEXT)') cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_history ON movie (value)") cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_history ON show (value)") cursor.execute( "REPLACE INTO %s Values (?)" % media_type, (search_string,) ) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() return [] finally: tools.try_release_lock(tools.searchHistoryDB_lock) def clearSearchHistory(): try: tools.searchHistoryDB_lock.acquire() confirmation = tools.showDialog.yesno(tools.addonName, tools.lang(40169)) if not confirmation: return cursor = _get_connection_cursor(tools.searchHistoryDB) cursor.execute("DROP TABLE IF EXISTS movie") cursor.execute("DROP TABLE IF EXISTS show") try: cursor.execute("VACCUM") except: pass cursor.connection.commit() cursor.close() tools.showDialog.notification(tools.addonName, tools.lang(40259), time=5000) except: try: cursor.close() except: pass import traceback traceback.print_exc() return [] finally: tools.try_release_lock(tools.searchHistoryDB_lock) def getTorrents(item_meta): if tools.getSetting('general.torrentCache') == 'false': return [] tools.torrentScrapeCacheFile_lock.acquire() try: cursor = _get_connection_cursor(tools.torrentScrapeCacheFile) _try_create_torrent_cache(cursor) if 'showInfo' in item_meta: season = item_meta['info']['season'] episode = item_meta['info']['episode'] trakt_id = item_meta['showInfo']['ids']['trakt'] cursor.execute("SELECT * FROM %s WHERE trakt_id=? AND package=?" % cache_table, (trakt_id, 'show')) torrent_list = cursor.fetchall() cursor.execute("SELECT * FROM %s WHERE trakt_id=? AND package=? AND season=?" % cache_table, (trakt_id, 'season', season)) torrent_list += cursor.fetchall() cursor.execute("SELECT * FROM %s WHERE trakt_id=? AND package=? AND season=? AND episode=?" % cache_table, (trakt_id, 'single', season, episode)) torrent_list += cursor.fetchall() else: trakt_id = item_meta['ids']['trakt'] cursor.execute("SELECT * FROM %s WHERE trakt_id=?" % cache_table, (trakt_id,)) torrent_list = cursor.fetchall() cursor.close() torrent_list = [ast.literal_eval(torrent['meta']) for torrent in torrent_list] return torrent_list except: try: cursor.close() except: pass import traceback traceback.print_exc() return [] finally: tools.try_release_lock(tools.torrentScrapeCacheFile_lock) def _try_create_torrent_cache(cursor): cursor.execute( "CREATE TABLE IF NOT EXISTS %s (" "trakt_id TEXT, " "meta TEXT, " "hash TEXT, " "season TEXT, " "episode TEXT, " "package, " "UNIQUE(hash))" % cache_table ) cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_%s ON %s (hash)" % (cache_table, cache_table)) def addTorrent(item_meta, torrent_objects): try: if tools.getSetting('general.torrentCache') == 'false': return if 'showInfo' in item_meta: season = item_meta['info']['season'] episode = item_meta['info']['episode'] trakt_id = item_meta['showInfo']['ids']['trakt'] else: season = 0 episode = 0 trakt_id = item_meta['ids']['trakt'] tools.torrentScrapeCacheFile_lock.acquire() cursor = _get_connection_cursor(tools.torrentScrapeCacheFile) try: # Confirm we are on the newer version of the torrent cache database columns = [i['name'] for i in cursor.execute("PRAGMA table_info(cache);").fetchall()] if 'trakt_id' not in columns: raise Exception except: cursor.execute("DROP TABLE IF EXISTS cache") _try_create_torrent_cache(cursor) for torrent_object in torrent_objects: try: hash = torrent_object['hash'] pack = torrent_object['package'] cursor.execute("REPLACE INTO %s (trakt_id, meta, hash, season, episode, package) " "VALUES (?, ?, ?, ?, ?, ?)" % cache_table, (trakt_id, str(torrent_object), hash, season, episode, pack)) except: import traceback traceback.print_exc() cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() return [] finally: tools.try_release_lock(tools.torrentScrapeCacheFile_lock) def torrent_cache_clear(): confirmation = tools.showDialog.yesno(tools.addonName, tools.lang(32043)) if not confirmation: return try: tools.torrentScrapeCacheFile_lock.acquire() cursor = _get_connection_cursor(tools.torrentScrapeCacheFile) for t in [cache_table, 'rel_list', 'rel_lib']: try: cursor.execute("DROP TABLE IF EXISTS %s" % t) cursor.execute("VACUUM") cursor.connection.commit() except: pass except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.torrentScrapeCacheFile_lock) tools.showDialog.notification('{}: {}'.format(tools.addonName, tools.lang(40306)), tools.lang(32079), time=5000) def get_assist_torrents(): try: tools.activeTorrentsDBFile_lock.acquire() cursor = _get_connection_cursor(tools.activeTorrentsDBFile) cursor.execute("SELECT * FROM torrents") results = cursor.fetchall() cursor.close() return results except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.activeTorrentsDBFile_lock) def clear_non_active_assist(): try: tools.activeTorrentsDBFile_lock.acquire() cursor = _get_connection_cursor(tools.activeTorrentsDBFile) cursor.execute( "DELETE FROM torrents WHERE status = 'failed'" ) cursor.execute( "DELETE FROM torrents WHERE status = 'finished'" ) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.activeTorrentsDBFile_lock) def _try_create_torrent_table(cursor): cursor.execute( "CREATE TABLE IF NOT EXISTS torrents " "(debrid_id TEXT PRIMARY KEY, provider TEXT, status TEXT, release_title TEXT, progress TEXT)" ) cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_torrents ON torrents (debrid_id)") def add_assist_torrent(debrid_id, provider, status, release_title, progress): try: tools.activeTorrentsDBFile_lock.acquire() cursor = _get_connection_cursor(tools.activeTorrentsDBFile) _try_create_torrent_table(cursor) cursor.execute("REPLACE INTO torrents (debrid_id, provider, status, release_title, progress) " "VALUES (?, ?, ?, ?, ?)", (debrid_id, provider, status, release_title, progress)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.activeTorrentsDBFile_lock) def clear_assist_torrents(): try: tools.activeTorrentsDBFile_lock.acquire() cursor = _get_connection_cursor(tools.activeTorrentsDBFile) for t in [cache_table, 'rel_list', 'rel_lib']: try: cursor.execute("DROP TABLE IF EXISTS torrents") cursor.execute("VACUUM") cursor.connection.commit() cursor.close() except: pass except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.activeTorrentsDBFile_lock) tools.showDialog.notification('{}: {}'.format(tools.addonName, tools.lang(40306)), tools.lang(32080), time=5000) def get_single_provider(provider_name, package, country): try: tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) _try_create_provider_table(cursor) cursor.execute("SELECT * FROM providers WHERE provider_name=? AND package=? AND country=?", (provider_name, package, country)) sources = cursor.fetchone() cursor.close() return sources except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def get_providers(): try: tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) _try_create_provider_table(cursor) cursor.execute("SELECT * FROM providers") sources = cursor.fetchall() cursor.close() return sources except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def _try_create_package_table(cursor): cursor.execute( "CREATE TABLE IF NOT EXISTS packages (hash TEXT," " pack_name TEXT, author TEXT, remote_meta TEXT, version TEXT, UNIQUE(hash))" ) cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_packages ON packages (hash)") def get_provider_packages(): try: tools.log('Getting provider packages') tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) _try_create_package_table(cursor) cursor.execute("SELECT * FROM packages") packages = cursor.fetchall() cursor.close() return packages except: try: cursor.close() except: pass import traceback traceback.print_exc() return None finally: tools.try_release_lock(tools.providersDB_lock) def add_provider_package(pack_name, author, remote_meta, version): try: tools.providersDB_lock.acquire() hash = _hash_function('%s%s' % (pack_name, author)) cursor = _get_connection_cursor(tools.providersDB) _try_create_package_table(cursor) cursor.execute("REPLACE INTO packages (hash, pack_name, author, remote_meta, version) " "VALUES (?, ?, ?, ?, ?)", (hash, pack_name, author, remote_meta, version)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def remove_provider_package(pack_name): try: tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) cursor.execute("DELETE FROM packages WHERE pack_name=?", (pack_name,)) cursor.execute("DELETE FROM providers WHERE package=?", (pack_name,)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def _try_create_provider_table(cursor): cursor.execute( "CREATE TABLE IF NOT EXISTS providers (hash TEXT," " provider_name TEXT, status TEXT, package TEXT, country TEXT, provider_type TEXT, UNIQUE(hash))" ) cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_providers ON providers (hash)") def add_provider(provider_name, package, status, language, provider_type): try: tools.providersDB_lock.acquire() hash = _hash_function('%s%s' % (provider_name, package)) cursor = _get_connection_cursor(tools.providersDB) _try_create_provider_table(cursor) cursor.execute("REPLACE INTO providers (hash, provider_name, status, package, country, provider_type) " "VALUES (?, ?, ?, ?, ?, ?)", (hash, provider_name, status, package, language, provider_type)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def adjust_provider_status(provider_name, package_name, state): try: tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) _try_create_provider_table(cursor) cursor.execute( "UPDATE providers SET status=? WHERE provider_name=? AND package=?", (state, provider_name, package_name) ) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def remove_individual_provider(provider_name, package_name): try: tools.providersDB_lock.acquire() hash = _hash_function('%s%s' % (provider_name, package_name)) cursor = _get_connection_cursor(tools.providersDB) _try_create_provider_table(cursor) cursor.execute("DELETE FROM providers WHERE hash=?", (hash,)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def remove_package_providers(package_name): try: tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) cursor.execute("DELETE FROM providers WHERE package=?", (package_name,)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def clear_providers(): try: tools.providersDB_lock.acquire() cursor = _get_connection_cursor(tools.providersDB) for t in [cache_table, 'rel_list', 'rel_lib']: try: cursor.execute("DROP TABLE IF EXISTS providers") cursor.execute("VACCUM") except: pass try: cursor.execute("DROP TABLE IF EXISTS packages") cursor.execute("VACUUM") except: pass cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.providersDB_lock) def get_premiumize_transfers(): try: tools.premiumizeDB_lock.acquire() cursor = _get_connection_cursor(tools.premiumizeDB) cursor.execute( "CREATE TABLE IF NOT EXISTS transfers (transfer_id TEXT)" ) cursor.execute("SELECT * FROM transfers") transfers = cursor.fetchall() cursor.close() return transfers except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.premiumizeDB_lock) def add_premiumize_transfer(transfer_id): try: tools.premiumizeDB_lock.acquire() cursor = _get_connection_cursor(tools.premiumizeDB) cursor.execute("CREATE TABLE IF NOT EXISTS transfers (transfer_id TEXT)") cursor.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_transfers ON transfers (transfer_id)") cursor.execute("REPLACE INTO transfers (transfer_id) VALUES (?)", transfer_id) cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.premiumizeDB_lock) def remove_premiumize_transfer(transfer_id): try: tools.premiumizeDB_lock.acquire() cursor = _get_connection_cursor(tools.premiumizeDB) cursor.execute("DELETE FROM transfers WHERE transfer_id=?", (transfer_id,)) cursor.connection.commit() cursor.close() except: try: cursor.close() except: pass import traceback traceback.print_exc() finally: tools.try_release_lock(tools.premiumizeDB_lock) def _dict_factory(cursor, row): d = {} for idx, col in enumerate(cursor.description): d[col[0]] = row[idx] return d def _hash_function(function_instance, *args): return _get_function_name(function_instance) + _generate_md5(args) def _get_function_name(function_instance): return re.sub('.+\smethod\s|.+function\s|\sat\s.+|\sof\s.+', '', repr(function_instance)) def _generate_md5(*args): md5_hash = hashlib.md5() try: [md5_hash.update(str(arg)) for arg in args] except: [md5_hash.update(str(arg).encode('utf-8')) for arg in args] return str(md5_hash.hexdigest()) def _is_cache_valid(cached_time, cache_timeout): now = int(time.time()) diff = now - cached_time return (cache_timeout * 3600) > diff def cache_version_check(): if _find_cache_version(): cache_clear() def _find_cache_version(): import os versionFile = os.path.join(tools.dataPath, 'cache.v') try: with open(versionFile, 'rb') as fh: oldVersion = fh.read() except: oldVersion = '0' try: curVersion = tools.addon('script.module.seren').getAddonInfo('version') if oldVersion != curVersion: with open(versionFile, 'wb') as fh: fh.write(curVersion) return True else: return False except: return False def clear_local_bookmarks(): cursor = _get_connection_cursor(tools.get_video_database_path()) cursor.execute("SELECT * FROM files WHERE strFilename LIKE '%plugin.video.seren%'") file_ids = [str(i['idFile']) for i in cursor.fetchall()] for table in ["bookmark", "streamdetails", "files"]: cursor.execute("DELETE FROM {} WHERE idFile IN ({})".format(table, ','.join(file_ids))) cursor.connection.commit() cursor.close()