winipedia-utils 0.1.34__tar.gz → 0.1.36__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 (97) hide show
  1. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/PKG-INFO +1 -1
  2. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/pyproject.toml +1 -1
  3. winipedia_utils-0.1.36/winipedia_utils/pyside/core/__init__.py +1 -0
  4. winipedia_utils-0.1.36/winipedia_utils/pyside/core/py_qiodevice.py +93 -0
  5. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/pages/player.py +3 -2
  6. winipedia_utils-0.1.36/winipedia_utils/pyside/ui/widgets/clickable_widget.py +29 -0
  7. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/widgets/media_player.py +53 -39
  8. winipedia_utils-0.1.36/winipedia_utils/security/cryptography.py +29 -0
  9. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/security/keyring.py +11 -7
  10. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/LICENSE +0 -0
  11. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/README.md +0 -0
  12. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/__init__.py +0 -0
  13. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/concurrent/__init__.py +0 -0
  14. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/concurrent/concurrent.py +0 -0
  15. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/concurrent/multiprocessing.py +0 -0
  16. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/concurrent/multithreading.py +0 -0
  17. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/consts.py +0 -0
  18. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/data/__init__.py +0 -0
  19. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/data/dataframe.py +0 -0
  20. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/django/__init__.py +0 -0
  21. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/django/bulk.py +0 -0
  22. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/django/command.py +0 -0
  23. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/django/database.py +0 -0
  24. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/__init__.py +0 -0
  25. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/gitignore/__init__.py +0 -0
  26. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/gitignore/gitignore.py +0 -0
  27. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/pre_commit/__init__.py +0 -0
  28. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/pre_commit/config.py +0 -0
  29. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/pre_commit/hooks.py +0 -0
  30. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/git/pre_commit/run_hooks.py +0 -0
  31. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/iterating/__init__.py +0 -0
  32. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/iterating/iterate.py +0 -0
  33. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/logging/__init__.py +0 -0
  34. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/logging/ansi.py +0 -0
  35. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/logging/config.py +0 -0
  36. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/logging/logger.py +0 -0
  37. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/modules/__init__.py +0 -0
  38. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/modules/class_.py +0 -0
  39. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/modules/function.py +0 -0
  40. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/modules/module.py +0 -0
  41. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/modules/package.py +0 -0
  42. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/oop/__init__.py +0 -0
  43. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/oop/mixins/__init__.py +0 -0
  44. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/oop/mixins/meta.py +0 -0
  45. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/oop/mixins/mixin.py +0 -0
  46. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/os/__init__.py +0 -0
  47. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/os/os.py +0 -0
  48. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/projects/__init__.py +0 -0
  49. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/projects/poetry/__init__.py +0 -0
  50. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/projects/poetry/config.py +0 -0
  51. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/projects/poetry/poetry.py +0 -0
  52. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/projects/project.py +0 -0
  53. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/py.typed +0 -0
  54. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/__init__.py +0 -0
  55. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/__init__.py +0 -0
  56. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/base/__init__.py +0 -0
  57. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/base/base.py +0 -0
  58. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/pages/__init__.py +0 -0
  59. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/pages/base/__init__.py +0 -0
  60. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/pages/base/base.py +0 -0
  61. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/pages/browser.py +0 -0
  62. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/widgets/__init__.py +0 -0
  63. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/widgets/browser.py +0 -0
  64. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/widgets/notification.py +0 -0
  65. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/windows/__init__.py +0 -0
  66. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/windows/base/__init__.py +0 -0
  67. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/pyside/ui/windows/base/base.py +0 -0
  68. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/__init__.py +0 -0
  69. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/__init__.py +0 -0
  70. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/download_arrow.svg +0 -0
  71. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +0 -0
  72. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/fullscreen_icon.svg +0 -0
  73. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/pause_icon.svg +0 -0
  74. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/play_icon.svg +0 -0
  75. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/resources/svgs/svg.py +0 -0
  76. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/security/__init__.py +0 -0
  77. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/setup.py +0 -0
  78. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/__init__.py +0 -0
  79. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/assertions.py +0 -0
  80. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/convention.py +0 -0
  81. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/create_tests.py +0 -0
  82. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/fixtures.py +0 -0
  83. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/__init__.py +0 -0
  84. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/__init__.py +0 -0
  85. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/__init__.py +0 -0
  86. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/fixture.py +0 -0
  87. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/scopes/__init__.py +0 -0
  88. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +0 -0
  89. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/scopes/function.py +0 -0
  90. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/scopes/module.py +0 -0
  91. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/scopes/package.py +0 -0
  92. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/fixtures/scopes/session.py +0 -0
  93. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/utils/__init__.py +0 -0
  94. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/base/utils/utils.py +0 -0
  95. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/testing/tests/conftest.py +0 -0
  96. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/text/__init__.py +0 -0
  97. {winipedia_utils-0.1.34 → winipedia_utils-0.1.36}/winipedia_utils/text/string.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: winipedia-utils
