cuemsutils 0.1.0rc15__tar.gz → 0.1.0rc16__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 (46) hide show
  1. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/PKG-INFO +1 -1
  2. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/__init__.py +1 -1
  3. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/CueList.py +22 -3
  4. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/CuemsScript.py +18 -5
  5. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/MediaCue.py +25 -0
  6. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/.gitignore +0 -0
  7. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/LICENSE +0 -0
  8. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/README.md +0 -0
  9. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/create_script.py +0 -0
  10. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/ActionCue.py +0 -0
  11. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/AudioCue.py +0 -0
  12. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/Cue.py +0 -0
  13. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/CueOutput.py +0 -0
  14. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/DmxCue.py +0 -0
  15. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/FadeCue.py +0 -0
  16. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/FadeProfile.py +0 -0
  17. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/VideoCue.py +0 -0
  18. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/cues/__init__.py +0 -0
  19. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/helpers.py +0 -0
  20. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/log.py +0 -0
  21. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/timeoutloop.py +0 -0
  22. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/CTimecode.py +0 -0
  23. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/CTimecodeTimer.py +0 -0
  24. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/CommunicatorServices.py +0 -0
  25. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/ConfigBase.py +0 -0
  26. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/ConfigManager.py +0 -0
  27. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/CopyMoveVersioned.py +0 -0
  28. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/FadeCalculator.py +0 -0
  29. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/HubServices.py +0 -0
  30. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/SignalEngine.py +0 -0
  31. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/StringSanitizer.py +0 -0
  32. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/Uuid.py +0 -0
  33. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/tools/__init__.py +0 -0
  34. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/CMLCuemsConverter.py +0 -0
  35. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/Parsers.py +0 -0
  36. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/Settings.py +0 -0
  37. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/XmlBuilder.py +0 -0
  38. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/XmlReaderWriter.py +0 -0
  39. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/__init__.py +0 -0
  40. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/schemas/network_map.xsd +0 -0
  41. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/schemas/outputs.xsd +0 -0
  42. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/schemas/project_mappings.xsd +0 -0
  43. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/schemas/project_settings.xsd +0 -0
  44. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/schemas/script.xsd +0 -0
  45. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/cuemsutils/xml/schemas/settings.xsd +0 -0
  46. {cuemsutils-0.1.0rc15 → cuemsutils-0.1.0rc16}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cuemsutils
3
- Version: 0.1.0rc15
3
+ Version: 0.1.0rc16
4
4
  Summary: Reusable classes and methods for CueMS system
5
5
  Project-URL: Documentation, https://stagesoft.github.io/cuems-utils/
6
6
  Project-URL: Issues, https://github.com/stagesoft/cuems-utils/issues
@@ -2,4 +2,4 @@
2
2
  #
3
3
  # SPDX-License-Identifier: GPL-3.0
4
4
  # SPDX-FileContributor: Ion Reguera <ion@stagelab.coop>
5
- __version__ = "0.1.0rc15"
5
+ __version__ = "0.1.0rc16"
@@ -1,6 +1,10 @@
1
+ # SPDX-FileCopyrightText: 2026 Stagelab Coop SCCL
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+ # SPDX-FileContributor: Ion Reguera <ion@stagelab.coop>
1
4
  from .Cue import Cue
2
5
  from .MediaCue import MediaCue
3
6
  from ..helpers import ensure_items
7
+ from ..log import Logger
4
8
  from ..tools.Uuid import Uuid
5
9
 
