plexutil 3.3.1__tar.gz → 3.4.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.3.1 → plexutil-3.4.0}/PKG-INFO +1 -1
  2. {plexutil-3.3.1 → plexutil-3.4.0}/pyproject.toml +1 -1
  3. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/__main__.py +37 -3
  4. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/auth.py +4 -8
  5. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/library.py +1 -0
  6. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/prompt.py +16 -6
  7. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/user_request.py +1 -0
  8. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/util/icons.py +3 -0
  9. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil.egg-info/PKG-INFO +1 -1
  10. {plexutil-3.3.1 → plexutil-3.4.0}/.github/workflows/python-publish.yml +0 -0
  11. {plexutil-3.3.1 → plexutil-3.4.0}/.gitignore +0 -0
  12. {plexutil-3.3.1 → plexutil-3.4.0}/LICENSE +0 -0
  13. {plexutil-3.3.1 → plexutil-3.4.0}/MANIFEST.in +0 -0
  14. {plexutil-3.3.1 → plexutil-3.4.0}/README.md +0 -0
  15. {plexutil-3.3.1 → plexutil-3.4.0}/git-hooks/commit-msg +0 -0
  16. {plexutil-3.3.1 → plexutil-3.4.0}/git-hooks/pre-commit +0 -0
  17. {plexutil-3.3.1 → plexutil-3.4.0}/init.sh +0 -0
  18. {plexutil-3.3.1 → plexutil-3.4.0}/requirements.txt +0 -0
  19. {plexutil-3.3.1 → plexutil-3.4.0}/ruff.toml +0 -0
  20. {plexutil-3.3.1 → plexutil-3.4.0}/setup.cfg +0 -0
  21. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/__init__.py +0 -0
  22. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/config/log_config.yaml +0 -0
  23. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/__init__.py +0 -0
  24. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/library_factory.py +0 -0
  25. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/movie_library.py +0 -0
  26. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/music_library.py +0 -0
  27. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/music_playlist.py +0 -0
  28. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/core/tv_library.py +0 -0
  29. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/__init__.py +0 -0
  30. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/bootstrap_paths_dto.py +0 -0
  31. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/dropdown_item_dto.py +0 -0
  32. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/library_setting_dto.py +0 -0
  33. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/movie_dto.py +0 -0
  34. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/music_playlist_dto.py +0 -0
  35. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/song_dto.py +0 -0
  36. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/tv_episode_dto.py +0 -0
  37. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/dto/tv_series_dto.py +0 -0
  38. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/__init__.py +0 -0
  39. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/agent.py +0 -0
  40. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/file_type.py +0 -0
  41. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/language.py +0 -0
  42. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/library_setting.py +0 -0
  43. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/library_type.py +0 -0
  44. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/scanner.py +0 -0
  45. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/enums/server_setting.py +0 -0
  46. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/__init__.py +0 -0
  47. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/auth_error.py +0 -0
  48. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/bootstrap_error.py +0 -0
  49. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/database_connection_error.py +0 -0
  50. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/device_error.py +0 -0
  51. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/entity_not_found_error.py +0 -0
  52. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/library_illegal_state_error.py +0 -0
  53. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/library_op_error.py +0 -0
  54. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/library_poll_timeout_error.py +0 -0
  55. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/library_section_missing_error.py +0 -0
  56. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/library_unsupported_error.py +0 -0
  57. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/plex_media_missing_error.py +0 -0
  58. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/server_connection_error.py +0 -0
  59. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/unexpected_argument_error.py +0 -0
  60. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/unexpected_naming_pattern_error.py +0 -0
  61. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/exception/user_error.py +0 -0
  62. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/graphical/__init__.py +0 -0
  63. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/graphical/selection_window.py +0 -0
  64. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/mapper/__init__.py +0 -0
  65. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/mapper/music_playlist_mapper.py +0 -0
  66. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/mapper/song_mapper.py +0 -0
  67. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/model/__init__.py +0 -0
  68. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/model/music_playlist_entity.py +0 -0
  69. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/model/song_entity.py +0 -0
  70. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/model/song_music_playlist_entity.py +0 -0
  71. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/plex_util_logger.py +0 -0
  72. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/service/__init__.py +0 -0
  73. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/service/db_manager.py +0 -0
  74. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/service/music_playlist_service.py +0 -0
  75. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/service/song_music_playlist_composite_service.py +0 -0
  76. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/service/song_service.py +0 -0
  77. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/static.py +0 -0
  78. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/util/__init__.py +0 -0
  79. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/util/file_importer.py +0 -0
  80. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/util/plex_ops.py +0 -0
  81. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil/util/query_builder.py +0 -0
  82. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil.egg-info/SOURCES.txt +0 -0
  83. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil.egg-info/dependency_links.txt +0 -0
  84. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil.egg-info/entry_points.txt +0 -0
  85. {plexutil-3.3.1 → plexutil-3.4.0}/src/plexutil.egg-info/requires.txt +0 -0
  86. {plexutil-3.3.1 → plexutil-3.4.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.3.1
3
+ Version: 3.4.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.3.1"
7
+ version = "3.4.0"
8
8
  requires-python = ">=3.11"
9
9
  authors = [
10
10
  {name = "Carlos Florez", email = "carlos@florez.co.uk"}
@@ -38,7 +38,7 @@ def main() -> None:
38
38
  bootstrap_paths_dto = FileImporter.bootstrap()
39
39
  user_request = Prompt.confirm_user_request()
40
40
  try:
41
- plex_resources = Auth.get_resources(bootstrap_paths_dto)
41
+ plex_account = Auth.get_account(bootstrap_paths_dto)
42
42
  except Unauthorized:
43
43
  description = f"{Icons.WARNING} Reauthentication required\n"
44
44
  PlexUtilLogger.get_logger().warning(description)
@@ -46,12 +46,46 @@ def main() -> None:
46
46
  bootstrap_paths_dto.private_key_dir.unlink(missing_ok=True)
47
47
  bootstrap_paths_dto.public_key_dir.unlink(missing_ok=True)
48
48
  bootstrap_paths_dto.token_dir.unlink(missing_ok=True)
49
- plex_resources = Auth.get_resources(bootstrap_paths_dto)
49
+ plex_account = Auth.get_account(bootstrap_paths_dto)
50
50
 
51
- plex_server = Prompt.confirm_server(plex_resources=plex_resources)
51
+ plex_server = Prompt.confirm_server(plex_account=plex_account)
52
+
53
+ release = plex_server.checkForUpdate()
54
+ current_version = plex_server.version
55
+
56
+ if release and user_request is not UserRequest.CHANGELOG:
57
+ release_version = release.version
58
+ description = (
59
+ f"{Icons.WARNING} A Server update is available: "
60
+ f"{release_version} (Current: {current_version})\n"
61
+ f"plexutil changelog (to display changes)"
62
+ )
63
+ PlexUtilLogger.get_logger().warning(description)
52
64
 
53
65
  if user_request is UserRequest.SETTINGS:
54
66
  PlexOps.set_server_settings(plex_server=plex_server)
67
+ elif user_request is UserRequest.CHANGELOG:
68
+ if release:
69
+ release_version = release.version
70
+ added = release.added
71
+ fixed = release.fixed
72
+
73
+ description = (
74
+ f"{Icons.BANNER_LEFT}{release_version}{Icons.BANNER_RIGHT}\n"
75
+ f"{Icons.BANNER_LEFT}\nADDED\n{Icons.BANNER_LEFT}\n"
76
+ f"{added}\n"
77
+ f"{Icons.BANNER_LEFT}\nFIXED\n{Icons.BANNER_LEFT}\n"
78
+ f"{fixed}"
79
+ )
80
+
81
+ else:
82
+ description = (
83
+ f"{Icons.SUCCESS} Server is up-to-date ({current_version})"
84
+ )
85
+
86
+ PlexUtilLogger.get_logger().info(description)
87
+ sys.exit(0)
88
+
55
89
  else:
56
90
  library = LibraryFactory.get(
57
91
  user_request=user_request,
@@ -1,7 +1,7 @@
1
1
  import uuid
2
2
  from importlib.metadata import PackageNotFoundError, version
3
3
 
4
- from plexapi.myplex import MyPlexAccount, MyPlexJWTLogin, MyPlexResource
4
+ from plexapi.myplex import MyPlexAccount, MyPlexJWTLogin
5
5
 
6
6
  from plexutil.dto.bootstrap_paths_dto import BootstrapPathsDTO
7
7
  from plexutil.exception.auth_error import AuthError
@@ -13,9 +13,9 @@ from plexutil.util.icons import Icons
13
13
 
14
14
  class Auth(Static):
15
15
  @staticmethod
16
- def get_resources(
16
+ def get_account(
17
17
  bootstrap_paths_dto: BootstrapPathsDTO,
18
- ) -> list[MyPlexResource]:
18
+ ) -> MyPlexAccount:
19
19
  """
20
20
  Login to Plex and returns a list of all the available Plex Resources
21
21
  (Servers and Clients)
@@ -82,8 +82,4 @@ class Auth(Static):
82
82
  description = "Auth exists"
83
83
  PlexUtilLogger.get_logger().debug(description)
84
84
  token, _ = FileImporter.get_jwt(bootstrap_paths_dto.token_dir)
85
- account = MyPlexAccount(token=token)
86
- resources = account.resources()
87
- description = f"Available Resources: {resources}"
88
- PlexUtilLogger.get_logger().debug(description)
89
- return resources
85
+ return MyPlexAccount(token=token)
@@ -146,6 +146,7 @@ class Library(ABC):
146
146
  for x in self.plex_server.activities
147
147
  )
148
148
  if not is_updating:
149
+ spinner.text = ""
149
150
  spinner.ok(f"{Icons.SUCCESS} Updated")
150
151
 
151
152
  elapsed = time.time() - start
@@ -23,7 +23,7 @@ if TYPE_CHECKING:
23
23
  MusicSection,
24
24
  ShowSection,
25
25
  )
26
- from plexapi.myplex import MyPlexResource
26
+ from plexapi.myplex import MyPlexAccount
27
27
  from plexapi.server import PlexServer
28
28
  from plexapi.video import Movie, Show
29
29
 
@@ -388,7 +388,7 @@ class Prompt(Static):
388
388
  ).value
389
389
 
390
390
  @staticmethod
391
- def confirm_server(plex_resources: list[MyPlexResource]) -> PlexServer:
391
+ def confirm_server(plex_account: MyPlexAccount) -> PlexServer:
392
392
  """
393
393
  Prompts user for a Plex Media Server selection
394
394
 
@@ -403,10 +403,17 @@ class Prompt(Static):
403
403
  """
404
404
  is_default = True
405
405
  dropdown = []
406
+ plex_resources = plex_account.resources()
407
+ description = f"Available Resources: {plex_resources}"
408
+ PlexUtilLogger.get_logger().debug(description)
409
+
410
+ pass_icon = f"{Icons.PASS} " if plex_account.subscriptionActive else ""
411
+
406
412
  for resource in plex_resources:
413
+ display_name = f"{pass_icon}{resource.name} ({resource.device})"
407
414
  if resource.product == "Plex Media Server":
408
415
  item = DropdownItemDTO(
409
- display_name=f"{resource.name} ({resource.device})",
416
+ display_name=display_name,
410
417
  value=resource,
411
418
  is_default=is_default,
412
419
  )
@@ -423,6 +430,7 @@ class Prompt(Static):
423
430
  try:
424
431
  plex_server = plex_resource.connect()
425
432
  except NotFound as e:
433
+ spinner.text = ""
426
434
  spinner.fail(f"{Icons.FAILURE} Connection Failure")
427
435
  description = (
428
436
  f"Failed to connect to: {plex_resource.name} "
@@ -431,6 +439,7 @@ class Prompt(Static):
431
439
  )
432
440
  raise ServerConnectionError(description) from e
433
441
  except Exception as e:
442
+ spinner.text = ""
434
443
  spinner.fail(f"{Icons.FAILURE} Connection Failure")
435
444
  description = (
436
445
  f"Failed to connect to: {plex_resource.name} "
@@ -439,6 +448,7 @@ class Prompt(Static):
439
448
  )
440
449
  raise ServerConnectionError(description) from e
441
450
 
451
+ spinner.text = ""
442
452
  spinner.ok(f"{Icons.SUCCESS} Connected")
443
453
 
444
454
  description = f"Connected to: {plex_server}"
@@ -560,11 +570,11 @@ class Prompt(Static):
560
570
 
561
571
  space = " " * offset
562
572
  if dropdown_count < max_single_space:
563
- number_format = f"[ {dropdown_count}] "
573
+ number_format = f"[ {dropdown_count}]"
564
574
  elif dropdown_count < max_double_space:
565
- number_format = f"[ {dropdown_count}] "
575
+ number_format = f"[ {dropdown_count}]"
566
576
  else:
567
- number_format = f"[{dropdown_count}] "
577
+ number_format = f"[{dropdown_count}]"
568
578
 
569
579
  if item.is_default and expect_input:
570
580
  display_name = f"{item.display_name} {Icons.STAR}"
@@ -12,6 +12,7 @@ class UserRequest(Enum):
12
12
  UPLOAD = "upload"
13
13
  DISPLAY = "display"
14
14
  UPDATE = "update"
15
+ CHANGELOG = "changelog"
15
16
 
16
17
  @staticmethod
17
18
  # Forward Reference used here in type hint
@@ -27,3 +27,6 @@ class Icons(Static):
27
27
  "🟢" if sys.stdout.encoding.lower().startswith("utf") else "SUCCESS"
28
28
  )
29
29
  FAILURE = "🔴" if sys.stdout.encoding.lower().startswith("utf") else "FAIL"
30
+ PASS = (
31
+ "🎟" if sys.stdout.encoding.lower().startswith("utf") else "*PlexPass*"
32
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plexutil
3
- Version: 3.3.1
3
+ Version: 3.4.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