plexutil 3.2.2__tar.gz → 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. {plexutil-3.2.2 → plexutil-3.3.0}/PKG-INFO +1 -1
  2. {plexutil-3.2.2 → plexutil-3.3.0}/pyproject.toml +1 -1
  3. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/__main__.py +10 -0
  4. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/library.py +11 -0
  5. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/music_playlist.py +42 -5
  6. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil.egg-info/PKG-INFO +1 -1
  7. {plexutil-3.2.2 → plexutil-3.3.0}/.github/workflows/python-publish.yml +0 -0
  8. {plexutil-3.2.2 → plexutil-3.3.0}/.gitignore +0 -0
  9. {plexutil-3.2.2 → plexutil-3.3.0}/LICENSE +0 -0
  10. {plexutil-3.2.2 → plexutil-3.3.0}/MANIFEST.in +0 -0
  11. {plexutil-3.2.2 → plexutil-3.3.0}/README.md +0 -0
  12. {plexutil-3.2.2 → plexutil-3.3.0}/git-hooks/commit-msg +0 -0
  13. {plexutil-3.2.2 → plexutil-3.3.0}/git-hooks/pre-commit +0 -0
  14. {plexutil-3.2.2 → plexutil-3.3.0}/init.sh +0 -0
  15. {plexutil-3.2.2 → plexutil-3.3.0}/requirements.txt +0 -0
  16. {plexutil-3.2.2 → plexutil-3.3.0}/ruff.toml +0 -0
  17. {plexutil-3.2.2 → plexutil-3.3.0}/setup.cfg +0 -0
  18. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/__init__.py +0 -0
  19. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/config/log_config.yaml +0 -0
  20. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/__init__.py +0 -0
  21. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/auth.py +0 -0
  22. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/library_factory.py +0 -0
  23. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/movie_library.py +0 -0
  24. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/music_library.py +0 -0
  25. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/prompt.py +0 -0
  26. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/core/tv_library.py +0 -0
  27. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/__init__.py +0 -0
  28. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/bootstrap_paths_dto.py +0 -0
  29. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/dropdown_item_dto.py +0 -0
  30. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/library_setting_dto.py +0 -0
  31. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/movie_dto.py +0 -0
  32. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/music_playlist_dto.py +0 -0
  33. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/song_dto.py +0 -0
  34. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/tv_episode_dto.py +0 -0
  35. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/dto/tv_series_dto.py +0 -0
  36. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/__init__.py +0 -0
  37. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/agent.py +0 -0
  38. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/file_type.py +0 -0
  39. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/language.py +0 -0
  40. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/library_setting.py +0 -0
  41. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/library_type.py +0 -0
  42. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/scanner.py +0 -0
  43. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/server_setting.py +0 -0
  44. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/enums/user_request.py +0 -0
  45. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/__init__.py +0 -0
  46. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/auth_error.py +0 -0
  47. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/bootstrap_error.py +0 -0
  48. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/database_connection_error.py +0 -0
  49. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/device_error.py +0 -0
  50. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/entity_not_found_error.py +0 -0
  51. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/library_illegal_state_error.py +0 -0
  52. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/library_op_error.py +0 -0
  53. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/library_poll_timeout_error.py +0 -0
  54. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/library_section_missing_error.py +0 -0
  55. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/library_unsupported_error.py +0 -0
  56. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/plex_media_missing_error.py +0 -0
  57. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/server_connection_error.py +0 -0
  58. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/unexpected_argument_error.py +0 -0
  59. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/unexpected_naming_pattern_error.py +0 -0
  60. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/exception/user_error.py +0 -0
  61. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/graphical/__init__.py +0 -0
  62. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/graphical/selection_window.py +0 -0
  63. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/mapper/__init__.py +0 -0
  64. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/mapper/music_playlist_mapper.py +0 -0
  65. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/mapper/song_mapper.py +0 -0
  66. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/model/__init__.py +0 -0
  67. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/model/music_playlist_entity.py +0 -0
  68. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/model/song_entity.py +0 -0
  69. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/model/song_music_playlist_entity.py +0 -0
  70. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/plex_util_logger.py +0 -0
  71. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/service/__init__.py +0 -0
  72. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/service/db_manager.py +0 -0
  73. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/service/music_playlist_service.py +0 -0
  74. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/service/song_music_playlist_composite_service.py +0 -0
  75. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/service/song_service.py +0 -0
  76. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/static.py +0 -0
  77. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/util/__init__.py +0 -0
  78. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/util/file_importer.py +0 -0
  79. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/util/icons.py +0 -0
  80. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/util/plex_ops.py +0 -0
  81. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil/util/query_builder.py +0 -0
  82. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil.egg-info/SOURCES.txt +0 -0
  83. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil.egg-info/dependency_links.txt +0 -0
  84. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil.egg-info/entry_points.txt +0 -0
  85. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil.egg-info/requires.txt +0 -0
  86. {plexutil-3.2.2 → plexutil-3.3.0}/src/plexutil.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexutil
3
- Version: 3.2.2
3
+ Version: 3.3.0
4
4
  Author-email: Carlos Florez <carlos@florez.co.uk>
5
5
  Maintainer-email: Carlos Florez <carlos@florez.co.uk>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "plexutil"
7
- version = "3.2.2"
7
+ version = "3.3.0"
8
8
  requires-python = ">=3.11"