3
- Version: 0.1.34
3
+ Version: 0.1.36
4
4
  Summary: A package with many utility functions
5
5
  License: MIT
6
6
  Author: Winipedia
@@ -1,7 +1,7 @@
1
1
  # Project section
2
2
  [project]
3
3
  name = "winipedia-utils"
4
- version = "0.1.34"
4
+ version = "0.1.36"
5
5
  description = "A package with many utility functions"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.12,<3.14"
@@ -0,0 +1 @@
1
+ """__init__ module for winipedia_utils.pyside6.core."""
@@ -0,0 +1,93 @@
1
+ """PySide6 QIODevice wrapper."""
2
+
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from PySide6.QtCore import QFile, QIODevice
7
+
8
+
9
+ class PyQIODevice(QIODevice):
10
+ """QFile subclass that decrypts data on read."""
11
+
12
+ def __init__(self, q_device: QIODevice, *args: Any, **kwargs: Any) -> None:
13
+ """Initialize the device."""
14
+ super().__init__(*args, **kwargs)
15
+ self.q_device = q_device
16
+
17
+ def atEnd(self) -> bool: # noqa: N802
18
+ """Check if we are at the end of the file."""
19
+ return self.q_device.atEnd()
20
+
21
+ def bytesAvailable(self) -> int: # noqa: N802
22
+ """Return the number of bytes available for reading."""
23
+ return self.q_device.bytesAvailable()
24
+
25
+ def bytesToWrite(self) -> int: # noqa: N802
26
+ """Return the number of bytes available for writing."""
27
+ return self.q_device.bytesToWrite()
28
+
29
+ def canReadLine(self) -> bool: # noqa: N802
30
+ """Check if we can read a line."""
31
+ return self.q_device.canReadLine()
32
+
33
+ def close(self) -> None:
34
+ """Close the device."""
35
+ self.q_device.close()
36
+ return super().close()
37
+
38
+ def isSequential(self) -> bool: # noqa: N802
39
+ """Check if the device is sequential."""
40
+ return self.q_device.isSequential()
41
+
42
+ def open(self, mode: QIODevice.OpenModeFlag) -> bool:
43
+ """Open the device."""
44
+ self.q_device.open(mode)
45
+ return super().open(mode)
46
+
47
+ def pos(self) -> int:
48
+ """Return the current position in the device."""
49
+ return self.q_device.pos()
50
+
51
+ def readData(self, maxlen: int) -> bytes: # noqa: N802
52
+ """Read data from the device."""
53
+ return bytes(self.q_device.read(maxlen).data())
54
+
55
+ def readLineData(self, maxlen: int) -> object: # noqa: N802
56
+ """Read a line from the device."""
57
+ return self.q_device.readLine(maxlen)
58
+
59
+ def reset(self) -> bool:
60
+ """Reset the device."""
61
+ return self.q_device.reset()
62
+
63
+ def seek(self, pos: int) -> bool:
64
+ """Seek to a position in the device."""
65
+ return self.q_device.seek(pos)
66
+
67
+ def size(self) -> int:
68
+ """Return the size of the device."""
69
+ return self.q_device.size()
70
+
71
+ def skipData(self, maxSize: int) -> int: # noqa: N802, N803
72
+ """Skip data in the device."""
73
+ return self.q_device.skip(maxSize)
74
+
75
+ def waitForBytesWritten(self, msecs: int) -> bool: # noqa: N802
76
+ """Wait for bytes to be written."""
77
+ return self.q_device.waitForBytesWritten(msecs)
78
+
79
+ def waitForReadyRead(self, msecs: int) -> bool: # noqa: N802
80
+ """Wait for the device to be ready to read."""
81
+ return self.q_device.waitForReadyRead(msecs)
82
+
83
+ def writeData(self, data: bytes | bytearray | memoryview, len: int) -> int: # noqa: A002, ARG002, N802
84
+ """Write data to the device."""
85
+ return self.q_device.write(data)
86
+
87
+
88
+ class PyQFile(PyQIODevice):
89
+ """QFile subclass."""
90
+
91
+ def __init__(self, path: Path, *args: Any, **kwargs: Any) -> None:
92
+ """Initialize the device."""
93
+ super().__init__(QFile(path), *args, **kwargs)
@@ -3,6 +3,7 @@
3
3
  This module contains the player page class for the VideoVault application.
