Beispiel #1
0
 def find_plex_show(self, media_entries: List[MediaEntry]) -> DownloadableQueue:
     """
     Fetches a list of shows by title if they exist, if not an empty collection would be returned
     :param media_entries: a model consisting of various anime names from anilist
     :return: a list of optional shows
     """
     shows_in_plex_matching_users_list: List[Optional[Show]] = list()
     show_media_entry_mapped_to_plex: List[Optional[MediaEntry]] = list()
     shows_missing_in_plex_found_on_users_list: List[Optional[MediaEntry]] = list()
     for entry in media_entries:
         if entry.media.status != 'NOT_YET_RELEASED':
             if entry.status != 'COMPLETED':
                 show = self.plex_controller.find_all_by_title(
                     entry,
                     lambda: self.__add_missing_item(
                         entry,
                         shows_missing_in_plex_found_on_users_list
                     )
                 )
                 if show:
                     shows_in_plex_matching_users_list += show
                     show_media_entry_mapped_to_plex.append(entry)
     EventLogHelper.log_info(
         f"Fetched list of shows by title, returned {len(shows_in_plex_matching_users_list)} results.\n"
         f"Items which could not be found in plex {len(shows_missing_in_plex_found_on_users_list)}.",
         self.__class__.__name__,
         inspect.currentframe().f_code.co_name
     )
     return DownloadableQueue(
         shows_in_plex_matching_users_list,
         shows_missing_in_plex_found_on_users_list,
         show_media_entry_mapped_to_plex
     )
Beispiel #2
0
    def __search_for_shows(anime_section: ShowSection, media_entry: MediaEntry) -> SearchResult:
        search_results: List[Show] = list()
        search_match_term_match: str = ""

        search_terms: List[str] = media_entry.generate_search_terms()

        for search_term in search_terms:
            search_results = anime_section.search(title=search_term)
            if search_results:
                search_match_term_match = search_term
                break

        filtered_search_results: List[Show] = list(
            filter(
                lambda show: PlexController.__matches_search_term(
                    show.title, search_match_term_match
                ), search_results)
        )

        if filtered_search_results:
            EventLogHelper.log_info(
                f"Search term match found `{search_match_term_match}` -> `{filtered_search_results}`",
                "PlexController",
                inspect.currentframe().f_code.co_name
            )

        return SearchResult(
            filtered_search_results,
            search_match_term_match
        )
Beispiel #3
0
 def save_or_update(self, value: Optional[Dict]):
     result_ids: List[Any] = self.db.upsert(value, where('id') == value['id'])
     if result_ids.__len__() < 1:
         EventLogHelper.log_error(f"Error objects to {ANILIST_DATABASE}",
                                  self.__class__.__name__,
                                  inspect.currentframe().f_code.co_name,
                                  logging.CRITICAL)
Beispiel #4
0
    def _find_missing_episodes(
        self, show: Optional[Show], search_results: Optional[List[TorrentInfo]]
    ) -> List[Optional[TorrentInfo]]:
        """
        Find episodes that might not exist and add then to the download queue
        :return:
        """
        torrent_matches: List[Optional[TorrentInfo]] = list()

        for search_result in search_results:
            sleep(self.sleep_duration)
            if not search_result.added_anime_info():
                continue

            anime_info = search_result.anime_info
            if f"[{anime_info.release_group}]" != self.config.torrent_preferred_group:
                continue

            is_episode_present = False
            for episode in show.episodes():
                is_episode_present = episode.index == int(
                    float(anime_info.episode_number))
                if is_episode_present:
                    break

            if not is_episode_present:
                EventLogHelper.log_info(
                    f"Adding missing episode: `{anime_info.file_name}`",
                    self.__class__.__name__,
                    inspect.currentframe().f_code.co_name)
                torrent_matches.append(search_result)

        return torrent_matches
