diff --git a/VERSION b/VERSION index 055a08e4..acc5c62c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.5-develop80 +1.16.5-develop81 diff --git a/docs/home/environmental.md b/docs/home/environmental.md index 41ea987e..1f2eb0f4 100644 --- a/docs/home/environmental.md +++ b/docs/home/environmental.md @@ -21,6 +21,7 @@ These docs are assuming you have a basic understanding of Docker concepts. One | [Libraries First](#libraries-first) | `-lf` or `--libraries-first` | `PMM_LIBRARIES_FIRST` | | [Ignore Schedules](#ignore-schedules) | `-is` or `--ignore-schedules` | `PMM_IGNORE_SCHEDULES` | | [Ignore Ghost](#ignore-ghost) | `-ig` or `--ignore-ghost` | `PMM_IGNORE_GHOST` | +| [Cache Libraries](#cache-libraries) | `-ca` or `--cache-libraries` | `PMM_CACHE_LIBRARIES` | | [Delete Collections](#delete-collections) | `-dc` or `--delete-collections` | `PMM_DELETE_COLLECTIONS` | | [Resume Run](#resume-run) | `-re` or `--resume` | `PMM_RESUME` | | [No Countdown](#no-countdown) | `-nc` or `--no-countdown` | `PMM_NO_COUNTDOWN` | @@ -574,6 +575,45 @@ docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex +### Cache Libraries + +Cache the library Load for 1 day. + + + + + + + + + + + + + + + + + +
ShellEnvironment
Flags-ca or --cache-librariesPMM_CACHE_LIBRARIES
Example--cache-librariesPMM_CACHE_LIBRARIES=true
+ +
+ Local Environment + +```shell +python plex_meta_manager.py --cache-libraries +``` + +
+
+ Docker Environment + +```shell +docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex-meta-manager --cache-libraries +``` + +
+ ### Delete Collections Delete all collections in a Library prior to running collections/operations. diff --git a/modules/builder.py b/modules/builder.py index e25cf3c0..acee8952 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -2342,7 +2342,7 @@ class CollectionBuilder: logger.error(e) if self.run_again: self.run_again_shows.extend(missing_tvdb_ids) - if len(self.missing_parts) > 0 and self.library.is_show and self.details["save_missing"] is True: + if len(self.missing_parts) > 0 and self.library.is_show and self.details["show_missing"] is True: for missing in self.missing_parts: logger.info(f"{self.name} {self.Type} | X | {missing}") return added_to_radarr, added_to_sonarr diff --git a/modules/cache.py b/modules/cache.py index 81e18939..5690f378 100644 --- a/modules/cache.py +++ b/modules/cache.py @@ -740,15 +740,6 @@ class Cache: with closing(connection.cursor()) as cursor: cursor.execute(f"INSERT OR IGNORE INTO {arr}_adds({id_type}, library) VALUES(?, ?)", (t_id, library)) - def update_list_ids(self, list_key, media_ids): - final_ids = [] - for media_id, media_type in media_ids: - final_ids.append((list_key, media_id, media_type)) - with sqlite3.connect(self.cache_path) as connection: - connection.row_factory = sqlite3.Row - with closing(connection.cursor()) as cursor: - cursor.executemany(f"INSERT OR IGNORE INTO list_ids(list_key, media_id, media_type) VALUES(?, ?, ?)", final_ids) - def update_list_cache(self, list_type, list_data, expired, expiration): list_key = None expiration_date = datetime.now() if expired is True else (datetime.now() - timedelta(days=expiration)) @@ -778,6 +769,15 @@ class Cache: expired = time_between_insertion.days > expiration return list_key, expired + def update_list_ids(self, list_key, media_ids): + final_ids = [] + for media_id, media_type in media_ids: + final_ids.append((list_key, media_id, media_type)) + with sqlite3.connect(self.cache_path) as connection: + connection.row_factory = sqlite3.Row + with closing(connection.cursor()) as cursor: + cursor.executemany(f"INSERT OR IGNORE INTO list_ids(list_key, media_id, media_type) VALUES(?, ?, ?)", final_ids) + def query_list_ids(self, list_key): ids = [] with sqlite3.connect(self.cache_path) as connection: diff --git a/modules/convert.py b/modules/convert.py index e1b01046..15e4754d 100644 --- a/modules/convert.py +++ b/modules/convert.py @@ -204,25 +204,36 @@ class Convert: else: return None + def ids_from_cache(self, ratingKey, guid, item_type, check_id, library): + media_id_type = None + cache_id = None + imdb_check = None + expired = None + if self.config.Cache: + cache_id, imdb_check, media_type, expired = self.config.Cache.query_guid_map(guid) + if (cache_id or imdb_check) and not expired: + media_id_type = "movie" if "movie" in media_type else "show" + if item_type == "hama" and check_id.startswith("anidb"): + anidb_id = int(re.search("-(.*)", check_id).group(1)) + library.anidb_map[anidb_id] = ratingKey + elif item_type == "myanimelist": + library.mal_map[int(check_id)] = ratingKey + return media_id_type, cache_id, imdb_check, expired + + def scan_guid(self, guid_str): + guid = requests.utils.urlparse(guid_str) + return guid.scheme.split(".")[-1], guid.netloc + def get_id(self, item, library): expired = None tmdb_id = [] tvdb_id = [] imdb_id = [] anidb_id = None - guid = requests.utils.urlparse(item.guid) - item_type = guid.scheme.split(".")[-1] - check_id = guid.netloc - if self.config.Cache: - cache_id, imdb_check, media_type, expired = self.config.Cache.query_guid_map(item.guid) - if (cache_id or imdb_check) and not expired: - media_id_type = "movie" if "movie" in media_type else "show" - if item_type == "hama" and check_id.startswith("anidb"): - anidb_id = int(re.search("-(.*)", check_id).group(1)) - library.anidb_map[anidb_id] = item.ratingKey - elif item_type == "myanimelist": - library.mal_map[int(check_id)] = item.ratingKey - return media_id_type, cache_id, imdb_check + item_type, check_id = self.scan_guid(item.guid) + media_id_type, cache_id, imdb_check, expired = self.ids_from_cache(item.ratingKey, item.guid, item_type, check_id, library) + if (cache_id or imdb_check) and expired is False: + return media_id_type, cache_id, imdb_check try: if item_type == "plex": try: diff --git a/modules/library.py b/modules/library.py index 3ae3c2b0..7a2f4fa5 100644 --- a/modules/library.py +++ b/modules/library.py @@ -236,17 +236,27 @@ class Library(ABC): def map_guids(self, items): for i, item in enumerate(items, 1): - logger.ghost(f"Processing: {i}/{len(items)} {item.title}") - if item.ratingKey not in self.movie_rating_key_map and item.ratingKey not in self.show_rating_key_map: - id_type, main_id, imdb_id = self.config.Convert.get_id(item, self) + if isinstance(item, tuple): + logger.ghost(f"Processing: {i}/{len(items)}") + key, guid = item + else: + logger.ghost(f"Processing: {i}/{len(items)} {item.title}") + key = item.ratingKey + guid = item.guid + if key not in self.movie_rating_key_map and key not in self.show_rating_key_map: + if isinstance(item, tuple): + item_type, check_id = self.config.Convert.scan_guid(guid) + id_type, main_id, imdb_id, _ = self.config.Convert.ids_from_cache(key, guid, item_type, check_id, self) + else: + id_type, main_id, imdb_id = self.config.Convert.get_id(item, self) if main_id: if id_type == "movie": - self.movie_rating_key_map[item.ratingKey] = main_id[0] - util.add_dict_list(main_id, item.ratingKey, self.movie_map) + self.movie_rating_key_map[key] = main_id[0] + util.add_dict_list(main_id, key, self.movie_map) elif id_type == "show": - self.show_rating_key_map[item.ratingKey] = main_id[0] - util.add_dict_list(main_id, item.ratingKey, self.show_map) + self.show_rating_key_map[key] = main_id[0] + util.add_dict_list(main_id, key, self.show_map) if imdb_id: - util.add_dict_list(imdb_id, item.ratingKey, self.imdb_map) + util.add_dict_list(imdb_id, key, self.imdb_map) logger.info("") logger.info(f"Processed {len(items)} {self.type}s") diff --git a/plex_meta_manager.py b/plex_meta_manager.py index b7569614..a642678d 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -32,6 +32,7 @@ parser.add_argument("-lf", "--library-first", "--libraries-first", dest="library parser.add_argument("-rc", "-cl", "--collection", "--collections", "--run-collection", "--run-collections", dest="collections", help="Process only specified collections (comma-separated list)", type=str) parser.add_argument("-rl", "-l", "--library", "--libraries", "--run-library", "--run-libraries", dest="libraries", help="Process only specified libraries (comma-separated list)", type=str) parser.add_argument("-rm", "-m", "--metadata", "--metadata-files", "--run-metadata-files", dest="metadata", help="Process only specified Metadata files (comma-separated list)", type=str) +parser.add_argument("-ca", "--cache-library", "--cache-libraries", dest="cache_libraries", help="Cache Library load for 1 day", action="store_true", default=False) parser.add_argument("-dc", "--delete", "--delete-collections", dest="delete", help="Deletes all Collections in the Plex Library before running", action="store_true", default=False) parser.add_argument("-nc", "--no-countdown", dest="no_countdown", help="Run without displaying the countdown", action="store_true", default=False) parser.add_argument("-nm", "--no-missing", dest="no_missing", help="Run without running the missing section", action="store_true", default=False) @@ -76,6 +77,7 @@ library_first = get_arg("PMM_LIBRARIES_FIRST", args.library_first, arg_bool=True collections = get_arg("PMM_COLLECTIONS", args.collections) libraries = get_arg("PMM_LIBRARIES", args.libraries) metadata_files = get_arg("PMM_METADATA_FILES", args.metadata) +cache_libraries = get_arg("PMM_CACHE_LIBRARIES", args.cache_libraries, arg_bool=True) delete = get_arg("PMM_DELETE_COLLECTIONS", args.delete, arg_bool=True) resume = get_arg("PMM_RESUME", args.resume) no_countdown = get_arg("PMM_NO_COUNTDOWN", args.no_countdown, arg_bool=True) @@ -275,14 +277,29 @@ def update_libraries(config): library.query(collection.delete) library_status[library.name]["All Collections Deleted"] = str(datetime.now() - time_start).split('.')[0] - temp_items = library.cache_items() + time_start = datetime.now() + temp_items = None + list_key = None + expired = None + if config.Cache and cache_libraries: + list_key, expired = config.Cache.query_list_cache("library", library.mapping_name, 1) + if list_key and expired is False: + logger.info(f"Library: {library.mapping_name} loaded from Cache") + temp_items = config.Cache.query_list_ids(list_key) + + if not temp_items: + temp_items = library.cache_items() + if config.Cache and cache_libraries: + if list_key: + config.Cache.delete_list_ids(list_key) + list_key = config.Cache.update_list_cache("library", library.mapping_name, expired, 1) + config.Cache.update_list_ids(list_key, [(i.ratingKey, i.guid) for i in temp_items]) if not library.is_other and not library.is_music: - time_start = datetime.now() logger.info("") logger.separator(f"Mapping {library.name} Library", space=False, border=False) logger.info("") library.map_guids(temp_items) - library_status[library.name]["Library Loading and Mapping"] = str(datetime.now() - time_start).split('.')[0] + library_status[library.name]["Library Loading and Mapping"] = str(datetime.now() - time_start).split('.')[0] if config.library_first and not config.test_mode and not collection_only: if not overlays_only and library.library_operation: