[72] add `delete_playlist` and `playlist_report`

pull/858/head
meisnate12 3 years ago
parent 261eb2ce16
commit 97b7fd262b

@ -1 +1 @@
1.16.5-develop71 1.16.5-develop72

@ -51,6 +51,7 @@ The available setting attributes which can be set at each level are outlined bel
| [`ignore_imdb_ids`](#ignore-imdb-ids) | ✅ | ✅ | ✅ | | [`ignore_imdb_ids`](#ignore-imdb-ids) | ✅ | ✅ | ✅ |
| [`item_refresh_delay`](#item-refresh-delay) | ✅ | ✅ | ✅ | | [`item_refresh_delay`](#item-refresh-delay) | ✅ | ✅ | ✅ |
| [`playlist_sync_to_users`](#playlist-sync-to-users) | ✅ | ❌ | ✅ | | [`playlist_sync_to_users`](#playlist-sync-to-users) | ✅ | ❌ | ✅ |
| [`playlist_report`](#playlist-report) | ✅ | ❌ | ❌ |
| [`custom_repo`](#custom-repo) | ✅ | ❌ | ❌ | | [`custom_repo`](#custom-repo) | ✅ | ❌ | ❌ |
| [`verify_ssl`](#verify-ssl) | ✅ | ❌ | ❌ | | [`verify_ssl`](#verify-ssl) | ✅ | ❌ | ❌ |
| [`check_nightly`](#check-nightly) | ✅ | ❌ | ❌ | | [`check_nightly`](#check-nightly) | ✅ | ❌ | ❌ |
@ -513,9 +514,24 @@ Set the default playlist `sync_to_users`. To Sync a playlist to only yourself le
</tr> </tr>
</table> </table>
# Playlist Report
Set `playlist_report` to true to print out a playlist report at the end of the log.
<table class="dualTable colwidths-auto align-default table">
<tr>
<th>Default Value</th>
<td><code>false</code></td>
</tr>
<tr>
<th>Allowed Values</th>
<td><code>true</code> or <code>false</code>
</tr>
</table>
## Custom Repo ## Custom Repo
Specify where the `repo` attribute's base is when defining `metadata_paths` and `playlist_files`. Specify where the `repo` attribute's base is when defining `metadata_paths` and `playlist_files`.
* Ensure you are using the raw GitHub link (i.e. https://github.com/meisnate12/Plex-Meta-Manager-Configs/tree/master/meisnate12 ) * Ensure you are using the raw GitHub link (i.e. https://github.com/meisnate12/Plex-Meta-Manager-Configs/tree/master/meisnate12 )
<table class="dualTable colwidths-auto align-default table"> <table class="dualTable colwidths-auto align-default table">
<tr> <tr>
<th>Default Value</th> <th>Default Value</th>

@ -34,9 +34,15 @@ playlists:
# ... builder, details, and filters for this playlist # ... builder, details, and filters for this playlist
``` ```
Playlists require the `libraries` attribute, which instructs the operation to look in the specified libraries. This allows media to be combined from multiple libraries into one playlist. The mappings that you define in the `libraries` attribute must match the library names in your [Configuration File](../config/configuration). ### Special Playlist Attributes
The playlist can also use the `sync_to_users` attributes to control who has visibility of the playlist. This will override the global [`playlist_sync_to_users` Setting](../config/settings.md#playlist-sync-to-users). `sync_to_users` can be set to `all` to sync to all users who have access to the Plex Media Server, or a list/comma-separated string of users. The Plex Media Server owner will always have visibility of the Playlists, so does not need to be defined within the attribute. Leaving `sync_to_users` empty will make the playlist visible to the Plex Media Server owner only. | Attribute | Description | Required |
|:------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:|
| `libraries` | Determine which libraries the playlist will be built from.<br>**Options:** Comma-separated string or list of library mapping names defined in the `libraries` attribute. | &#9989; |
| `sync_to_users` | Determine which Users have the playlist synced.<br>This will override the global [`playlist_sync_to_users` Setting](../config/settings.md#playlist-sync-to-users).<br>**Options:** Comma-separated string or list of users, `all` for every user who has server access, or leave blank for just the server owner. | &#10060; |
| `delete_playlist` | Will delete this playlist for the users defined by sync_to_users.<br>**Options:** `true` or `false` | &#10060; |
* Any defined playlist will be always be visible by The Plex Media Server owner, so it doesn't need to be defined within `sync_to_users`.
There are three types of attributes that can be utilized within a playlist: There are three types of attributes that can be utilized within a playlist:

@ -312,7 +312,41 @@ class CollectionBuilder:
else: else:
logger.error(f"{self.Type} Error: suppress_overlays attribute is blank") logger.error(f"{self.Type} Error: suppress_overlays attribute is blank")
self.sync_to_users = None
self.valid_users = []
if self.playlist: if self.playlist:
self.sync_to_users = config.general["playlist_sync_to_users"]
if "sync_to_users" in methods or "sync_to_user" in methods:
s_attr = f"sync_to_user{'s' if 'sync_to_users' in methods else ''}"
logger.debug("")
logger.debug(f"Validating Method: {s_attr}")
logger.debug(f"Value: {self.data[methods[s_attr]]}")
if self.data[methods[s_attr]]:
self.sync_to_users = self.data[methods[s_attr]]
else:
logger.warning(f"Playlist Error: sync_to_users attribute is blank defaulting to playlist_sync_to_users: {self.sync_to_users}")
else:
logger.warning(f"Playlist Error: sync_to_users attribute not found defaulting to playlist_sync_to_users: {self.sync_to_users}")
plex_users = self.library.users
if self.sync_to_users:
if str(self.sync_to_users) == "all":
self.valid_users = plex_users
else:
for user in util.get_list(self.sync_to_users):
if user in plex_users:
self.valid_users.append(user)
else:
raise Failed(f"Playlist Error: User: {user} not found in plex\nOptions: {plex_users}")
if "delete_playlist" in methods:
logger.debug("")
logger.debug("Validating Method: delete_not_scheduled")
logger.debug(f"Value: {data[methods['delete_not_scheduled']]}")
if util.parse(self.Type, "delete_not_scheduled", self.data, datatype="bool", methods=methods, default=False):
self.obj = self.library.get_playlist(self.name)
logger.info(self.delete())
if "libraries" in methods: if "libraries" in methods:
logger.debug("") logger.debug("")
logger.debug("Validating Method: libraries") logger.debug("Validating Method: libraries")
@ -389,8 +423,6 @@ class CollectionBuilder:
self.exists = False self.exists = False
self.created = False self.created = False
self.deleted = False self.deleted = False
self.sync_to_users = None
self.valid_users = []
if self.playlist: if self.playlist:
server_check = None server_check = None
@ -401,30 +433,6 @@ class CollectionBuilder:
else: else:
server_check = pl_library.PlexServer.machineIdentifier server_check = pl_library.PlexServer.machineIdentifier
self.sync_to_users = config.general["playlist_sync_to_users"]
if "sync_to_users" in methods or "sync_to_user" in methods:
s_attr = f"sync_to_user{'s' if 'sync_to_users' in methods else ''}"
logger.debug("")
logger.debug(f"Validating Method: {s_attr}")
logger.debug(f"Value: {self.data[methods[s_attr]]}")
if self.data[methods[s_attr]]:
self.sync_to_users = self.data[methods[s_attr]]
else:
logger.warning(f"Playlist Error: sync_to_users attribute is blank defaulting to playlist_sync_to_users: {self.sync_to_users}")
else:
logger.warning(f"Playlist Error: sync_to_users attribute not found defaulting to playlist_sync_to_users: {self.sync_to_users}")
plex_users = self.library.users
if self.sync_to_users:
if str(self.sync_to_users) == "all":
self.valid_users = plex_users
else:
for user in util.get_list(self.sync_to_users):
if user in plex_users:
self.valid_users.append(user)
else:
raise Failed(f"Playlist Error: User: {user} not found in plex\nOptions: {plex_users}")
if "delete_not_scheduled" in methods and not self.overlay: if "delete_not_scheduled" in methods and not self.overlay:
logger.debug("") logger.debug("")
logger.debug("Validating Method: delete_not_scheduled") logger.debug("Validating Method: delete_not_scheduled")
@ -2679,11 +2687,6 @@ class CollectionBuilder:
self.library.moveItem(self.obj, item, previous) self.library.moveItem(self.obj, item, previous)
previous = item previous = item
def delete_user_playlist(self, title, user):
user_server = self.library.PlexServer.switchUser(user)
user_playlist = user_server.playlist(title)
user_playlist.delete()
def delete(self): def delete(self):
output = "" output = ""
if self.obj: if self.obj:
@ -2693,7 +2696,7 @@ class CollectionBuilder:
if self.valid_users: if self.valid_users:
for user in self.valid_users: for user in self.valid_users:
try: try:
self.delete_user_playlist(self.obj.title, user) self.library.delete_user_playlist(self.obj.title, user)
output += f"\nPlaylist {self.obj.title} deleted on User {user}" output += f"\nPlaylist {self.obj.title} deleted on User {user}"
except NotFound: except NotFound:
output += f"\nPlaylist {self.obj.title} not found on User {user}" output += f"\nPlaylist {self.obj.title} not found on User {user}"
@ -2706,7 +2709,7 @@ class CollectionBuilder:
logger.info("") logger.info("")
for user in self.valid_users: for user in self.valid_users:
try: try:
self.delete_user_playlist(self.obj.title, user) self.library.delete_user_playlist(self.obj.title, user)
except NotFound: except NotFound:
pass pass
self.obj.copyToUser(user) self.obj.copyToUser(user)

@ -318,6 +318,7 @@ class ConfigFile:
"ignore_ids": check_for_attribute(self.data, "ignore_ids", parent="settings", var_type="int_list", default_is_none=True), "ignore_ids": check_for_attribute(self.data, "ignore_ids", parent="settings", var_type="int_list", default_is_none=True),
"ignore_imdb_ids": check_for_attribute(self.data, "ignore_imdb_ids", parent="settings", var_type="list", default_is_none=True), "ignore_imdb_ids": check_for_attribute(self.data, "ignore_imdb_ids", parent="settings", var_type="list", default_is_none=True),
"playlist_sync_to_users": check_for_attribute(self.data, "playlist_sync_to_users", parent="settings", default="all", default_is_none=True), "playlist_sync_to_users": check_for_attribute(self.data, "playlist_sync_to_users", parent="settings", default="all", default_is_none=True),
"playlist_report": check_for_attribute(self.data, "playlist_report", parent="settings", var_type="bool", default=True),
"verify_ssl": check_for_attribute(self.data, "verify_ssl", parent="settings", var_type="bool", default=True), "verify_ssl": check_for_attribute(self.data, "verify_ssl", parent="settings", var_type="bool", default=True),
"custom_repo": check_for_attribute(self.data, "custom_repo", parent="settings", default_is_none=True), "custom_repo": check_for_attribute(self.data, "custom_repo", parent="settings", default_is_none=True),
"check_nightly": check_for_attribute(self.data, "check_nightly", parent="settings", var_type="bool", default=False), "check_nightly": check_for_attribute(self.data, "check_nightly", parent="settings", var_type="bool", default=False),

@ -450,10 +450,9 @@ class Plex(Library):
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
def exact_search(self, title, libtype=None, year=None): def exact_search(self, title, libtype=None, year=None):
terms = {"title=": title}
if year: if year:
terms = {"title=": title, "year": year} terms["year"] = year
else:
terms = {"title=": title}
return self.Plex.search(libtype=libtype, **terms) return self.Plex.search(libtype=libtype, **terms)
@retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex) @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex)
@ -622,6 +621,21 @@ class Plex(Library):
self._users = users self._users = users
return self._users return self._users
def delete_user_playlist(self, title, user):
self.PlexServer.switchUser(user).playlist(title).delete()
def playlist_report(self):
playlists = {}
def scan_user(server, username):
for playlist in server.playlists():
if playlist.title not in playlists:
playlists[playlist.title] = []
playlists[playlist.title].append(username)
scan_user(self.PlexServer, self.PlexServer.myPlexAccount().title)
for user in self.users:
scan_user(self.PlexServer.switchUser(user), user)
return playlists
def manage_recommendations(self): def manage_recommendations(self):
return [(r.title, r._data.attrib.get('identifier'), r._data.attrib.get('promotedToRecommended'), return [(r.title, r._data.attrib.get('identifier'), r._data.attrib.get('promotedToRecommended'),
r._data.attrib.get('promotedToOwnHome'), r._data.attrib.get('promotedToSharedHome')) r._data.attrib.get('promotedToOwnHome'), r._data.attrib.get('promotedToSharedHome'))

@ -333,9 +333,28 @@ def update_libraries(config):
playlist_status = {} playlist_status = {}
playlist_stats = {} playlist_stats = {}
if config.playlist_files: if config.playlist_files or config.general["playlist_report"]:
logger.add_playlists_handler() logger.add_playlists_handler()
playlist_status, playlist_stats = run_playlists(config) if config.playlist_files:
playlist_status, playlist_stats = run_playlists(config)
if config.general["playlist_report"]:
ran = []
for library in config.libraries:
if library.PlexServer.machineIdentifier in ran:
continue
ran.append(library.PlexServer.machineIdentifier)
logger.info("")
logger.separator(f"{library.PlexServer.friendlyName} Playlist Report")
logger.info("")
report = library.playlist_report()
max_length = 0
for playlist_name in report:
if len(playlist_name) > max_length:
max_length = len(playlist_name)
logger.info(f"{'Playlist Title':<{max_length}} | Users")
logger.separator(f"{logger.separating_character * max_length}|", space=False, border=False, side_space=False, left=True)
for playlist_name, users in report.items():
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()
has_run_again = False has_run_again = False
@ -411,13 +430,14 @@ def update_libraries(config):
logger.info(error) logger.info(error)
logger.info("") logger.info("")
logger.info("")
logger.separator("Summary") logger.separator("Summary")
for library in config.libraries: for library in config.libraries:
logger.info("") logger.info("")
logger.separator(f"{library.name} Summary", space=False, border=False) logger.separator(f"{library.name} Summary", space=False, border=False)
logger.info("") logger.info("")
logger.info(f"{'Title':<27} | Run Time |") logger.info(f"{'Title':<27} | Run Time |")
logger.separator(f"{logger.separating_character * 27}|{logger.separating_character * 10}|", space=False, border=False, side_space=False, left=True) logger.info(f"{logger.separating_character * 27} | {logger.separating_character * 8} |")
for text, value in library_status[library.name].items(): for text, value in library_status[library.name].items():
logger.info(f"{text:<27} | {value:>8} |") logger.info(f"{text:<27} | {value:>8} |")
logger.info("") logger.info("")

Loading…
Cancel
Save