Ejemplo n.º 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
     )
Ejemplo n.º 2
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
Ejemplo n.º 3
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
        )
Ejemplo n.º 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
Ejemplo n.º 5
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
Ejemplo n.º 6
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
Ejemplo n.º 7
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
Ejemplo n.º 8
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
Ejemplo n.º 9
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
Ejemplo n.º 10
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
Ejemplo n.º 11
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
Ejemplo n.º 12
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
Ejemplo n.º 13
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
Ejemplo n.º 14
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)
Ejemplo n.º 15
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)