Beispiel #5
0
 def added_anime_info(self) -> bool:
     """
     Added anime info for the the current torrent
     :return: true if successful otherwise false if the release is a Batch
     """
     parsed_file_name = ""
     try:
         parsed_file_name = anitopy.parse(self.name)
         if not isinstance(parsed_file_name['episode_number'], str) \
                 or parsed_file_name.__contains__('release_information') \
                 and parsed_file_name['release_information'] == 'Batch':
             print()
             EventLogHelper.log_info(
                 f"Skipping torrent : `{self.name}` | {parsed_file_name}",
                 self.__class__.__name__,
                 inspect.currentframe().f_code.co_name)
             print(
                 '<------------------------------------------------------------>'
             )
             return False
         else:
             torrent_name_info = from_dict(TorrentAnimeInfo,
                                           parsed_file_name)
             self.anime_info = torrent_name_info
             return True
     except Exception as e:
         print()
         EventLogHelper.log_info(
             f"Error converting dictionary to data class\n"
             f"Details: {e} | {parsed_file_name}", self.__class__.__name__,
             inspect.currentframe().f_code.co_name)
         print(
             '<------------------------------------------------------------>'
         )
         return False
Beispiel #6
0
 def __handle_response(self, media_collection_list: Optional[List[Dict]],
                       anilist_store: AniListStore):
     try:
         for item in media_collection_list:
             for entry in item['entries']:
                 anilist_store.save_or_update(entry)
     except Exception as e:
         EventLogHelper.log_error(f"Error handling response -> {e}",
                                  self.__class__.__name__,
                                  inspect.currentframe().f_code.co_name,
                                  logging.CRITICAL)
Beispiel #7
0
 def create_dictionary_class(self, response: MediaEntry) -> Optional[Dict]:
     parsed_dictionary: Optional[Dict] = None
     try:
         parsed_dictionary = dict(response)
     except Exception as e:
         print()
         EventLogHelper.log_info(f"Error converting data class to dictionary\n"
                                 f"details -> {e}",
                                 self.__class__.__name__,
                                 inspect.currentframe().f_code.co_name)
         print('<------------------------------------------------------------>')
     return parsed_dictionary
Beispiel #8
0
 def create_data_class(self, response: Optional[Dict]) -> Optional[MediaEntry]:
     parsed_object: Optional[List[MediaEntry]] = None
     try:
         parsed_object = from_dict(MediaEntry, response)
     except Exception as e:
         print()
         EventLogHelper.log_info(f"Error converting dictionary to data class\n"
                                 f"details -> {e}",
                                 self.__class__.__name__,
                                 inspect.currentframe().f_code.co_name)
         print('<------------------------------------------------------------>')
     return parsed_object
Beispiel #9
0
 def get_all(self) -> List[Optional[TorrentInfo]]:
     query_results: List[Optional[TorrentInfo]] = list()
     documents: List[Document] = self.db.all()
     try:
         for document in documents:
             data_class = self.model_helper.create_data_class(document)
             query_results.append(data_class)
     except Exception as e:
         EventLogHelper.log_error(
             f"Database value is not in a valid format {APP_DATABASE}\n"
             f"Details: {e}", self.__class__.__name__,
             inspect.currentframe().f_code.co_name, logging.CRITICAL)
     return query_results
Beispiel #10
0
 def __init__(self) -> None:
     super().__init__()
     try:
         self.config = json.loads(StorageUtil.read_file('config', 'plex.json'))
         auth = json.loads(StorageUtil.read_file("auth", "credentials.json"))
         self.plex = PlexServer(auth["url"], auth["token"])
     except Exception as e:
         EventLogHelper.log_error(
             f"Encountered exception while initializing controller -> {e}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name,
             logging.CRITICAL
         )