4
4
  """
5
5
 
6
+ from pathlib import Path
6
7
  from typing import final
7
8
 
8
9
  from winipedia_utils.pyside.ui.pages.base.base import Base as BasePage
@@ -18,9 +19,9 @@ class Player(BasePage):
18
19
  self.media_player = MediaPlayer(self.v_layout)
19
20
 
20
21
  @final
21
- def play_data(self, data: bytes, name: str) -> None:
22
+ def play_file(self, path: Path) -> None:
22
23
  """Play the video."""
23
24
  # set current page to player
24
25
  self.set_current_page(self.__class__)
25
26
  # Stop current playback and clean up resources
26
- self.media_player.play_data(data, name)
27
+ self.media_player.play_file(path)
@@ -0,0 +1,29 @@
1
+ from typing import Any
2
+
3
+ from PySide6.QtCore import Qt, Signal
4
+ from PySide6.QtMultimediaWidgets import QVideoWidget
5
+ from PySide6.QtWidgets import QWidget
6
+
7
+
8
+ class ClickableWidget(QWidget):
9
+ """Widget that can be clicked."""
10
+
11
+ clicked = Signal()
12
+
13
+ def mousePressEvent(self, event: Any) -> None: # noqa: N802
14
+ """Handle mouse press event."""
15
+ if event.button() == Qt.MouseButton.LeftButton:
16
+ self.clicked.emit()
17
+ super().mousePressEvent(event)
18
+
19
+
20
+ class ClickableVideoWidget(QVideoWidget):
21
+ """Video widget that can be clicked."""
22
+
23
+ clicked = Signal()
24
+
25
+ def mousePressEvent(self, event: Any) -> None: # noqa: N802
26
+ """Handle mouse press event."""
27
+ if event.button() == Qt.MouseButton.LeftButton:
28
+ self.clicked.emit()
29
+ super().mousePressEvent(event)
@@ -3,12 +3,14 @@
3
3
  This module contains the media player class.
4
4
  """
5
5
 
6
+ import time
7
+ from collections.abc import Callable
6
8
  from functools import partial
9
+ from pathlib import Path
7
10
  from typing import Any
8
11
 
9
- from PySide6.QtCore import QBuffer, QByteArray, Qt, Signal
12
+ from PySide6.QtCore import Qt, QTimer, QUrl
10
13
  from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
