[84] #864 add --playlists-only

pull/877/head
meisnate12 3 years ago
parent daa9b8be3f
commit bc86686cd5

@ -1 +1 @@
1.16.5-develop83 1.16.5-develop84

@ -13,6 +13,7 @@ These docs are assuming you have a basic understanding of Docker concepts. One
| [Run](#run) | `-r` or `--run` | `PMM_RUN` | | [Run](#run) | `-r` or `--run` | `PMM_RUN` |
| [Run Tests](#run-tests) | `-rt`, `--tests`, or `--run-tests` | `PMM_TEST` | | [Run Tests](#run-tests) | `-rt`, `--tests`, or `--run-tests` | `PMM_TEST` |
| [Collections Only](#collections-only) | `-co` or `--collections-only` | `PMM_COLLECTIONS_ONLY` | | [Collections Only](#collections-only) | `-co` or `--collections-only` | `PMM_COLLECTIONS_ONLY` |
| [Plsylists Only](#plsylists-only) | `-po` or `--plsylists-only` | `PMM_PLAYLISTS_ONLY` |
| [Operations](#operations) | `-op` or `--operations` | `PMM_OPERATIONS` | | [Operations](#operations) | `-op` or `--operations` | `PMM_OPERATIONS` |
| [Overlays](#overlays) | `-ov` or `--overlays` | `PMM_OVERLAYS` | | [Overlays](#overlays) | `-ov` or `--overlays` | `PMM_OVERLAYS` |
| [Run Collections](#run-collections) | `-rc` or `--run-collections` | `PMM_COLLECTIONS` | | [Run Collections](#run-collections) | `-rc` or `--run-collections` | `PMM_COLLECTIONS` |
@ -212,7 +213,7 @@ docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex
### Collections Only ### Collections Only
Only run collection metadata/YAML files, skip library operations. Only run collection metadata/YAML files, skip library operations, overlays, and collections/metadata.
<table class="dualTable colwidths-auto align-default table"> <table class="dualTable colwidths-auto align-default table">
<tr> <tr>
@ -249,9 +250,48 @@ docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex
</details> </details>
### Playlists Only
Only run playlist metadata/YAML files, skip library operations, overlays, and collections/metadata.
<table class="dualTable colwidths-auto align-default table">
<tr>
<th style="background-color: #222;"></th>
<th>Shell</th>
<th>Environment</th>
</tr>
<tr>
<th>Flags</th>
<td><code>-po</code> or <code>--playlists-only</code></td>
<td><code>PMM_PLAYLISTS_ONLY</code></td>
</tr>
<tr>
<th>Example</th>
<td><code>--playlists-only</code></td>
<td><code>PMM_PLAYLISTS_ONLY=true</code></td>
</tr>
</table>
<details>
<summary>Local Environment</summary>
```shell
python plex_meta_manager.py --playlists-only
```
</details>
<details>
<summary>Docker Environment</summary>
```shell
docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex-meta-manager --playlists-only
```
</details>
### Operations ### Operations
Only run library operations skipping collections and overlays. Only run library operations skipping collections/metadata, playlists, and overlays.
<table class="dualTable colwidths-auto align-default table"> <table class="dualTable colwidths-auto align-default table">
<tr> <tr>
@ -290,7 +330,7 @@ docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex
### Overlays ### Overlays
Only run library overlays skipping operations and collections. Only run library overlays skipping collections/metadata, playlists, and operations.
<table class="dualTable colwidths-auto align-default table"> <table class="dualTable colwidths-auto align-default table">
<tr> <tr>

@ -26,6 +26,7 @@ parser.add_argument("-is", "--ignore-schedules", dest="ignore_schedules", help="
parser.add_argument("-ig", "--ignore-ghost", dest="ignore_ghost", help="Run ignoring ghost logging", action="store_true", default=False) parser.add_argument("-ig", "--ignore-ghost", dest="ignore_ghost", help="Run ignoring ghost logging", action="store_true", default=False)
parser.add_argument("-rt", "--test", "--tests", "--run-test", "--run-tests", dest="test", help="Run in debug mode with only collections that have test: true", action="store_true", default=False) parser.add_argument("-rt", "--test", "--tests", "--run-test", "--run-tests", dest="test", help="Run in debug mode with only collections that have test: true", action="store_true", default=False)
parser.add_argument("-co", "--collection-only", "--collections-only", dest="collection_only", help="Run only collection operations", action="store_true", default=False) parser.add_argument("-co", "--collection-only", "--collections-only", dest="collection_only", help="Run only collection operations", action="store_true", default=False)
parser.add_argument("-po", "--playlist-only", "--playlists-only", dest="playlist_only", help="Run only playlist operations", action="store_true", default=False)
parser.add_argument("-op", "--operation", "--operations", "-lo", "--library-only", "--libraries-only", "--operation-only", "--operations-only", dest="operations", help="Run only operations", action="store_true", default=False) parser.add_argument("-op", "--operation", "--operations", "-lo", "--library-only", "--libraries-only", "--operation-only", "--operations-only", dest="operations", help="Run only operations", action="store_true", default=False)
parser.add_argument("-ov", "--overlay", "--overlays", "--overlay-only", "--overlays-only", dest="overlays", help="Run only overlays", action="store_true", default=False) parser.add_argument("-ov", "--overlay", "--overlays", "--overlay-only", "--overlays-only", dest="overlays", help="Run only overlays", action="store_true", default=False)
parser.add_argument("-lf", "--library-first", "--libraries-first", dest="library_first", help="Run library operations before collections", action="store_true", default=False) parser.add_argument("-lf", "--library-first", "--libraries-first", dest="library_first", help="Run library operations before collections", action="store_true", default=False)
@ -71,6 +72,7 @@ test = get_arg("PMM_TEST", args.test, arg_bool=True)
ignore_schedules = get_arg("PMM_IGNORE_SCHEDULES", args.ignore_schedules, arg_bool=True) ignore_schedules = get_arg("PMM_IGNORE_SCHEDULES", args.ignore_schedules, arg_bool=True)
ignore_ghost = get_arg("PMM_IGNORE_GHOST", args.ignore_ghost, arg_bool=True) ignore_ghost = get_arg("PMM_IGNORE_GHOST", args.ignore_ghost, arg_bool=True)
collection_only = get_arg("PMM_COLLECTIONS_ONLY", args.collection_only, arg_bool=True) collection_only = get_arg("PMM_COLLECTIONS_ONLY", args.collection_only, arg_bool=True)
playlist_only = get_arg("PMM_PLAYLISTS_ONLY", args.playlist_only, arg_bool=True)
operations_only = get_arg(["PMM_OPERATIONS", "PMM_LIBRARIES_ONLY"], args.operations, arg_bool=True) operations_only = get_arg(["PMM_OPERATIONS", "PMM_LIBRARIES_ONLY"], args.operations, arg_bool=True)
overlays_only = get_arg(["PMM_OVERLAYS", "PMM_OVERLAYS_ONLY"], args.overlays, arg_bool=True) overlays_only = get_arg(["PMM_OVERLAYS", "PMM_OVERLAYS_ONLY"], args.overlays, arg_bool=True)
library_first = get_arg("PMM_LIBRARIES_FIRST", args.library_first, arg_bool=True) library_first = get_arg("PMM_LIBRARIES_FIRST", args.library_first, arg_bool=True)
@ -171,6 +173,7 @@ def start(attrs):
attrs["version"] = version attrs["version"] = version
attrs["no_missing"] = no_missing attrs["no_missing"] = no_missing
attrs["collection_only"] = collection_only attrs["collection_only"] = collection_only
attrs["playlist_only"] = playlist_only
attrs["operations_only"] = operations_only attrs["operations_only"] = operations_only
attrs["overlays_only"] = overlays_only attrs["overlays_only"] = overlays_only
logger.separator(debug=True) logger.separator(debug=True)
@ -179,6 +182,7 @@ def start(attrs):
logger.debug(f"--run (PMM_RUN): {run}") logger.debug(f"--run (PMM_RUN): {run}")
logger.debug(f"--run-tests (PMM_TEST): {test}") logger.debug(f"--run-tests (PMM_TEST): {test}")
logger.debug(f"--collections-only (PMM_COLLECTIONS_ONLY): {collection_only}") logger.debug(f"--collections-only (PMM_COLLECTIONS_ONLY): {collection_only}")
logger.debug(f"--playlists-only (PMM_PLAYLISTS_ONLY): {playlist_only}")
logger.debug(f"--operations (PMM_OPERATIONS): {operations_only}") logger.debug(f"--operations (PMM_OPERATIONS): {operations_only}")
logger.debug(f"--overlays (PMM_OVERLAYS): {overlays_only}") logger.debug(f"--overlays (PMM_OVERLAYS): {overlays_only}")
logger.debug(f"--libraries-first (PMM_LIBRARIES_FIRST): {library_first}") logger.debug(f"--libraries-first (PMM_LIBRARIES_FIRST): {library_first}")
@ -208,7 +212,7 @@ def start(attrs):
logger.critical(e) logger.critical(e)
else: else:
try: try:
stats = update_libraries(config) stats = run_config(config)
except Exception as e: except Exception as e:
config.notify(e) config.notify(e)
logger.stacktrace() logger.stacktrace()
@ -228,130 +232,12 @@ def start(attrs):
logger.separator(f"Finished {start_type}Run\n{version_line}\nFinished: {end_time.strftime('%H:%M:%S %Y-%m-%d')} Run Time: {run_time}") logger.separator(f"Finished {start_type}Run\n{version_line}\nFinished: {end_time.strftime('%H:%M:%S %Y-%m-%d')} Run Time: {run_time}")
logger.remove_main_handler() logger.remove_main_handler()
def update_libraries(config): def run_config(config):
library_status = {} library_status = run_libraries(config) if not playlist_only else {}
for library in config.libraries:
if library.skip_library:
logger.info("")
logger.separator(f"Skipping {library.name} Library")
continue
library_status[library.name] = {}
try:
logger.add_library_handler(library.mapping_name)
plexapi.server.TIMEOUT = library.timeout
logger.info("")
logger.separator(f"{library.name} Library")
logger.debug("")
logger.debug(f"Mapping Name: {library.original_mapping_name}")
logger.debug(f"Folder Name: {library.mapping_name}")
for ad in library.asset_directory:
logger.debug(f"Asset Directory: {ad}")
logger.debug(f"Asset Folders: {library.asset_folders}")
logger.debug(f"Create Asset Folders: {library.create_asset_folders}")
logger.debug(f"Download URL Assets: {library.download_url_assets}")
logger.debug(f"Sync Mode: {library.sync_mode}")
logger.debug(f"Minimum Items: {library.minimum_items}")
logger.debug(f"Delete Below Minimum: {library.delete_below_minimum}")
logger.debug(f"Delete Not Scheduled: {library.delete_not_scheduled}")
logger.debug(f"Default Collection Order: {library.default_collection_order}")
logger.debug(f"Missing Only Released: {library.missing_only_released}")
logger.debug(f"Only Filter Missing: {library.only_filter_missing}")
logger.debug(f"Show Unmanaged: {library.show_unmanaged}")
logger.debug(f"Show Filtered: {library.show_filtered}")
logger.debug(f"Show Missing: {library.show_missing}")
logger.debug(f"Show Missing Assets: {library.show_missing_assets}")
logger.debug(f"Save Report: {library.save_report}")
logger.debug(f"Report Path: {library.report_path}")
logger.debug(f"Clean Bundles: {library.clean_bundles}")
logger.debug(f"Empty Trash: {library.empty_trash}")
logger.debug(f"Optimize: {library.optimize}")
logger.debug(f"Timeout: {library.timeout}")
if config.delete_collections:
time_start = datetime.now()
logger.info("")
logger.separator(f"Deleting all Collections from the {library.name} Library", space=False, border=False)
logger.info("")
for collection in library.get_all_collections():
logger.info(f"Collection {collection.title} Deleted")
library.query(collection.delete)
library_status[library.name]["All Collections Deleted"] = str(datetime.now() - time_start).split('.')[0]
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:
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:
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]
if config.library_first and not config.test_mode and not collection_only:
if not overlays_only and library.library_operation:
library_status[library.name]["Library Operations"] = library.Operations.run_operations()
if not operations_only and (library.overlay_files or library.remove_overlays):
library_status[library.name]["Library Overlays"] = library.Overlays.run_overlays()
if not operations_only and not overlays_only:
time_start = datetime.now()
for metadata in library.metadata_files:
metadata_name = metadata.get_file_name()
if config.requested_metadata_files and metadata_name not in config.requested_metadata_files:
logger.info("")
logger.separator(f"Skipping {metadata_name} Metadata File")
continue
logger.info("")
logger.separator(f"Running {metadata_name} Metadata File\n{metadata.path}")
if not config.test_mode and not config.resume_from and not collection_only:
try:
metadata.update_metadata()
except Failed as e:
library.notify(e)
logger.error(e)
collections_to_run = metadata.get_collections(config.requested_collections)
if config.resume_from and config.resume_from not in collections_to_run:
logger.info("")
logger.warning(f"Collection: {config.resume_from} not in Metadata File: {metadata.path}")
continue
if collections_to_run:
logger.info("")
logger.separator(f"{'Test ' if config.test_mode else ''}Collections")
logger.remove_library_handler(library.mapping_name)
run_collection(config, library, metadata, collections_to_run)
logger.re_add_library_handler(library.mapping_name)
library_status[library.name]["Library Metadata Files"] = str(datetime.now() - time_start).split('.')[0]
if not config.library_first and not config.test_mode and not collection_only:
if not overlays_only and library.library_operation:
library_status[library.name]["Library Operations"] = library.Operations.run_operations()
if not operations_only and (library.overlay_files or library.remove_overlays):
library_status[library.name]["Library Overlays"] = library.Overlays.run_overlays()
logger.remove_library_handler(library.mapping_name)
except Exception as e:
library.notify(e)
logger.stacktrace()
logger.critical(e)
playlist_status = {} playlist_status = {}
playlist_stats = {} playlist_stats = {}
if config.playlist_files or config.general["playlist_report"]: if (config.playlist_files or config.general["playlist_report"]) and not overlays_only and not operations_only and not collection_only:
logger.add_playlists_handler() logger.add_playlists_handler()
if config.playlist_files: if config.playlist_files:
playlist_status, playlist_stats = run_playlists(config) playlist_status, playlist_stats = run_playlists(config)
@ -375,14 +261,15 @@ def update_libraries(config):
logger.info(f"{playlist_name:<{max_length}} | {'all' if len(users) == len(library.users) + 1 else ', '.join(users)}") logger.info(f"{playlist_name:<{max_length}} | {'all' if len(users) == len(library.users) + 1 else ', '.join(users)}")
logger.remove_playlists_handler() logger.remove_playlists_handler()
amount_added = 0
if not operations_only and not overlays_only and not playlist_only:
has_run_again = False has_run_again = False
for library in config.libraries: for library in config.libraries:
if library.run_again: if library.run_again:
has_run_again = True has_run_again = True
break break
amount_added = 0 if has_run_again:
if has_run_again and not operations_only and not overlays_only:
logger.info("") logger.info("")
logger.separator("Run Again") logger.separator("Run Again")
logger.info("") logger.info("")
@ -416,6 +303,7 @@ def update_libraries(config):
logger.stacktrace() logger.stacktrace()
logger.critical(e) logger.critical(e)
if not collection_only and not overlays_only and not playlist_only:
used_url = [] used_url = []
for library in config.libraries: for library in config.libraries:
if library.url not in used_url: if library.url not in used_url:
@ -490,6 +378,128 @@ def update_libraries(config):
stats["names"].extend([{"name": n, "library": "PLAYLIST"} for n in playlist_stats["names"]]) stats["names"].extend([{"name": n, "library": "PLAYLIST"} for n in playlist_stats["names"]])
return stats return stats
def run_libraries(config):
library_status = {}
for library in config.libraries:
if library.skip_library:
logger.info("")
logger.separator(f"Skipping {library.name} Library")
continue
library_status[library.name] = {}
try:
logger.add_library_handler(library.mapping_name)
plexapi.server.TIMEOUT = library.timeout
logger.info("")
logger.separator(f"{library.name} Library")
logger.debug("")
logger.debug(f"Mapping Name: {library.original_mapping_name}")
logger.debug(f"Folder Name: {library.mapping_name}")
for ad in library.asset_directory:
logger.debug(f"Asset Directory: {ad}")
logger.debug(f"Asset Folders: {library.asset_folders}")
logger.debug(f"Create Asset Folders: {library.create_asset_folders}")
logger.debug(f"Download URL Assets: {library.download_url_assets}")
logger.debug(f"Sync Mode: {library.sync_mode}")
logger.debug(f"Minimum Items: {library.minimum_items}")
logger.debug(f"Delete Below Minimum: {library.delete_below_minimum}")
logger.debug(f"Delete Not Scheduled: {library.delete_not_scheduled}")
logger.debug(f"Default Collection Order: {library.default_collection_order}")
logger.debug(f"Missing Only Released: {library.missing_only_released}")
logger.debug(f"Only Filter Missing: {library.only_filter_missing}")
logger.debug(f"Show Unmanaged: {library.show_unmanaged}")
logger.debug(f"Show Filtered: {library.show_filtered}")
logger.debug(f"Show Missing: {library.show_missing}")
logger.debug(f"Show Missing Assets: {library.show_missing_assets}")
logger.debug(f"Save Report: {library.save_report}")
logger.debug(f"Report Path: {library.report_path}")
logger.debug(f"Clean Bundles: {library.clean_bundles}")
logger.debug(f"Empty Trash: {library.empty_trash}")
logger.debug(f"Optimize: {library.optimize}")
logger.debug(f"Timeout: {library.timeout}")
if config.delete_collections:
time_start = datetime.now()
logger.info("")
logger.separator(f"Deleting all Collections from the {library.name} Library", space=False, border=False)
logger.info("")
for collection in library.get_all_collections():
logger.info(f"Collection {collection.title} Deleted")
library.query(collection.delete)
library_status[library.name]["All Collections Deleted"] = str(datetime.now() - time_start).split('.')[0]
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:
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:
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]
if config.library_first and not config.test_mode and not collection_only:
if not overlays_only and library.library_operation:
library_status[library.name]["Library Operations"] = library.Operations.run_operations()
if not operations_only and (library.overlay_files or library.remove_overlays):
library_status[library.name]["Library Overlays"] = library.Overlays.run_overlays()
if not operations_only and not overlays_only:
time_start = datetime.now()
for metadata in library.metadata_files:
metadata_name = metadata.get_file_name()
if config.requested_metadata_files and metadata_name not in config.requested_metadata_files:
logger.info("")
logger.separator(f"Skipping {metadata_name} Metadata File")
continue
logger.info("")
logger.separator(f"Running {metadata_name} Metadata File\n{metadata.path}")
if not config.test_mode and not config.resume_from and not collection_only:
try:
metadata.update_metadata()
except Failed as e:
library.notify(e)
logger.error(e)
collections_to_run = metadata.get_collections(config.requested_collections)
if config.resume_from and config.resume_from not in collections_to_run:
logger.info("")
logger.warning(f"Collection: {config.resume_from} not in Metadata File: {metadata.path}")
continue
if collections_to_run:
logger.info("")
logger.separator(f"{'Test ' if config.test_mode else ''}Collections")
logger.remove_library_handler(library.mapping_name)
run_collection(config, library, metadata, collections_to_run)
logger.re_add_library_handler(library.mapping_name)
library_status[library.name]["Library Metadata Files"] = str(datetime.now() - time_start).split('.')[0]
if not config.library_first and not config.test_mode and not collection_only:
if not overlays_only and library.library_operation:
library_status[library.name]["Library Operations"] = library.Operations.run_operations()
if not operations_only and (library.overlay_files or library.remove_overlays):
library_status[library.name]["Library Overlays"] = library.Overlays.run_overlays()
logger.remove_library_handler(library.mapping_name)
except Exception as e:
library.notify(e)
logger.stacktrace()
logger.critical(e)
return library_status
def run_collection(config, library, metadata, requested_collections): def run_collection(config, library, metadata, requested_collections):
logger.info("") logger.info("")
for mapping_name, collection_attrs in requested_collections.items(): for mapping_name, collection_attrs in requested_collections.items():

Loading…
Cancel
Save