Beispiel #11
0
 def __search_for_matching_until_found(search_page: int,
                                       search_terms: List[str]):
     for search_term in search_terms:
         # noinspection PyTypeChecker,PyCallByClass
         search_results = Nyaa.search(keyword=search_term,
                                      category='1',
                                      page=search_page)
         if search_results:
             EventLogHelper.log_info(
                 f"Nyaa search results found for search term: `{search_term}` | on page: `{search_page}`"
                 f" | found `{len(search_results)}` results",
                 "NyaaController",
                 inspect.currentframe().f_code.co_name)
             return search_results
Beispiel #12
0
    def save_or_update(self, value: Optional[Dict]):
        result_ids: List[Any] = list()

        try:
            result_ids += self.db.upsert(value, where('name') == value['name'])
        except Exception as e:
            EventLogHelper.log_error(
                f"Error saving or updating model to {value}\n"
                f"Details: {e}", self.__class__.__name__,
                inspect.currentframe().f_code.co_name, logging.CRITICAL)

        if len(result_ids) < 1:
            EventLogHelper.log_error(f"Error objects to {APP_DATABASE}",
                                     self.__class__.__name__,
                                     inspect.currentframe().f_code.co_name,
                                     logging.CRITICAL)
Beispiel #13
0
 def create_data_class(
         self, response: Dict[Optional[str],
                              Optional[str]]) -> Optional[TorrentInfo]:
     parsed_object: Optional[TorrentInfo] = None
     try:
         parsed_object = from_dict(TorrentInfo, response)
     except Exception as e:
         print()
         EventLogHelper.log_info(
             f"Error converting dictionary to data class\n"
             f"Details: {e}", self.__class__.__name__,
             inspect.currentframe().f_code.co_name)
         print(
             '<------------------------------------------------------------>'
         )
     return parsed_object
Beispiel #14
0
    def search_nyaa_for_shows(self, download_queue: DownloadableQueue) -> Optional[List[TorrentInfo]]:
        """
        Searches nyaa.si for torrents matching the tittle name/s
        :param download_queue: a model consisting of a tuple shows and media entries of missing episodes
        :return: a list of torrent results
        """
        torrent_search_result_list: List[TorrentInfo] = list()
        torrent_search_result_list_for_missing_shows: List[TorrentInfo] = list()

        print()
        print('-------------------------------------------------------')

        EventLogHelper.log_info(
            f"Searching for missing items in plex",
            self.__class__.__name__,
            inspect.currentframe().f_code.co_name
        )
        for media in download_queue.shows_missing_in_plex:
            torrent_search_results = self.nyaa_controller.search_for_missing_shows(media, self.app_config)
            if len(torrent_search_results) > 0:
                torrent_search_result_list_for_missing_shows += torrent_search_results
            else:
                EventLogHelper.log_info(
                    f"Unable to find {media.media.title.userPreferred} from nyaa.si",
                    self.__class__.__name__,
                    inspect.currentframe().f_code.co_name
                )
        print('-------------------------------------------------------')
        print()

        print()
        print('-------------------------------------------------------')
        EventLogHelper.log_info(
            f"Searching for matching items in plex",
            self.__class__.__name__,
            inspect.currentframe().f_code.co_name
        )
        for show, media in zip(download_queue.shows_found_in_plex, download_queue.show_media_entry_in_plex):
            torrent_search_results = self.nyaa_controller.search_for_shows(
                show, media, self.app_config
            )

            if len(torrent_search_results) > 0:
                print()
                torrent_search_result_list += torrent_search_results
            else:
                EventLogHelper.log_info(
                    f"No new releases found for the following torrent/s `{media.generate_search_terms()}` on nyaa.si",
                    self.__class__.__name__,
                    inspect.currentframe().f_code.co_name
                )
                print()
        print('-------------------------------------------------------')
        print()
        return torrent_search_result_list + torrent_search_result_list_for_missing_shows
