def rescrape_with_disabled(db_type, meta, season=None, episode=None): from caches.providers_cache import ExternalProvidersCache from modules.sources import Sources from modules.nav_utils import clear_cache kodi_utils.show_busy_dialog() deleted = ExternalProvidersCache().delete_cache_single( db_type, str(meta['tmdb_id'])) if not deleted: return kodi_utils.notification(32574) clear_cache('internal_scrapers', silent=True) if db_type == 'movie': play_params = { 'mode': 'play_media', 'vid_type': 'movie', 'tmdb_id': meta['tmdb_id'], 'disabled_ignored': 'true', 'prescrape': 'false' } else: play_params = { 'mode': 'play_media', 'vid_type': 'episode', 'tmdb_id': meta['tmdb_id'], 'tvshowtitle': meta['rootname'], 'season': season, 'episode': episode, 'disabled_ignored': 'true', 'prescrape': 'false' } kodi_utils.hide_busy_dialog() Sources().playback_prep(play_params)
def furkPacks(self, name, file_id, highlight=None, download=False): from apis.furk_api import FurkAPI kodi_utils.show_busy_dialog() t_files = FurkAPI().t_files(file_id) t_files = [i for i in t_files if 'video' in i['ct'] and 'bitrate' in i] t_files.sort(key=lambda k: k['name'].lower()) kodi_utils.hide_busy_dialog() if download: return t_files default_furk_icon = kodi_utils.translate_path( 'special://home/addons/script.tikiart/resources/media/furk.png') list_items = [{ 'line1': '%.2f GB | %s' % (float(item['size']) / 1073741824, clean_file_name(item['name']).upper()), 'icon': default_furk_icon } for item in t_files] kwargs = { 'items': json.dumps(list_items), 'heading': name, 'highlight': highlight, 'enumerate': 'true', 'multi_choice': 'false', 'multi_line': 'false' } chosen_result = kodi_utils.select_dialog(t_files, **kwargs) if chosen_result is None: return None link = chosen_result['url_dl'] name = chosen_result['name'] return FenPlayer().run(link, 'video')
def imdb_keywords_choice(db_type, imdb_id, poster): from apis.imdb_api import imdb_keywords def _builder(): for item in keywords_info: obj = { 'line': item, 'function': json.dumps({ 'mode': mode, 'action': 'imdb_keywords_list_contents', 'list_id': item, 'iconImage': 'imdb.png' }) } yield obj kodi_utils.show_busy_dialog() keywords_info = imdb_keywords(imdb_id) if len(keywords_info) == 0: kodi_utils.hide_busy_dialog() kodi_utils.notification(32760, 2500) return None meta_type = 'movie' if db_type == 'movies' else 'tvshow' mode = 'build_%s_list' % meta_type items = list(_builder()) kodi_utils.hide_busy_dialog() return open_window(['windows.extras', 'ShowSelectMedia'], 'select_media.xml', items=items, poster=poster, context_active_action='keywords')
def manual_add_magnet_to_cloud(params): show_busy_dialog() function = [i[2] for i in debrid_list if i[0] == params['provider']][0] result = function.create_transfer(params['magnet_url']) function.clear_cache() hide_busy_dialog() if result == 'failed': notification(32490) else: notification(32576)
def new_page(self): try: show_busy_dialog() self.win.reset() self.current_page += 1 self.next_page_params['in_progress'] = 'true' self.list_items, self.next_page_params = self.ImagesInstance.run( self.next_page_params) hide_busy_dialog() self.make_page() except: self.close()
def debridPacks(self, debrid_provider, name, magnet_url, info_hash, highlight=None, download=False): if debrid_provider == 'Real-Debrid': from apis.real_debrid_api import RealDebridAPI as debrid_function icon = 'realdebrid.png' elif debrid_provider == 'Premiumize.me': from apis.premiumize_api import PremiumizeAPI as debrid_function icon = 'premiumize.png' elif debrid_provider == 'AllDebrid': from apis.alldebrid_api import AllDebridAPI as debrid_function icon = 'alldebrid.png' kodi_utils.show_busy_dialog() try: debrid_files = debrid_function().display_magnet_pack( magnet_url, info_hash) except: debrid_files = None kodi_utils.hide_busy_dialog() if not debrid_files: return kodi_utils.notification(32574) debrid_files.sort(key=lambda k: k['filename'].lower()) if download: return debrid_files, debrid_function default_debrid_icon = kodi_utils.translate_path( 'special://home/addons/script.tikiart/resources/media/%s' % icon) list_items = [{ 'line1': '%.2f GB | %s' % (float(item['size']) / 1073741824, clean_file_name(item['filename']).upper()), 'icon': default_debrid_icon } for item in debrid_files] kwargs = { 'items': json.dumps(list_items), 'heading': name, 'highlight': highlight, 'enumerate': 'true', 'multi_choice': 'false', 'multi_line': 'false' } chosen_result = kodi_utils.select_dialog(debrid_files, **kwargs) if chosen_result is None: return None url_dl = chosen_result['link'] if debrid_provider in ('Real-Debrid', 'AllDebrid'): link = debrid_function().unrestrict_link(url_dl) elif debrid_provider == 'Premiumize.me': link = debrid_function().add_headers_to_url(url_dl) name = chosen_result['filename'] return FenPlayer().run(link, 'video')
def playback_prep(self, params=None): self._clear_properties() if params: self.params = params self.prescrape = self.params.get('prescrape', self.prescrape) == 'true' self.background = self.params.get('background', 'false') == 'true' if self.background: kodi_utils.hide_busy_dialog() else: kodi_utils.show_busy_dialog() self.disabled_ignored = self.params.get( 'disabled_ignored', self.disabled_ignored) == 'true' self.from_library = self.params.get('library', 'False') == 'True' self.vid_type = self.params['vid_type'] self.tmdb_id = self.params['tmdb_id'] self.ep_name = self.params.get('ep_name') self.plot = self.params.get('plot') self.custom_title = self.params.get('custom_title', None) self.custom_year = self.params.get('custom_year', None) if 'autoplay' in self.params: self.autoplay = self.params.get('autoplay', 'False') == 'True' else: self.autoplay = settings.auto_play(self.vid_type) if 'season' in self.params: self.season = int(self.params['season']) else: self.season = '' if 'episode' in self.params: self.episode = int(self.params['episode']) else: self.episode = '' if 'meta' in self.params: self.meta = json.loads(self.params['meta']) else: self._grab_meta() self.active_internal_scrapers = settings.active_internal_scrapers() self.active_external = 'external' in self.active_internal_scrapers self.provider_sort_ranks = settings.provider_sort_ranks() self.sleep_time = settings.display_sleep_time() self.scraper_settings = settings.scraping_settings() self.include_prerelease_results = settings.include_prerelease_results() self.ignore_filters = settings.ignore_results_filter() self.filter_hevc = settings.filter_status('hevc') self.filter_hdr = settings.filter_status('hdr') self.filter_dv = settings.filter_status('dv') self.hybrid_allowed = self.filter_hdr in (0, 2) self.sort_function = settings.results_sort_order() self.display_uncached_torrents = settings.display_uncached_torrents() self.quality_filter = self._quality_filter() self.filter_size = get_setting('results.filter.size', '0') == 'true' self.include_unknown_size = get_setting( 'results.include.unknown.size') == 'true' self.include_3D_results = get_setting('include_3d_results') == 'true' self._update_meta() self._search_info() kodi_utils.set_property('fen_playback_meta', json.dumps(self.meta)) self.get_sources()
def runner(params): kodi_utils.show_busy_dialog() threads = [] append = threads.append action = params.get('action') if action == 'meta.single': Downloader(params).run() elif action == 'image': for item in ('thumb_url', 'image_url'): image_params = params image_params['url'] = params.pop(item) image_params['db_type'] = item Downloader(image_params).run() elif action.startswith('cloud'): Downloader(params).run() elif action == 'meta.pack': from modules.source_utils import find_season_in_release_title provider = params['provider'] if provider == 'furk': try: t_files = Sources().furkPacks(params['file_name'], params['file_id'], download=True) pack_choices = [ dict( params, **{ 'pack_files': { 'link': item['url_dl'], 'filename': item['name'], 'size': item['size'] } }) for item in t_files ] icon = 'furk.png' except: return kodi_utils.notification(32692) else: try: debrid_files, debrid_function = Sources().debridPacks( provider, params['name'], params['magnet_url'], params['info_hash'], download=True) pack_choices = [ dict(params, **{'pack_files': item}) for item in debrid_files ] icon = { 'Real-Debrid': 'realdebrid.png', 'Premiumize.me': 'premiumize.png', 'AllDebrid': 'alldebrid.png' }[provider] except: return kodi_utils.notification(32692) default_icon = kodi_utils.translate_path( 'special://home/addons/script.tikiart/resources/media/%s' % icon) chosen_list = select_pack_item(pack_choices, params['highlight'], default_icon) if not chosen_list: return if provider == 'furk': show_package = True else: show_package = json.loads( params['source']).get('package') == 'show' meta = json.loads(chosen_list[0].get('meta')) default_name = '%s (%s)' % (clean_file_name( get_title(meta)), meta.get('year')) default_foldername = kodi_utils.dialog.input(ls(32228), defaultt=default_name) for item in chosen_list: if show_package: season = find_season_in_release_title( item['pack_files']['filename']) if season: meta['season'] = season item['meta'] = json.dumps(meta) item['default_foldername'] = default_foldername else: pass append(Thread(target=Downloader(item).run)) [i.start() for i in threads]
def add_uncached_torrent(self, magnet_url, pack=False): def _return_failed(message=32574, cancelled=False): try: kodi_utils.progressDialog.close() except Exception: pass kodi_utils.hide_busy_dialog() kodi_utils.sleep(500) if cancelled: if kodi_utils.confirm_dialog(text=32044, top_space=True): kodi_utils.ok_dialog(heading=32733, text=ls(32732) % ls(32063), top_space=True) else: self.delete_transfer(transfer_id) else: kodi_utils.ok_dialog(heading=2733, text=message) return False kodi_utils.show_busy_dialog() transfer_id = self.create_transfer(magnet_url) if not transfer_id: return _return_failed() transfer_info = self.list_transfer(transfer_id) if not transfer_info: return _return_failed() if pack: self.clear_cache() kodi_utils.hide_busy_dialog() kodi_utils.ok_dialog(text=ls(32732) % ls(32063)) return True interval = 5 line = '%s[CR]%s[CR]%s' line1 = '%s...' % (ls(32732) % ls(32063)) line2 = transfer_info['filename'] line3 = transfer_info['status'] kodi_utils.progressDialog.create(ls(32733), line % (line1, line2, line3)) while not transfer_info['statusCode'] == 4: kodi_utils.sleep(1000 * interval) transfer_info = self.list_transfer(transfer_id) file_size = transfer_info['size'] line2 = transfer_info['filename'] if transfer_info['statusCode'] == 1: download_speed = round( float(transfer_info['downloadSpeed']) / (1000**2), 2) progress = int( float(transfer_info['downloaded']) / file_size * 100) if file_size > 0 else 0 line3 = ls(32734) % (download_speed, transfer_info['seeders'], progress, round(float(file_size) / (1000**3), 2)) elif transfer_info['statusCode'] == 3: upload_speed = round( float(transfer_info['uploadSpeed']) / (1000**2), 2) progress = int( float(transfer_info['uploaded']) / file_size * 100) if file_size > 0 else 0 line3 = ls(32735) % (upload_speed, progress, round(float(file_size) / (1000**3), 2)) else: line3 = transfer_info['status'] progress = 0 kodi_utils.progressDialog.update(progress, line % (line1, line2, line3)) if kodi_utils.monitor.abortRequested() == True: return sysexit() try: if kodi_utils.progressDialog.iscanceled(): return _return_failed(32736, cancelled=True) except Exception: pass if 5 <= transfer_info['statusCode'] <= 10: return _return_failed() kodi_utils.sleep(1000 * interval) try: kodi_utils.progressDialog.close() except Exception: pass kodi_utils.hide_busy_dialog() return True
def add_uncached_torrent(self, magnet_url, pack=False): from modules.kodi_utils import show_busy_dialog, hide_busy_dialog from modules.source_utils import supported_video_extensions def _return_failed(message=32574, cancelled=False): try: progressDialog.close() except Exception: pass hide_busy_dialog() sleep(500) if cancelled: if confirm_dialog(text=32044, top_space=True): ok_dialog(heading=32733, text=ls(32732) % ls(32054)) else: self.delete_torrent(torrent_id) else: ok_dialog(heading=32733, text=message) return False show_busy_dialog() try: active_count = self.torrents_activeCount() if active_count['nb'] >= active_count['limit']: return _return_failed() except: pass interval = 5 stalled = ('magnet_error', 'error', 'virus', 'dead') extensions = supported_video_extensions() torrent = self.add_magnet(magnet_url) torrent_id = torrent['id'] if not torrent_id: return _return_failed() torrent_info = self.torrent_info(torrent_id) if 'error_code' in torrent_info: return _return_failed() status = torrent_info['status'] line = '%s[CR]%s[CR]%s' if status == 'magnet_conversion': line1 = ls(32737) line2 = torrent_info['filename'] line3 = ls(32738) % torrent_info['seeders'] timeout = 100 progressDialog.create(ls(32733), line % (line1, line2, line3)) while status == 'magnet_conversion' and timeout > 0: progressDialog.update(timeout, line % (line1, line2, line3)) if monitor.abortRequested() == True: return sysexit() try: if progressDialog.iscanceled(): return _return_failed(32736, cancelled=True) except Exception: pass timeout -= interval sleep(1000 * interval) torrent_info = self.torrent_info(torrent_id) status = torrent_info['status'] if any(x in status for x in stalled): return _return_failed() line3 = ls(32738) % torrent_info['seeders'] try: progressDialog.close() except Exception: pass if status == 'downloaded': hide_busy_dialog() return True if status == 'magnet_conversion': return _return_failed() if any(x in status for x in stalled): return _return_failed(str(status)) if status == 'waiting_files_selection': video_files = [] append = video_files.append all_files = torrent_info['files'] for item in all_files: if any(item['path'].lower().endswith(x) for x in extensions): append(item) if pack: try: if len(video_files) == 0: return _return_failed() video_files.sort(key=lambda x: x['path']) torrent_keys = [str(i['id']) for i in video_files] if not torrent_keys: return _return_failed(ls(32736)) torrent_keys = ','.join(torrent_keys) self.add_torrent_select(torrent_id, torrent_keys) ok_dialog(text=ls(32732) % ls(32054)) self.clear_cache() hide_busy_dialog() return True except Exception: return _return_failed() else: try: video = max(video_files, key=lambda x: x['bytes']) file_id = video['id'] except ValueError: return _return_failed() self.add_torrent_select(torrent_id, str(file_id)) sleep(2000) torrent_info = self.torrent_info(torrent_id) status = torrent_info['status'] if status == 'downloaded': hide_busy_dialog() return True file_size = round(float(video['bytes']) / (1000**3), 2) line1 = '%s...' % (ls(32732) % ls(32054)) line2 = torrent_info['filename'] line3 = status progressDialog.create(ls(32733), line % (line1, line2, line3)) while not status == 'downloaded': sleep(1000 * interval) torrent_info = self.torrent_info(torrent_id) status = torrent_info['status'] if status == 'downloading': line3 = ls(32739) % ( file_size, round(float(torrent_info['speed']) / (1000**2), 2), torrent_info['seeders'], torrent_info['progress']) else: line3 = status progressDialog.update(int(float(torrent_info['progress'])), line % (line1, line2, line3)) if monitor.abortRequested() == True: return sys.exit() try: if progressDialog.iscanceled(): return _return_failed(32736, cancelled=True) except: pass if any(x in status for x in stalled): return _return_failed() try: progressDialog.close() except: pass hide_busy_dialog() return True hide_busy_dialog() return False
def options_menu(params, meta=None): def _builder(): for item in listing: line1 = item[0] line2 = item[1] if line2 == '': line2 = line1 yield {'line1': line1, 'line2': line2} content = params.get('content', None) if not content: content = kodi_utils.container_content()[:-1] season = params.get('season', None) episode = params.get('episode', None) if not meta: function = metadata.movie_meta if content == 'movie' else metadata.tvshow_meta meta_user_info = metadata.retrieve_user_info() meta = function('tmdb_id', params['tmdb_id'], meta_user_info) watched_indicators = settings.watched_indicators() on_str, off_str, currently_str, open_str, settings_str = ls(32090), ls( 32027), ls(32598), ls(32641), ls(32247) autoplay_status, autoplay_toggle, quality_setting = (on_str, 'false', 'autoplay_quality_%s' % content) if settings.auto_play(content) \ else (off_str, 'true', 'results_quality_%s' % content) quality_filter_setting = 'autoplay_quality_%s' % content if autoplay_status == on_str else 'results_quality_%s' % content autoplay_next_status, autoplay_next_toggle = ( on_str, 'false') if settings.autoplay_next_episode() else (off_str, 'true') results_xml_style_status = get_setting('results.xml_style', 'Default') results_filter_ignore_status, results_filter_ignore_toggle = ( on_str, 'false') if settings.ignore_results_filter() else (off_str, 'true') results_sorting_status = get_setting('results.sort_order_display').replace( '$ADDON[plugin.video.fen 32582]', ls(32582)) current_results_highlights_action = get_setting('highlight.type') results_highlights_status = ls( 32240) if current_results_highlights_action == '0' else ls( 32583) if current_results_highlights_action == '1' else ls(32241) current_subs_action = get_setting('subtitles.subs_action') current_subs_action_status = 'Auto' if current_subs_action == '0' else ls( 32193) if current_subs_action == '1' else off_str active_internal_scrapers = [ i.replace('_', '') for i in settings.active_internal_scrapers() ] current_scrapers_status = ', '.join([ i for i in active_internal_scrapers ]) if len(active_internal_scrapers) > 0 else 'N/A' current_quality_status = ', '.join( settings.quality_filter(quality_setting)) uncached_torrents_status, uncached_torrents_toggle = ( on_str, 'false') if settings.display_uncached_torrents() else (off_str, 'true') listing = [] base_str1 = '%s%s' base_str2 = '%s: [B]%s[/B]' % (currently_str, '%s') if content in ('movie', 'episode'): multi_line = 'true' listing += [(ls(32014), '', 'clear_and_rescrape')] listing += [(ls(32006), '', 'rescrape_with_disabled')] listing += [(ls(32135), '', 'scrape_with_custom_values')] listing += [(base_str1 % (ls(32175), ' (%s)' % content), base_str2 % autoplay_status, 'toggle_autoplay')] if autoplay_status == on_str and content == 'episode': listing += [ (base_str1 % (ls(32178), ''), base_str2 % autoplay_next_status, 'toggle_autoplay_next') ] listing += [(base_str1 % (ls(32105), ' (%s)' % content), base_str2 % current_quality_status, 'set_quality')] listing += [(base_str1 % ('', '%s %s' % (ls(32055), ls(32533))), base_str2 % current_scrapers_status, 'enable_scrapers')] if autoplay_status == off_str: listing += [(base_str1 % ('', ls(32140)), base_str2 % results_xml_style_status, 'set_results_xml_display')] listing += [ (base_str1 % ('', ls(32151)), base_str2 % results_sorting_status, 'set_results_sorting') ] listing += [(base_str1 % ('', ls(32138)), base_str2 % results_highlights_status, 'set_results_highlights')] listing += [(base_str1 % ('', ls(32686)), base_str2 % results_filter_ignore_status, 'set_results_filter_ignore')] listing += [(base_str1 % ('', ls(32183)), base_str2 % current_subs_action_status, 'set_subs_action') ] if 'external' in active_internal_scrapers: listing += [(base_str1 % ('', ls(32160)), base_str2 % uncached_torrents_status, 'toggle_torrents_display_uncached')] else: multi_line = 'false' listing += [(ls(32046), '', 'extras_lists_choice')] if content in ('movie', 'tvshow') and meta: listing += [ (ls(32604) % (ls(32028) if meta['mediatype'] == 'movie' else ls(32029)), '', 'clear_media_cache') ] if watched_indicators == 1: listing += [(ls(32497) % ls(32037), '', 'clear_trakt_cache')] if content in ('movie', 'episode'): listing += [(ls(32637), '', 'clear_scrapers_cache')] listing += [('%s %s' % (ls(32118), ls(32513)), '', 'open_external_scrapers_manager')] listing += [('%s %s %s' % (open_str, ls(32522), settings_str), '', 'open_scraper_settings')] listing += [('%s %s %s' % (open_str, ls(32036), settings_str), '', 'open_fen_settings')] listing += [(ls(32640), '', 'save_and_exit')] list_items = list(_builder()) heading = ls(32646).replace('[B]', '').replace('[/B]', '') kwargs = { 'items': json.dumps(list_items), 'heading': heading, 'enumerate': 'false', 'multi_choice': 'false', 'multi_line': multi_line } choice = kodi_utils.select_dialog([i[2] for i in listing], **kwargs) if choice in (None, 'save_and_exit'): return elif choice == 'clear_and_rescrape': return clear_and_rescrape(content, meta, season, episode) elif choice == 'rescrape_with_disabled': return rescrape_with_disabled(content, meta, season, episode) elif choice == 'scrape_with_custom_values': return scrape_with_custom_values(content, meta, season, episode) elif choice == 'toggle_autoplay': set_setting('auto_play_%s' % content, autoplay_toggle) elif choice == 'toggle_autoplay_next': set_setting('autoplay_next_episode', autoplay_next_toggle) elif choice == 'enable_scrapers': enable_scrapers_choice() elif choice == 'set_results_xml_display': results_layout_choice() elif choice == 'set_results_sorting': results_sorting_choice() elif choice == 'set_results_filter_ignore': set_setting('ignore_results_filter', results_filter_ignore_toggle) elif choice == 'set_results_highlights': results_highlights_choice() elif choice == 'set_quality': set_quality_choice(quality_filter_setting) elif choice == 'set_subs_action': set_subtitle_choice() elif choice == 'extras_lists_choice': extras_lists_choice() elif choice == 'clear_media_cache': return refresh_cached_data(meta['mediatype'], 'tmdb_id', meta['tmdb_id'], meta['tvdb_id'], settings.get_language()) elif choice == 'toggle_torrents_display_uncached': set_setting('torrent.display.uncached', uncached_torrents_toggle) elif choice == 'clear_trakt_cache': return clear_cache('trakt') elif choice == 'clear_scrapers_cache': return clear_scrapers_cache() elif choice == 'open_external_scrapers_manager': return external_scrapers_manager() elif choice == 'open_scraper_settings': return kodi_utils.execute_builtin( 'Addon.OpenSettings(script.module.fenomscrapers)') elif choice == 'open_fen_settings': return open_settings('0.0') if choice == 'clear_trakt_cache' and content in ('movie', 'tvshow', 'season', 'episode'): kodi_utils.execute_builtin('Container.Refresh') kodi_utils.show_busy_dialog() kodi_utils.sleep(200) kodi_utils.hide_busy_dialog() options_menu(params, meta=meta)
def add_uncached_torrent(self, magnet_url, pack=False): from modules.kodi_utils import show_busy_dialog, hide_busy_dialog from modules.source_utils import supported_video_extensions def _transfer_info(transfer_id): info = self.transfers_list() if 'status' in info and info['status'] == 'success': for item in info['transfers']: if item['id'] == transfer_id: return item return {} def _return_failed(message=32574, cancelled=False): try: progressDialog.close() except Exception: pass hide_busy_dialog() sleep(500) if cancelled: if confirm_dialog(heading=32733, text=32044, top_space=True): ok_dialog(heading=32733, text=ls(32732) % ls(32061)) else: self.delete_transfer(transfer_id) else: ok_dialog(heading=32733, text=message) return False show_busy_dialog() extensions = supported_video_extensions() transfer_id = self.create_transfer(magnet_url) if not transfer_id['status'] == 'success': return _return_failed(transfer_id.get('message')) transfer_id = transfer_id['id'] transfer_info = _transfer_info(transfer_id) if not transfer_info: return _return_failed() if pack: self.clear_cache() hide_busy_dialog() ok_dialog(text=ls(32732) % ls(32061)) return True interval = 5 line = '%s[CR]%s[CR]%s' line1 = '%s...' % (ls(32732) % ls(32061)) line2 = transfer_info['name'] line3 = transfer_info['message'] progressDialog.create(ls(32733), line % (line1, line2, line3)) while not transfer_info['status'] == 'seeding': sleep(1000 * interval) transfer_info = _transfer_info(transfer_id) line3 = transfer_info['message'] progressDialog.update(int(float(transfer_info['progress']) * 100), line % (line1, line2, line3)) if monitor.abortRequested() == True: return sysexit() try: if progressDialog.iscanceled(): return _return_failed(ls(32736), cancelled=True) except Exception: pass if transfer_info.get('status') == 'stalled': return _return_failed() sleep(1000 * interval) try: progressDialog.close() except Exception: pass hide_busy_dialog() return True