def acestream_engine(): import acestream from support.common import temp_path return acestream.Engine(host=plugin.get_setting('as-host', unicode), port=plugin.get_setting('as-port', int, default=62062), save_path=temp_path() if plugin.get_setting('save-files', int) else None)
def utorrent_client(): from support.torrent.client import UTorrentClient return UTorrentClient(login=plugin.get_setting('utorrent-login', unicode), password=plugin.get_setting('utorrent-password', unicode), host=plugin.get_setting('utorrent-host', unicode), port=plugin.get_setting('utorrent-port', int, default=8080))
def transmission_client(): from support.torrent.client import TransmissionClient return TransmissionClient(login=plugin.get_setting('transmission-login', unicode), password=plugin.get_setting('transmission-password', unicode), host=plugin.get_setting('transmission-host', unicode), port=plugin.get_setting('transmission-port', int, default=9091), path=plugin.get_setting('transmission-path', unicode))
def transmission_client(): from support.torrent.client import TransmissionClient return TransmissionClient( login=plugin.get_setting('transmission-login', unicode), password=plugin.get_setting('transmission-password', unicode), host=plugin.get_setting('transmission-host', unicode), port=plugin.get_setting('transmission-port', int, default=9091), path=plugin.get_setting('transmission-path', unicode))
def get_scraper(): from support.services import xrequests_session return LostFilmScraper(login=plugin.get_setting('login', unicode), password=plugin.get_setting('password', unicode), cookie_jar=plugin.addon_data_path('cookies'), xrequests_session=xrequests_session(), max_workers=BATCH_SERIES_COUNT, series_cache=series_cache(), shows_ids_cache=shows_ids())
def download_torrent(torrent): from support import services client = services.torrent_client() path = plugin.get_setting('custom-save-path', unicode) \ if plugin.get_setting('use-custom-save-path', bool) \ else save_path(local=True) client.add(torrent, path) if plugin.has_addon(client.addon_id) and \ xbmcgui.Dialog().yesno(lang(40160), *(lang(40161) % client.addon_name).split("|")): plugin.run_addon(client.addon_id)
def ts_engine(): import torrserve from support.common import temp_path return torrserve.Engine(host=plugin.get_setting('ts-host', unicode), port=plugin.get_setting('ts-port', int, default=8090), pre_buffer_bytes=plugin.get_setting( 'ts-preload-mb', int))
def get_scraper(): from support.services import xrequests_session anonymized_urls = plugin.get_storage().setdefault('anonymized_urls', [], ttl=24 * 60 * 7) return LostFilmScraper(login=plugin.get_setting('login', unicode), password=plugin.get_setting('password', unicode), cookie_jar=plugin.addon_data_path('cookies'), xrequests_session=xrequests_session(), max_workers=BATCH_SERIES_COUNT, series_cache=series_cache(), anonymized_urls=anonymized_urls)
def acestream_engine(): import acestream from support.common import temp_path return acestream.Engine(host=plugin.get_setting('as-host', unicode), port=plugin.get_setting('as-port', int, default=62062), save_path=temp_path() if plugin.get_setting( 'save-files', int) else None)
def save_path(local=False): path = plugin.get_setting('save-path', unicode) if path == plugin.get_setting('temp-path', unicode): raise LocalizedError(33032, "Path for downloaded files and temporary path should not be the same", check_settings=True) if not direxists(path): raise LocalizedError(33031, "Invalid save path", check_settings=True) if local: path = ensure_path_local(path) path = path.strip("\\/") return ensure_unicode(path)
def index(): plugin.set_content('episodes') skip = plugin.request.arg('skip') per_page = plugin.get_setting('per-page', int) need_clear = plugin.get_setting('clear-cache', bool, default=False) if (need_clear): clear_series() check_first_start() scraper = get_scraper() episodes = scraper.browse_episodes(skip) if episodes and not skip: check_last_episode(episodes[0]) new_episodes = library_new_episodes() new_str = "(%s) " % tf.color(str( len(new_episodes)), NEW_LIBRARY_ITEM_COLOR) if new_episodes else "" total = len(episodes) header = [{ 'label': lang(40401), 'path': plugin.url_for('browse') }, { 'label': lang(40407) % new_str, 'path': plugin.url_for('browse_library'), 'context_menu': update_library_menu() }] items = [] if skip: skip_prev = max(skip - per_page, 0) total += 1 items.append({ 'label': lang(34003), 'path': plugin.request.url_with_params(skip=skip_prev) }) elif header: items.extend(header) total += len(header) plugin.add_items(with_fanart(items), total) for batch_res in batch(episodes, BATCH_EPISODES_COUNT): if abort_requested(): break items = itemify_episodes(batch_res) plugin.add_items(with_fanart(items), total) items = [] if scraper.has_more: skip_next = (skip or 0) + per_page items.append({ 'label': lang(34004), 'path': plugin.request.url_with_params(skip=skip_next) }) plugin.finish(items=with_fanart(items), cache_to_disc=False, update_listing=skip is not None)
def torrent2http_engine(): import torrent2http from support.common import temp_path return torrent2http.Engine( download_path=temp_path(), state_file=plugin.addon_data_path('t2h_state'), connections_limit=plugin.get_setting('t2h-max-connections', int, default=None), download_kbps=plugin.get_setting('t2h-download-rate', int, default=None), upload_kbps=plugin.get_setting('t2h-upload-rate', int, default=None), log_overall_progress=plugin.get_setting('t2h-debug-mode', bool), log_pieces_progress=plugin.get_setting('t2h-debug-mode', bool), debug_alerts=plugin.get_setting('t2h-debug-mode', bool), listen_port=plugin.get_setting('t2h-listen-port', int, default=6881), use_random_port=plugin.get_setting('t2h-use-random-port', bool), trackers=['http://retracker.local/announce'], dht_routers=[ 'dht.transmissionbt.com', 'router.utorrent.com', 'router.bittorrent.com' ], max_idle_timeout=5, keep_files=True, enable_utp=False)
def mark_watched(self, series_id, season, episode, mode='on', force_mode=None): if plugin.get_setting('enable_sync', bool): if episode == 999 or episode == '999': types = 'markseason' else: types = 'markepisode' val = "{0}{1:03}{2:03}".format(series_id, season, episode) session = self._get_session() if force_mode: mode = force_mode else: watched = self.get_mark(series_id) if val in watched: mode = 'off' params = { 'session': session, 'act': 'serial', 'type': types, 'val': val, 'auto': 0, 'mode': mode } self.fetch(self.API_URL, data=params)
def toggle_episode_watched(series_id, season, episode): xbmc.executebuiltin(actions.toggle_watched()) if plugin.get_setting('sync_mark_watch', bool): scraper = get_scraper() scraper.api.mark_watched(series_id, season, episode, mode='on') if series_id in library_items(): library_new_episodes().remove_by(series_id, season, episode)
def browse_episodes(self, skip=0): self.ensure_authorized() self.check_for_new_series() page = (skip or 0) / 10 + 1 only_favorites = plugin.get_setting('check_only_favorites', bool) doc = self._get_new_episodes_doc(page, only_favorites) with Timer(logger=self.log, name="Parsing episodes list"): body = doc.find('div', {'class': 'content history'}) series_titles = body.find('div', {'class': 'name-ru'}).strings episode_titles = body.find('div', {'class': 'alpha'}).strings[::2] original_episode_titles = body.find('div', {'class': 'beta'}).strings[::2] release_dates = body.find('div', {'class': 'alpha'}).strings[1::2] release_dates = [str_to_date(r_d.split(' ')[-1], '%d.%m.%Y') for r_d in release_dates] paging = doc.find('div', {'class': 'pagging-pane'}) selected_page = paging.find('a', {'class': 'item active'}).text last_page = paging.find('a', {'class': 'item'}).last.text self.has_more = int(selected_page) < int(last_page) data_codes = body.find('div', {'class': 'haveseen-btn.*?'}).attrs('data-code') series_ids, season_numbers, episode_numbers = zip(*[parse_data_code(s or "") for s in data_codes]) posters = [img_url(i, y, z) for i, y, z in zip(series_ids, season_numbers, episode_numbers)] images = [img_url(series_id) for series_id in series_ids] icons = [img_url(series_id).replace('/poster.jpg', '/image.jpg') for series_id in series_ids] data = zip(series_ids, series_titles, season_numbers, episode_numbers, episode_titles, original_episode_titles, release_dates, icons, posters, images) episodes = [Episode(*e) for e in data if e[0]] self.log.info("Got %d episode(s) successfully" % (len(episodes))) self.log.debug(repr(episodes).decode("unicode-escape")) return episodes
def episode_label(e, same_series=False): """ :param same_series: :type e: Episode """ label = "" if not e.is_complete_season: label += tf.color( "S%02dE%02d " % (e.season_number, int(e.episode_number)), 'blue') if e in library_new_episodes(): label += tf.color("* ", NEW_LIBRARY_ITEM_COLOR) if e.series_id in library_items() and not same_series: color = LIBRARY_ITEM_COLOR else: color = 'white' if not same_series: label += tf.color(e.series_title, color) + " / " + e.episode_title else: label += tf.color(e.episode_title, color) if e.original_title and plugin.get_setting('show-original-title', bool): try: title = e.original_title.decode("utf-8") except: title = e.original_title label += " / " + title return label
def temp_path(): path = ensure_path_local(plugin.get_setting('temp-path', unicode)) if not xbmcvfs.mkdir(path): raise LocalizedError(33030, "Invalid temporary path", check_settings=True) return path
def torrent_stream(): """ :rtype : TorrentStream """ stream = plugin.get_setting('torrent-stream', choices=(torrent2http_stream, ace_stream)) return stream()
def browse_trailers(): per_page = plugin.get_setting('per-page', int) skip = plugin.request.arg('skip') scraper = get_scraper() trailers = scraper.browse_trailers(skip) items = [] if len(trailers) < per_page: skip = (skip or 1) + 1 trailers.extend(scraper.browse_trailers(skip)) total = len(trailers) if skip > 2: skip_prev = max(skip - 1, 0) total += 1 items.append({ 'label': lang(34003), 'path': plugin.request.url_with_params(skip=skip_prev) }) plugin.add_items(with_fanart(items), total) for batch_res in batch(trailers, BATCH_EPISODES_COUNT): if abort_requested(): break items = itemify_trailers(batch_res) plugin.add_items(with_fanart(items), total) items = [] if scraper.has_more: skip_next = (skip or 1) + 1 items.append({ 'label': lang(34004), 'path': plugin.request.url_with_params(skip=skip_next) }) plugin.add_items(with_fanart(items), len(items)) plugin.finish(update_listing=skip > 2)
def torrent_stream(): """ :rtype : TorrentStream """ stream = plugin.get_setting('torrent-stream', choices=(ts_stream, elementum_stream)) return stream()
def torrent2http_stream(): from support.torrent.stream import Torrent2HttpStream return Torrent2HttpStream(engine=torrent2http_engine(), buffering_progress=stream_buffering_progress(), playing_progress=stream_playing_progress(), pre_buffer_bytes=plugin.get_setting('t2h-pre-buffer-mb', int) * 1024 * 1024)
def ts_stream(): from support.torrent.stream import TorrServeStream return TorrServeStream(engine=ts_engine(), buffering_progress=stream_buffering_progress(), playing_progress=stream_playing_progress(), pre_buffer_bytes=plugin.get_setting( 'ts-preload-mb', int))
def batch(iterable, size=None): from itertools import islice, chain size = size or plugin.get_setting('batch-results', int) sourceiter = iter(iterable) while True: batchiter = islice(sourceiter, size) yield list(chain([batchiter.next()], batchiter))
def update_library(): plugin.log.info("Starting LostFilm.TV library update...") progress = xbmcgui.DialogProgressBG() scraper = get_scraper() series_ids = library_items() if plugin.get_setting('sync_favorites', bool) and plugin.get_setting( 'enable_sync', bool): fav_ids = scraper.get_favorite_series() series_ids = list(set(series_ids + fav_ids)) total = len(series_ids) lib = get_library() processed = 0 with closing(progress): progress.create(lang(30000), lang(40409)) series_episodes = {} for ids in batch(series_ids, BATCH_SERIES_COUNT): series_episodes.update(scraper.get_series_episodes_bulk(ids)) processed += len(ids) progress.update(processed * 100 / total) if abort_requested(): return medias = [] for series_id, episodes in series_episodes.iteritems(): medias.extend( library.Episode(folder=e.series_title, title=e.episode_title.replace(u'й', u'й'), season_number=e.season_number, episode_number=e.episode_numbers, url=episode_url(e), time_added=e.release_date, episode=e) for e in episodes if not e.is_complete_season) lib.sync(medias) new_episodes = library_new_episodes() new_episodes |= NewEpisodes(lib.added_medias) if plugin.get_setting('update-xbmc-library', bool): if lib.added_medias or lib.created_medias or lib.updated_medias: plugin.wait_library_scan() plugin.log.info("Starting XBMC library update...") plugin.update_library('video', plugin.get_setting('library-path', unicode)) if lib.removed_files: plugin.wait_library_scan() plugin.log.info("Starting XBMC library clean...") plugin.clean_library('video') plugin.log.info("LostFilm.TV library update finished.") return lib.added_medias or lib.created_medias or lib.updated_medias or lib.removed_files
def download_menu(e): from xbmcswift2 import actions if plugin.get_setting('torrent-client', int): return [(lang(40308), actions.background(plugin.url_for('download', series=e.series_id, season=e.season_number, episode=e.episode_number)))] else: return []
def check_site(): import requests from support.services import antizapret az = antizapret() r = requests.get('http://www.lostfilm.tv/', proxies=az.get_proxy_list() if plugin.get_setting( 'use_proxy', bool) else None) return r
def torrent_client(): """ :rtype : TorrentClient """ client = plugin.get_setting('torrent-client', choices=(None, utorrent_client, transmission_client)) return client() if client else None
def torrent2http_stream(): from support.torrent.stream import Torrent2HttpStream return Torrent2HttpStream( engine=torrent2http_engine(), buffering_progress=stream_buffering_progress(), playing_progress=stream_playing_progress(), pre_buffer_bytes=plugin.get_setting('t2h-pre-buffer-mb', int) * 1024 * 1024)
def add_to_library(series_id): items = library_items() scraper = get_scraper() if series_id not in items: items.append(series_id) if plugin.get_setting('sync_add_remove_favorite', bool): scraper.api.favorite(series_id) plugin.set_setting('update-library', True) xbmc.executebuiltin(actions.refresh())
def remove_from_library(series_id): items = library_items() scraper = get_scraper() if series_id in items: items.remove(series_id) if plugin.get_setting('sync_add_remove_favorite', bool): scraper.api.favorite(series_id) library_new_episodes().remove_by(series_id=series_id) plugin.set_setting('update-library', True) xbmc.executebuiltin(actions.refresh())
def favorite(self, series_id): if plugin.get_setting('enable_sync', bool): session = self._get_session() params = { 'session': session, 'act': 'serial', 'type': 'follow', 'id': series_id } self.fetch(self.API_URL, data=params)
def play_episode(series, season, episode): select_quality = plugin.request.arg('select_quality') link = select_torrent_link(series, season, episode, select_quality) if not link: return torrent = get_torrent(link.url) library_new_episodes().remove_by(series, season, episode) play_torrent(torrent, episode) if plugin.get_setting('sync_mark_watch', bool): scraper = get_scraper() scraper.api.mark_watched(series, season, episode, force_mode='on')
def select_quality_menu(e): """ :type e: Episode """ if plugin.get_setting('quality', int) > 0: url = episode_url(e, True) if e.is_complete_season: return [(lang(40303), actions.update_view(url))] else: return [(lang(40301), actions.play_media(url))] else: return []
def purge_temp_dir(): path = temp_path() temp_size = get_dir_size(path) max_size = plugin.get_setting('temp-max-size', int) * 1024 * 1024 * 1024 log.info("Current temporary folder size / Max size: %d / %d", temp_size, max_size) if temp_size > max_size: log.info("Purging temporary folder...") shutil.rmtree(path, True) if not direxists(path): # noinspection PyBroadException if xbmcvfs.mkdirs(path): log.info("New temporary folder size: %d", get_dir_size(path))
def update_library(): plugin.log.info("Starting LostFilm.TV library update...") progress = xbmcgui.DialogProgressBG() scraper = get_scraper() series_ids = library_items() total = len(series_ids) lib = get_library() processed = 0 with closing(progress): progress.create(lang(30000), lang(40409)) series_episodes = {} for ids in batch(series_ids, BATCH_SERIES_COUNT): series_episodes.update(scraper.get_series_episodes_bulk(ids)) sleep(2000) processed += len(ids) progress.update(processed * 100 / total) if abort_requested(): return medias = [] for series_id, episodes in series_episodes.iteritems(): medias.extend(library.Episode(folder=e.series_title, title=e.episode_title, season_number=e.season_number, episode_number=e.episode_numbers, url=episode_url(e), time_added=e.release_date, episode=e) for e in episodes if not e.is_complete_season) lib.sync(medias) new_episodes = library_new_episodes() new_episodes |= NewEpisodes(lib.added_medias) if plugin.get_setting('update-xbmc-library', bool): if lib.added_medias or lib.created_medias or lib.updated_medias: plugin.wait_library_scan() plugin.log.info("Starting XBMC library update...") plugin.update_library('video', plugin.get_setting('library-path', unicode)) if lib.removed_files: plugin.wait_library_scan() plugin.log.info("Starting XBMC library clean...") plugin.clean_library('video') plugin.log.info("LostFilm.TV library update finished.") return lib.added_medias or lib.created_medias or lib.updated_medias or lib.removed_files
def create_lostfilm_source(): from support.sources import Sources, TvDbScraperSettings, SourceAlreadyExists sources = Sources() plugin.log.info("Creating LostFilm.TV source...") try: sources.add_video(plugin.get_setting('library-path', unicode), 'LostFilm.TV', TvDbScraperSettings()) except SourceAlreadyExists: plugin.set_setting('lostfilm-source-created', True) raise LocalizedError(40408, "Source is already exist") plugin.log.info("LostFilm.TV source created, restart needed...") plugin.set_setting('lostfilm-source-created', True) d = xbmcgui.Dialog() if d.yesno(lang(40404), lang(40405)): xbmc.executebuiltin('Quit')
def series_label(s, highlight_library_items=True): """ :type s: Series """ if s.id in library_items() and highlight_library_items: color = LIBRARY_ITEM_COLOR else: color = 'white' label = tf.color(s.title, color) if plugin.get_setting('show-original-title', bool): label += " / " + s.original_title new_episodes = library_new_episodes().get_by(series_id=s.id) if new_episodes: label += " (%s)" % tf.color(str(len(new_episodes)), NEW_LIBRARY_ITEM_COLOR) return label
def select_torrent_link(series, season, episode, force=False): scraper = get_scraper() links = scraper.get_torrent_links(series, season, episode) qualities = sorted(Quality) quality = plugin.get_setting('quality', int) ordered_links = [next((l for l in links if l.quality == q), None) for q in qualities] if not quality or force or not ordered_links[quality - 1]: filtered_links = [l for l in ordered_links if l] if not filtered_links: return options = ["%s / %s" % (tf.color(l.quality.localized, 'white'), tf.human_size(l.size)) for l in filtered_links] res = xbmcgui.Dialog().select(lang(40400), options) if res < 0: return return filtered_links[res] else: return ordered_links[quality - 1]
def xrequests_session(): from requests.packages.urllib3.util import Retry from support.xrequests import Session use_proxy = plugin.get_setting('use-proxy', int) session = Session(max_retries=Retry(total=2, status_forcelist=[500, 502, 503, 504], backoff_factor=0.3), timeout=5, proxy_list=proxy_list() if use_proxy else None) # noinspection PyUnusedLocal,PyShadowingNames def always_use_proxy(request, response): return True if use_proxy == 2: session.add_proxy_need_check(always_use_proxy) return session
def torrent2http_engine(): import torrent2http from support.common import temp_path return torrent2http.Engine(download_path=temp_path(), state_file=plugin.addon_data_path('t2h_state'), connections_limit=plugin.get_setting('t2h-max-connections', int, default=None), download_kbps=plugin.get_setting('t2h-download-rate', int, default=None), upload_kbps=plugin.get_setting('t2h-upload-rate', int, default=None), log_overall_progress=plugin.get_setting('t2h-debug-mode', bool), log_pieces_progress=plugin.get_setting('t2h-debug-mode', bool), debug_alerts=plugin.get_setting('t2h-debug-mode', bool), listen_port=plugin.get_setting('t2h-listen-port', int, default=6881), use_random_port=plugin.get_setting('t2h-use-random-port', bool), trackers=['http://retracker.local/announce'], dht_routers=['dht.transmissionbt.com', 'router.utorrent.com', 'router.bittorrent.com'], max_idle_timeout=5, keep_files=True, enable_utp=False)
def index(): plugin.set_content('episodes') skip = plugin.request.arg('skip') per_page = plugin.get_setting('per-page', int) scraper = get_scraper() episodes = scraper.browse_episodes(skip) if episodes and not skip: check_last_episode(episodes[0]) check_first_start() new_episodes = library_new_episodes() new_str = "(%s) " % tf.color(str(len(new_episodes)), NEW_LIBRARY_ITEM_COLOR) if new_episodes else "" total = len(episodes) header = [ {'label': lang(40401), 'path': plugin.url_for('browse_all_series')}, {'label': lang(40407) % new_str, 'path': plugin.url_for('browse_library'), 'context_menu': update_library_menu()}, ] items = [] if skip: skip_prev = max(skip - per_page, 0) total += 1 items.append({ 'label': lang(34003), 'path': plugin.request.url_with_params(skip=skip_prev) }) elif header: items.extend(header) total += len(header) plugin.add_items(with_fanart(items), total) for batch_res in batch(episodes, BATCH_EPISODES_COUNT): if abort_requested(): break items = itemify_episodes(batch_res) plugin.add_items(with_fanart(items), total) items = [] if scraper.has_more: skip_next = (skip or 0) + per_page items.append({ 'label': lang(34004), 'path': plugin.request.url_with_params(skip=skip_next) }) plugin.finish(items=with_fanart(items), cache_to_disc=False, update_listing=skip is not None)
def save_files(files, rename=False, on_finish=None): save = plugin.get_setting('save-files', int) if not save: on_finish() return src, dst = temp_path(), save_path() files_dict = {} for old_path in files: old_path = ensure_unicode(old_path) rel_path = os.path.relpath(old_path, src) new_path = os.path.join(dst, rel_path) if xbmcvfs.exists(new_path): if rename: if xbmcvfs.delete(old_path): log.info("File %s deleted.", old_path) continue files_dict[old_path] = new_path if not files_dict: if on_finish: on_finish() return files_to_copy = {} if save != 2 or xbmcgui.Dialog().yesno(lang(30000), *lang(40162).split("|")): for n, old_path in enumerate(files): old_path = ensure_unicode(old_path) if old_path not in files_dict: continue new_path = files_dict[old_path] xbmcvfs.mkdirs(os.path.dirname(new_path)) if rename: log.info("Renaming %s to %s...", old_path, new_path) if not xbmcvfs.rename(old_path, new_path): log.info("Renaming failed. Trying to copy and delete old file...") files_to_copy[old_path] = new_path else: log.info("Success.") else: files_to_copy[old_path] = new_path if files_to_copy: copy_files(files_to_copy, delete=rename, on_finish=on_finish) elif on_finish: on_finish()
def episode_label(e, same_series=False): """ :type e: Episode """ label = "" if not e.is_complete_season: label += tf.color("%02d.%s " % (e.season_number, e.episode_number), 'blue') if e in library_new_episodes(): label += tf.color("* ", NEW_LIBRARY_ITEM_COLOR) if e.series_id in library_items() and not same_series: color = LIBRARY_ITEM_COLOR else: color = 'white' if not same_series: label += tf.color(e.series_title, color) + " / " + e.episode_title else: label += tf.color(e.episode_title, color) if e.original_title and plugin.get_setting('show-original-title', bool): label += " / " + e.original_title return label
def get_library(): path = plugin.get_setting('library-path', unicode) path = xbmc.translatePath(path) return library.Library(path)
def check_first_start(): if is_authorized() and not plugin.get_setting('first-start', bool): d = xbmcgui.Dialog() plugin.set_setting('first-start', 'true') if d.yesno(lang(40402), *(lang(40403).split("|"))): create_lostfilm_source()
def stream_playing_progress(): from support.progress import XbmcOverlayTorrentTransferProgress if plugin.get_setting('show-playing-progress', bool): return XbmcOverlayTorrentTransferProgress(window_id=12005)
if e.kwargs.get('dialog'): xbmcgui.Dialog().ok(lang(30000), *e.localized.split("|")) else: notify(e.localized) except Exception as e: plugin.log.exception(e) notify(lang(40410)) finally: plugin.close_storages() return False if __name__ == '__main__': sleep(5000) safe_update_library() next_run = None while not abort_requested(): now = datetime.datetime.now() update_on_demand = plugin.get_setting('update-library', bool) if not next_run: next_run = now next_run += datetime.timedelta(hours=12) plugin.log.info("Scheduling next library update at %s" % next_run) elif now > next_run and not xbmc.Player().isPlaying() or update_on_demand: updated = safe_update_library() if update_on_demand: plugin.set_setting('update-library', False) if updated: plugin.refresh_container() next_run = None sleep(1000)