Beispiel #15
0
 def __init__(self) -> None:
     super().__init__()
     try:
         __config = json.loads(
             StorageUtil.read_file('auth', 'credentials.json'))
         if __config is not None:
             credentials = __config["transmission"]
             self.client = Client(host=credentials["host"],
                                  port=credentials["port"],
                                  username=credentials["username"],
                                  password=credentials["password"])
         else:
             self.client = Client()
     except Exception as e:
         EventLogHelper.log_error(
             f"Encountered exception while initializing controller -> {e}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name, logging.CRITICAL)
Beispiel #16
0
    def __move_torrent_to_monitored_directory(self, torrent_info: TorrentInfo):
        try:
            StorageUtil.copy_or_move_file(
                filename=f"{torrent_info.name}.torrent",
                directory_path=self.app_config.build_parent_save_path(
                    torrent_info.anime_info.anime_title
                ),
                destination_path=self.app_config.torrent_monitor_directory,
                keep_file=self.app_config.torrent_keep_file_after_queuing
            )

            model = self.nyaa_model_helper.create_dictionary_class(torrent_info)
            self.app_store.save_or_update(model)
        except Exception as e:
            EventLogHelper.log_error(
                f"__move_torrent_to_monitored_directory -> StorageUtil.copy_or_move_file -> {e}",
                self.__class__.__name__,
                inspect.currentframe().f_code.co_name
            )
Beispiel #17
0
 def add_torrent_magnet(self, filename: str) -> bool:
     """
     adds a magnet link instead of the actual torrent file contents
     :param filename: like of where the file can be found or path to actual file
     :return: True if the operation was a success otherwise False
     """
     try:
         torrent = self.client.torrent.add(filename=filename)
         sleep(.5)
         EventLogHelper.log_info(
             f"Added torrent file url to torrent client -> {torrent} | {filename}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name)
         return True
     except Exception as e:
         EventLogHelper.log_warning(
             f"Unable to add torrent to transmission -> {e}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name)
         return False
Beispiel #18
0
    def download_torrent_file(torrent_info: TorrentInfo,
                              config: AppConfig) -> bool:
        """
        Downloads a .torrent file and saves it into the app/torrents/ directory
        :param config: configuration class
        :param torrent_info:
        :return: True if the operation was successful otherwise False
        """
        try:
            print()
            torrent_file_name = f"{torrent_info.anime_info.file_name}.torrent"
            response: Response = get(url=torrent_info.download_url,
                                     allow_redirects=True,
                                     stream=True,
                                     timeout=30.0)

            if response.ok:
                StorageUtil.write_file_in_app(
                    directory_path=config.build_parent_save_path(
                        torrent_info.anime_info.anime_title),
                    filename=torrent_file_name,
                    contents=response,
                    write_mode='wb')
            else:
                EventLogHelper.log_info(
                    f"Requesting torrent for download failed : {response}\n"
                    f"Retrying in 5 seconds..", "NyaaController",
                    inspect.currentframe().f_code.co_name)
                sleep(5)
                NyaaController.download_torrent_file(torrent_info, config)
            print(
                '<------------------------------------------------------------>'
            )
        except Exception as e:
            EventLogHelper.log_error(
                f"Encountered exception while downloading torrent file -> {e}",
                "NyaaController",
                inspect.currentframe().f_code.co_name, logging.CRITICAL)
            return False
        return True
Beispiel #19
0
 def add_torrent(self, file_path: str, file_name: str):
     """
     adds the torrent from an existing file path
     :param file_name: name of the file
     :param file_path: where the file can be found
     :return: True if the operation was a success otherwise False
     """
     try:
         file_contents = StorageUtil.read_file(file_path, file_name)
         torrent = self.client.torrent.add(metainfo=file_contents)
         sleep(1.5)
         EventLogHelper.log_info(
             f"Added torrent file to download client -> {torrent} | {file_path}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name)
         return True
     except Exception as e:
         EventLogHelper.log_warning(
             f"Unable to add torrent to transmission -> {e}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name)
         return False
Beispiel #20
0
    def start_application(self) -> None:
        """
        Application starting point
        :return:
        """
        try:
            anime_list: List[Optional[MediaEntry]] = self.fetch_anime_list()
            print('-------------------------------------------------------')
            if anime_list:
                download_queue: DownloadableQueue = self.find_plex_show(anime_list)
                print('-------------------------------------------------------')
                if download_queue.contains_items():
                    print('-------------------------------------------------------')
                    search_results = self.search_nyaa_for_shows(download_queue)

                    if search_results is not None and len(search_results) > 0:
                        for torrent_info in search_results:
                            if torrent_info.anime_info is None:
                                print()
                                EventLogHelper.log_info(
                                    f"Skipping torrent without anime info -> {torrent_info}",
                                    self.__class__.__name__,
                                    inspect.currentframe().f_code.co_name
                                )
                                continue
                            downloaded_torrent = self.__find_downloadable_torrents(torrent_info)
                            if downloaded_torrent is None or len(downloaded_torrent) < 1:
                                queued: bool = self.__queue_downloaded_torrent_file(torrent_info)
                                if not queued:
                                    self.__download_torrent_file(torrent_info)
                            else:
                                print()
                                EventLogHelper.log_info(
                                    f"Skipping existing download -> {torrent_info.anime_info}",
                                    self.__class__.__name__,
                                    inspect.currentframe().f_code.co_name
                                )
                    else:
                        print()
                        EventLogHelper.log_info(
                            f"No new episodes to download, ending execution of script",
                            self.__class__.__name__,
                            inspect.currentframe().f_code.co_name
                        )
                    print('-------------------------------------------------------')
        except Exception as e:
            EventLogHelper.log_error(f"Uncaught exception thrown -> {e}",
                                     self.__class__.__name__,
                                     inspect.currentframe().f_code.co_name)
Beispiel #21
0
 def find_all_by_title(self, media_entry: MediaEntry, add_missing) -> List[Optional[Show]]:
     """
     Search for plex shows within a configuration given library name
     :param add_missing:
     :param media_entry: media to look for
     :return: list of optional shows
     """
     all_shows = list()
     try:
         anime_section: Optional[ShowSection] = self.plex.library.section(self.config['section_library_name'])
         if anime_section is not None:
             search_result: SearchResult = self.__search_for_shows(anime_section, media_entry)
             if search_result.search_results:
                 for show in search_result.search_results:
                     show_episodes_count = len(show.episodes())
                     episodes = media_entry.media.episodes
                     if show_episodes_count >= episodes:
                         continue
                     all_shows.append(show)
             else:
                 print()
                 search_term = media_entry.media.title.userPreferred
                 EventLogHelper.log_warning(
                     f"Search term not found `{search_term}`, adding to missing shows list",
                     self.__class__.__name__,
                     inspect.currentframe().f_code.co_name
                 )
                 print()
                 add_missing()
     except Exception as e:
         EventLogHelper.log_info(
             f"{e}",
             self.__class__.__name__,
             inspect.currentframe().f_code.co_name
         )
     return all_shows
Beispiel #22
0
 def __download_torrent_file(self, torrent_info: TorrentInfo):
     print()
     EventLogHelper.log_info(f"Downloading torrent for file -> {torrent_info.name}",
                             self.__class__.__name__,
                             inspect.currentframe().f_code.co_name)
     is_download_successful = self.nyaa_controller.download_torrent_file(torrent_info, self.app_config)
     if is_download_successful:
         model_dictionary = self.nyaa_model_helper.create_dictionary_class(torrent_info)
         self.app_store.save_or_update(model_dictionary)
         print()
         EventLogHelper.log_info(f"Download successful, anime attributes -> {torrent_info.anime_info}",
                                 self.__class__.__name__,
                                 inspect.currentframe().f_code.co_name)
         self.__move_torrent_to_monitored_directory(torrent_info)
     else:
         print()
         EventLogHelper.log_info(f"Failed to download, anime attributes -> {torrent_info.anime_info}",
                                 self.__class__.__name__,
                                 inspect.currentframe().f_code.co_name)