6
10
  REQ_ITEMS = {
@@ -111,9 +115,24 @@ class CueList(Cue):
111
115
  for cue in self.contents:
112
116
  if isinstance(cue, CueList):
113
117
  media_dict.update(cue.get_media())
114
- elif isinstance(cue, MediaCue) and hasattr(cue.media, 'file_name'):
115
- media_dict[str(cue.id)] = {str(cue.media.id) : cue.media.file_name }
116
-
118
+ elif isinstance(cue, MediaCue):
119
+ state, file_name = cue.media_status()
120
+ if state == 'ok':
121
+ media_dict[str(cue.id)] = {str(cue.media.id): file_name}
122
+ elif state == 'none':
123
+ # Legacy <Media/>: keep such projects listable and
124
+ # duplicable; saving one is the editor's validator's job.
125
+ Logger.warning(
126
+ f"cue {cue.get('name')} ({cue.get('id')}) has no media, skipped"
127
+ )
128
+ else:
129
+ # A media block without a file: a client dropped it. Never
130
+ # skip silently — a save that got through would write an
131
+ # empty <Media/> and lose the cue's media for good.
132
+ raise ValueError(
133
+ f"cue {cue.get('name')} ({cue.get('id')}) has no media file"
134
+ )
135
+
117
136
  return media_dict
118
137
 
119
138
  def get_next_cue(self):
@@ -1,3 +1,6 @@
1
+ # SPDX-FileCopyrightText: 2026 Stagelab Coop SCCL
2
+ # SPDX-License-Identifier: GPL-3.0-or-later
3
+ # SPDX-FileContributor: Ion Reguera <ion@stagelab.coop>
1
4
  import json
2
5
  import json_fix
3
6
 
@@ -238,11 +241,21 @@ class CuemsScript(dict):
238
241
  media_dict.update(
239
242
  self.get_own_media(config=config, cuelist=cue)
240
243
  )
241
- elif isinstance(cue, MediaCue) and hasattr(cue.media, 'file_name'):
242
- Logger.debug(f'get_own_media media cue at {pos}')
243
- cue.localize_cue(config.node_conf['uuid'])
244
- if cue._local:
245
- media_dict[str(cue.id)] = cue.media.file_name
244
+ elif isinstance(cue, MediaCue):
245
+ state, file_name = cue.media_status()
246
+ if state != 'ok':
247
+ # Engine path, every node, every load: never raise here.
248
+ # The project still loads and this cue fails at arm,
249
+ # loudly and locally, as it always did for <Media/>.
250
+ Logger.error(
251
+ f"get_own_media: cue {cue.get('name')} ({cue.get('id')}) "
252
+ f"has no media file ({state}); left out of the media list"
253
+ )
254
+ else:
255
+ Logger.debug(f'get_own_media media cue at {pos}')
256
+ cue.localize_cue(config.node_conf['uuid'])
257
+ if cue._local:
258
+ media_dict[str(cue.id)] = file_name
246
259
  pos += 1
247
260
  return media_dict
248
261
 
@@ -382,6 +382,31 @@ class MediaCue(Cue):
382
382
 
383
383
  media: Media = property(get_Media, set_Media)
384
384
 
385
+ def media_status(self) -> tuple[str, str | None]:
386
+ """Classify this cue's media without raising.
387
+
388
+ Returns ``(state, file_name)`` with ``state`` one of:
389
+
390
+ * ``'none'`` — no media object at all (``None``): what an empty
391
+ ``<Media/>`` or a JSON ``Media: null`` decodes to;
392
+ * ``'invalid'`` — a media object with no ``file_name`` key, an empty
393
+ ``file_name``, or no ``id`` key: what a client that dropped the
394
+ media block produces (``ensure_items`` builds an empty ``Media``);
395
+ * ``'ok'`` — a usable media reference.
396
+
397
+ Never touches the ``file_name`` property: it raises ``KeyError`` when
398
+ the key is missing, which is exactly the crash this replaces.
399
+ """
400
+ media = dict.get(self, 'Media')
401
+ if media is None:
402
+ return 'none', None
403
+ if not isinstance(media, dict):
404
+ return 'invalid', None
405
+ file_name = dict.get(media, 'file_name')
406
+ if not file_name or 'id' not in media:
407
+ return 'invalid', None
408
+ return 'ok', str(file_name)
409
+
385
410
  def get_outputs(self):
386
411
  """Get the output routing configuration.
387
412
 
File without changes
File without changes