11
- from PySide6.QtMultimediaWidgets import QVideoWidget
12
14
  from PySide6.QtWidgets import (
13
15
  QHBoxLayout,
14
16
  QLayout,
@@ -20,31 +22,9 @@ from PySide6.QtWidgets import (
20
22
  QWidget,
21
23
  )
22
24
 
25
+ from winipedia_utils.pyside.core.py_qiodevice import PyQFile, PyQIODevice
23
26
  from winipedia_utils.pyside.ui.base.base import Base as BaseUI
24
-
25
-
26
- class ClickableWidget(QWidget):
27
- """Widget that can be clicked."""
28
-
29
- clicked = Signal()
30
-
31
- def mousePressEvent(self, event: Any) -> None: # noqa: N802
32
- """Handle mouse press event."""
33
- if event.button() == Qt.MouseButton.LeftButton:
34
- self.clicked.emit()
35
- super().mousePressEvent(event)
36
-
37
-
38
- class ClickableVideoWidget(QVideoWidget):
39
- """Video widget that can be clicked."""
40
-
41
- clicked = Signal()
42
-
43
- def mousePressEvent(self, event: Any) -> None: # noqa: N802
44
- """Handle mouse press event."""
45
- if event.button() == Qt.MouseButton.LeftButton:
46
- self.clicked.emit()
47
- super().mousePressEvent(event)
27
+ from winipedia_utils.pyside.ui.widgets.clickable_widget import ClickableVideoWidget
48
28
 
49
29
 
50
30
  class MediaPlayer(QMediaPlayer):
@@ -228,38 +208,72 @@ class MediaPlayer(QMediaPlayer):
228
208
  self.media_controls_layout_below.addWidget(self.progress_slider)
229
209
 
230
210
  # Connect media player signals to update the progress slider
231
- self.positionChanged.connect(self.update_position)
232
- self.durationChanged.connect(self.set_duration)
211
+ self.positionChanged.connect(self.update_slider_position)
212
+ self.durationChanged.connect(self.set_slider_range)
233
213
 
234
214
  # Connect slider signals to update video position
235
- self.progress_slider.sliderMoved.connect(self.set_position)
236
- self.progress_slider.sliderReleased.connect(self.slider_released)
215
+ self.last_slider_moved_update = time.time()
216
+ self.slider_moved_update_interval = 0.1
217
+ self.progress_slider.sliderMoved.connect(self.on_slider_moved)
218
+ self.progress_slider.sliderReleased.connect(self.on_slider_released)
237
219
 
238
- def update_position(self, position: int) -> None:
220
+ def update_slider_position(self, position: int) -> None:
239
221
  """Update the progress slider position."""
240
222
  # Only update if not being dragged to prevent jumps during manual sliding
241
223
  if not self.progress_slider.isSliderDown():
242
224
  self.progress_slider.setValue(position)
243
225
 
244
- def set_duration(self, duration: int) -> None:
226
+ def set_slider_range(self, duration: int) -> None:
245
227
  """Set the progress slider range based on media duration."""
246
228
  self.progress_slider.setRange(0, duration)
247
229
 
248
- def set_position(self, position: int) -> None:
230
+ def on_slider_moved(self, position: int) -> None:
249
231
  """Set the media position when slider is moved."""
232
+ current_time = time.time()
233
+ if (
234
+ current_time - self.last_slider_moved_update
235
+ > self.slider_moved_update_interval
236
+ ):
237
+ self.setPosition(position)
238
+ self.last_slider_moved_update = current_time
250
239
  self.setPosition(position)
251
240
 
252
- def slider_released(self) -> None:
241
+ def on_slider_released(self) -> None:
253
242
  """Handle slider release event."""
254
243
  self.setPosition(self.progress_slider.value())
255
244
 
256
- def play_data(self, data: bytes, name: str) -> None:
245
+ def play_video(
246
+ self, set_source_func: Callable[..., Any], *args: Any, **kwargs: Any
247
+ ) -> None:
257
248
  """Play the video."""
258
249
  self.stop()
259
- self.buffer = QBuffer()
260
- self.buffer.setData(QByteArray(data))
261
- self.buffer.open(QBuffer.OpenModeFlag.ReadOnly)
262
250
 
263
- self.setSourceDevice(self.buffer, name)
251
+ # prevents freezing when starting a new video while another is playing
252
+ QTimer.singleShot(
253
+ 100, partial(self.set_source_and_play, set_source_func, *args, **kwargs)
254
+ )
264
255
 
256
+ def set_source_and_play(
257
+ self, set_source_func: Callable[..., Any], *args: Any, **kwargs: Any
258
+ ) -> None:
259
+ """Set the source and play the video."""
260
+ set_source_func(*args, **kwargs)
265
261
  self.play()
262
+
263
+ def play_file(self, path: Path) -> None:
264
+ """Play the video."""
265
+ self.play_video(
266
+ self.set_source_device,
267
+ io_device=PyQFile(path),
268
+ source_url=QUrl.fromLocalFile(path),
269
+ )
270
+
271
+ def set_source_device(
272
+ self, io_device: PyQIODevice, source_url: QUrl | None = None
273
+ ) -> None:
274
+ """Play the video."""
275
+ if source_url is None:
276
+ source_url = QUrl()
277
+ self.source_url = source_url
278
+ self.io_device = io_device
279
+ self.setSourceDevice(self.io_device, self.source_url)
@@ -0,0 +1,29 @@
1
+ """Cryptography utilities for secure data handling.
2
+
3
+ This module provides utility functions for working with cryptography,
4
+ including encryption and decryption using Fernet and AESGCM.
5
+ These utilities help with secure data handling.
6
+ """
7
+
8
+ import os
9
+
10
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
11
+
12
+ IV_LEN = 12
13
+
14
+
15
+ def encrypt_with_aes_gcm(
16
+ aes_gcm: AESGCM, data: bytes, aad: bytes | None = None
17
+ ) -> bytes:
18
+ """Encrypt data using AESGCM."""
19
+ iv = os.urandom(IV_LEN)
20
+ encrypted = aes_gcm.encrypt(iv, data, aad)
21
+ return iv + encrypted
22
+
23
+
24
+ def decrypt_with_aes_gcm(
25
+ aes_gcm: AESGCM, data: bytes, aad: bytes | None = None
26
+ ) -> bytes:
27
+ """Decrypt data using AESGCM."""
28
+ iv, encrypted = data[:IV_LEN], data[IV_LEN:]
29
+ return aes_gcm.decrypt(iv, encrypted, aad)
@@ -7,9 +7,10 @@ These utilities help with secure storage and retrieval of secrets.
7
7
 
8
8
  import keyring
9
9
  from cryptography.fernet import Fernet
10
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
10
11
 
11
12
 
12
- def get_or_create_secret(service_name: str, username: str) -> str:
13
+ def get_or_create_fernet(service_name: str, username: str) -> Fernet:
13
14
  """Get the app secret using keyring.
14
15
 
15
16
  If it does not exist, create it with a Fernet.
@@ -18,13 +19,16 @@ def get_or_create_secret(service_name: str, username: str) -> str:
18
19
  if secret is None:
19
20
  secret = Fernet.generate_key().decode()
20
21
  keyring.set_password(service_name, username, secret)
21
- return secret
22
+ return Fernet(secret.encode())
22
23
 
23
24
 
24
- def get_or_create_fernet(service_name: str, username: str) -> Fernet:
25
- """Get the app fernet using keyring.
25
+ def get_or_create_aes_gcm(service_name: str, username: str) -> AESGCM:
26
+ """Get the app secret using keyring.
26
27
 
27
- If it does not exist, create it with a Fernet.
28
+ If it does not exist, create it with a AESGCM.
28
29
  """
29
- secret = get_or_create_secret(service_name, username)
30
- return Fernet(secret.encode())
30
+ secret = keyring.get_password(service_name, username)
31
+ if secret is None:
32
+ secret = AESGCM.generate_key(bit_length=256).decode()
33
+ keyring.set_password(service_name, username, secret)
34
+ return AESGCM(secret.encode())