9
9
  authors = [
10
10
  {name = "Carlos Florez", email = "carlos@florez.co.uk"}
@@ -7,6 +7,7 @@ from plexutil.core.auth import Auth
7
7
  from plexutil.core.library_factory import LibraryFactory
8
8
  from plexutil.core.prompt import Prompt
9
9
  from plexutil.enums.user_request import UserRequest
10
+ from plexutil.exception.auth_error import AuthError
10
11
  from plexutil.exception.bootstrap_error import BootstrapError
11
12
  from plexutil.exception.library_illegal_state_error import (
12
13
  LibraryIllegalStateError,
@@ -71,6 +72,15 @@ def main() -> None:
71
72
  PlexUtilLogger.get_logger().exception(description)
72
73
  raise
73
74
 
75
+ except AuthError as e:
76
+ sys.tracebacklimit = 0
77
+ description = (
78
+ f"\n{Icons.BANNER_LEFT}Authentication "
79
+ f"Error{Icons.BANNER_RIGHT}\n{e!s}"
80
+ )
81
+ PlexUtilLogger.get_logger().error(description)
82
+ sys.exit(1)
83
+
74
84
  except UserError as e:
75
85
  sys.tracebacklimit = 0
76
86
  description = (
@@ -118,6 +118,12 @@ class Library(ABC):
118
118
 
119
119
  @abstractmethod
120
120
  def update(self) -> None:
121
+ """
122
+ Updates and Refreshes a Library, including each of its media items
123
+
124
+ Returns:
125
+ None: This method does not return a value
126
+ """
121
127
  start = time.time()
122
128
  section = self.get_section()
123
129
  section.update()
@@ -151,7 +157,12 @@ class Library(ABC):
151
157
 
152
158
  @abstractmethod
153
159
  def modify(self, is_modify_media: bool = False) -> None:
160
+ """
161
+ Modifies a Library
154
162
 
163
+ Returns:
164
+ None: This method does not return a value
165
+ """
155
166
  if is_modify_media:
156
167
  selected_media = self.display_media(expect_input=True)
157
168
 
@@ -47,6 +47,7 @@ class MusicPlaylist(Library):
47
47
  ) -> None:
48
48
  super().__init__(
49
49
  supported_requests=[
50
+ UserRequest.CREATE,
50
51
  UserRequest.DELETE,
51
52
  UserRequest.DISPLAY,
52
53
  UserRequest.UPLOAD,
@@ -85,8 +86,14 @@ class MusicPlaylist(Library):
85
86
  f"{Icons.WARNING} Song not found: {song!s} | Skipping..."
86
87
  )
87
88
  PlexUtilLogger.get_logger().warning(description)
88
- playlist = self.get_section().playlist(self.playlist_name)
89
- playlist.addItems(tracks)
89
+
90
+ if self.user_request is UserRequest.CREATE:
91
+ self.get_section().createPlaylist(
92
+ title=self.playlist_name, items=tracks
93
+ )
94
+ else:
95
+ playlist = self.get_section().playlist(self.playlist_name)
96
+ playlist.addItems(tracks)
90
97
 
91
98
  def remove_item(self) -> None:
92
99
  playlist = self.get_section().playlist(self.playlist_name)
@@ -117,11 +124,40 @@ class MusicPlaylist(Library):
117
124
  playlist.removeItems(tracks)
118
125
 
119
126
  def create(self) -> None:
120
- raise NotImplementedError
127
+
128
+ op_type = "Create Playlist"
129
+
130
+ self.display(expect_input=True)
131
+
132
+ text = Prompt.confirm_text(
133
+ "Playlist Name",
134
+ "Playlist requires a name",
135
+ "What should the playlist name be",
136
+ )
137
+ if not text:
138
+ raise LibraryOpError(
139
+ op_type=op_type,
140
+ library_type=self.library_type,
141
+ description="No name supplied?",
142
+ )
143
+ self.playlist_name = text[0]
144
+ exists = any(
145
+ x.name == self.playlist_name for x in self.__get_all_playlists()
146
+ )
147
+ if exists:
148
+ description = (
149
+ f"{self.playlist_name} already exists in Library ({self.name})"
150
+ )
151
+ raise LibraryOpError(
152
+ op_type=op_type,
153
+ library_type=self.library_type,
154
+ description=description,
155
+ )
156
+
157
+ self.add_item()
121
158
 
122
159
  def update(self) -> None:
123
- # None of MusicPlaylist operations benefit from a refresh/reload
124
- return
160
+ raise NotImplementedError
125
161
 
126
162
  def display_media(self, expect_input: bool = False) -> Track:
127
163
  raise NotImplementedError
@@ -158,6 +194,7 @@ class MusicPlaylist(Library):
158
194
  if (
159
195
  self.user_request is UserRequest.DOWNLOAD
160
196
  or self.user_request is UserRequest.UPLOAD
197
+ or self.user_request is UserRequest.CREATE
161
198
  ):
162
199
  return
163
200
  dropdown = []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexutil
3
- Version: 3.2.2
3
+ Version: 3.3.0
4
4
  Author-email: Carlos Florez <carlos@florez.co.uk>
5
5
  Maintainer-email: Carlos Florez <carlos@florez.co.uk>
6
6
  License: MIT License
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes