winipedia-utils 0.1.39__py3-none-any.whl → 0.1.41__py3-none-any.whl

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.
@@ -4,11 +4,11 @@ This module contains the media player class.
4
4
  """
5
5
 
6
6
  import time
7
- from collections.abc import Callable
8
7
  from functools import partial
9
8
  from pathlib import Path
10
9
  from typing import Any
11
10
 
11
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
12
12
  from PySide6.QtCore import Qt, QTimer, QUrl
13
13
  from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
14
14
  from PySide6.QtWidgets import (
@@ -22,7 +22,11 @@ from PySide6.QtWidgets import (
22
22
  QWidget,
23
23
  )
24
24
 
25
- from winipedia_utils.pyside.core.py_qiodevice import PyQFile, PyQIODevice
25
+ from winipedia_utils.pyside.core.py_qiodevice import (
26
+ EncryptedPyQFile,
27
+ PyQFile,
28
+ PyQIODevice,
29
+ )
26
30
  from winipedia_utils.pyside.ui.base.base import Base as BaseUI
27
31
  from winipedia_utils.pyside.ui.widgets.clickable_widget import ClickableVideoWidget
28
32
 
@@ -31,13 +35,23 @@ class MediaPlayer(QMediaPlayer):
31
35
  """Media player class."""
32
36
 
33
37
  def __init__(self, parent_layout: QLayout, *args: Any, **kwargs: Any) -> None:
34
- """Initialize the media player."""
38
+ """Initialize the media player.
39
+
40
+ Args:
41
+ parent_layout: The parent layout to add the media player widget to.
42
+ *args: Additional positional arguments passed to parent constructor.
43
+ **kwargs: Additional keyword arguments passed to parent constructor.
44
+ """
35
45
  super().__init__(*args, **kwargs)
36
46
  self.parent_layout = parent_layout
37
47
  self.make_widget()
38
48
 
39
49
  def make_widget(self) -> None:
40
- """Make the widget."""
50
+ """Make the widget.
51
+
52
+ Creates the main media player widget with vertical layout, adds media controls
53
+ above and below the video widget, and creates the video widget itself.
54
+ """
41
55
  self.media_player_widget = QWidget()
42
56
  self.media_player_layout = QVBoxLayout(self.media_player_widget)
43
57
  self.parent_layout.addWidget(self.media_player_widget)
@@ -46,7 +60,11 @@ class MediaPlayer(QMediaPlayer):
46
60
  self.add_media_controls_below()
47
61
 
48
62
  def make_video_widget(self) -> None:
49
- """Make the video widget."""
63
+ """Make the video widget.
64
+
65
+ Creates a clickable video widget with expanding size policy, sets up
66
+ audio output, and connects the click signal to toggle media controls.
67
+ """
50
68
  self.video_widget = ClickableVideoWidget()
51
69
  self.video_widget.clicked.connect(self.on_video_clicked)
52
70
  self.video_widget.setSizePolicy(
@@ -60,24 +78,37 @@ class MediaPlayer(QMediaPlayer):
60
78
  self.media_player_layout.addWidget(self.video_widget)
61
79
 
62
80
  def on_video_clicked(self) -> None:
63
- """Handle video widget click."""
81
+ """Handle video widget click.
82
+
83
+ Toggles the visibility of media controls when the video widget is clicked.
84
+ """
64
85
  if self.media_controls_widget_above.isVisible():
65
86
  self.hide_media_controls()
66
87
  return
67
88
  self.show_media_controls()
68
89
 
69
90
  def show_media_controls(self) -> None:
70
- """Show media controls."""
91
+ """Show media controls.
92
+
93
+ Makes both the above and below media control widgets visible.
94
+ """
71
95
  self.media_controls_widget_above.show()
72
96
  self.media_controls_widget_below.show()
73
97
 
74
98
  def hide_media_controls(self) -> None:
75
- """Hide media controls."""
99
+ """Hide media controls.
100
+
101
+ Hides both the above and below media control widgets.
102
+ """
76
103
  self.media_controls_widget_above.hide()
77
104
  self.media_controls_widget_below.hide()
78
105
 
79
106
  def add_media_controls_above(self) -> None:
80
- """Add media controls above the video."""
107
+ """Add media controls above the video.
108
+
109
+ Creates the top control bar with left, center, and right sections,
110
+ then adds speed, volume, playback, and fullscreen controls.
111
+ """
81
112
  # main above widget
82
113
  self.media_controls_widget_above = QWidget()
83
114
  self.media_controls_layout_above = QHBoxLayout(self.media_controls_widget_above)
@@ -106,14 +137,21 @@ class MediaPlayer(QMediaPlayer):
106
137
  self.add_fullscreen_control()
107
138
 
108
139
  def add_media_controls_below(self) -> None:
109
- """Add media controls below the video."""
140
+ """Add media controls below the video.
141
+
142
+ Creates the bottom control bar and adds the progress control slider.
143
+ """
110
144
  self.media_controls_widget_below = QWidget()
111
145
  self.media_controls_layout_below = QHBoxLayout(self.media_controls_widget_below)
112
146
  self.media_player_layout.addWidget(self.media_controls_widget_below)
113
147
  self.add_progress_control()
114
148
 
115
149
  def add_playback_control(self) -> None:
116
- """Add playback control."""
150
+ """Add playback control.
151
+
152
+ Creates a play/pause button with appropriate icons and connects it
153
+ to the toggle_playback method. Adds the button to the center controls.
154
+ """
117
155
  self.play_icon = BaseUI.get_svg_icon("play_icon")
118
156
  self.pause_icon = BaseUI.get_svg_icon("pause_icon")
119
157
  # Pause symbol: ⏸ (U+23F8)
@@ -124,7 +162,11 @@ class MediaPlayer(QMediaPlayer):
124
162
  self.center_controls_layout.addWidget(self.playback_button)
125
163
 
126
164
  def toggle_playback(self) -> None:
127
- """Toggle playback."""
165
+ """Toggle playback.
166
+
167
+ Switches between play and pause states, updating the button icon
168
+ accordingly based on the current playback state.
169
+ """
128
170
  if self.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
129
171
  self.pause()
130
172
  self.playback_button.setIcon(self.play_icon)
@@ -135,7 +177,8 @@ class MediaPlayer(QMediaPlayer):
135
177
  def add_speed_control(self) -> None:
136
178
  """Add speed control.
137
179
 
138
- A button in the top left that on click shows a dropdown to select the speed.
180
+ Creates a button in the top left that shows a dropdown menu to select
181
+ playback speed from predefined options (0.2x to 5x).
139
182
  """
140
183
  self.default_speed = 1
141
184
  self.speed_options = [0.2, 0.5, self.default_speed, 1.5, 2, 3, 4, 5]
@@ -149,24 +192,40 @@ class MediaPlayer(QMediaPlayer):
149
192
  self.left_controls_layout.addWidget(self.speed_button)
150
193
 
151
194
  def change_speed(self, speed: float) -> None:
152
- """Change playback speed."""
195
+ """Change playback speed.
196
+
197
+ Args:
198
+ speed: The new playback speed multiplier.
199
+ """
153
200
  self.setPlaybackRate(speed)
154
201
  self.speed_button.setText(f"{speed}x")
155
202
 
156
203
  def add_volume_control(self) -> None:
157
- """Add volume control."""
204
+ """Add volume control.
205
+
206
+ Creates a horizontal slider for volume control with range 0-100
207
+ and connects it to the volume change handler.
208
+ """
158
209
  self.volume_slider = QSlider(Qt.Orientation.Horizontal)
159
210
  self.volume_slider.setRange(0, 100)
160
211
  self.volume_slider.valueChanged.connect(self.on_volume_changed)
161
212
  self.left_controls_layout.addWidget(self.volume_slider)
162
213
 
163
214
  def on_volume_changed(self, value: int) -> None:
164
- """Handle volume slider value change."""
215
+ """Handle volume slider value change.
216
+
217
+ Args:
218
+ value: The new volume value from 0-100.
219
+ """
165
220
  volume = value / 100.0 # Convert to 0.0-1.0 range
166
221
  self.audio_output.setVolume(volume)
167
222
 
168
223
  def add_fullscreen_control(self) -> None:
169
- """Add fullscreen control."""
224
+ """Add fullscreen control.
225
+
226
+ Creates a fullscreen toggle button with appropriate icons and determines
227
+ which widgets to hide/show when entering/exiting fullscreen mode.
228
+ """
170
229
  self.fullscreen_icon = BaseUI.get_svg_icon("fullscreen_icon")
171
230
  self.exit_fullscreen_icon = BaseUI.get_svg_icon("exit_fullscreen_icon")
172
231
  self.fullscreen_button = QPushButton()
@@ -187,7 +246,11 @@ class MediaPlayer(QMediaPlayer):
187
246
  self.right_controls_layout.addWidget(self.fullscreen_button)
188
247
 
189
248
  def toggle_fullscreen(self) -> None:
190
- """Toggle fullscreen mode."""
249
+ """Toggle fullscreen mode.
250
+
251
+ Switches between fullscreen and windowed mode, hiding/showing other
252
+ widgets and updating the button icon accordingly.
253
+ """
191
254
  # Get the main window
192
255
  main_window = self.media_player_widget.window()
193
256
  if main_window.isFullScreen():
@@ -203,7 +266,11 @@ class MediaPlayer(QMediaPlayer):
203
266
  self.fullscreen_button.setIcon(self.exit_fullscreen_icon)
204
267
 
205
268
  def add_progress_control(self) -> None:
206
- """Add progress control."""
269
+ """Add progress control.
270
+
271
+ Creates a horizontal progress slider and connects it to media player
272
+ signals for position updates and user interaction handling.
273
+ """
207
274
  self.progress_slider = QSlider(Qt.Orientation.Horizontal)
208
275
  self.media_controls_layout_below.addWidget(self.progress_slider)
209
276
 
@@ -218,17 +285,32 @@ class MediaPlayer(QMediaPlayer):
218
285
  self.progress_slider.sliderReleased.connect(self.on_slider_released)
219
286
 
220
287
  def update_slider_position(self, position: int) -> None:
221
- """Update the progress slider position."""
288
+ """Update the progress slider position.
289
+
290
+ Args:
291
+ position: The current media position in milliseconds.
292
+ """
222
293
  # Only update if not being dragged to prevent jumps during manual sliding
223
294
  if not self.progress_slider.isSliderDown():
224
295
  self.progress_slider.setValue(position)
225
296
 
226
297
  def set_slider_range(self, duration: int) -> None:
227
- """Set the progress slider range based on media duration."""
298
+ """Set the progress slider range based on media duration.
299
+
300
+ Args:
301
+ duration: The total media duration in milliseconds.
302
+ """
228
303
  self.progress_slider.setRange(0, duration)
229
304
 
230
305
  def on_slider_moved(self, position: int) -> None:
231
- """Set the media position when slider is moved."""
306
+ """Set the media position when slider is moved.
307
+
308
+ Implements throttling to prevent excessive position updates during
309
+ slider dragging for better performance.
310
+
311
+ Args:
312
+ position: The new position from the slider in milliseconds.
313
+ """
232
314
  current_time = time.time()
233
315
  if (
234
316
  current_time - self.last_slider_moved_update
@@ -238,37 +320,104 @@ class MediaPlayer(QMediaPlayer):
238
320
  self.last_slider_moved_update = current_time
239
321
 
240
322
  def on_slider_released(self) -> None:
241
- """Handle slider release event."""
323
+ """Handle slider release event.
324
+
325
+ Sets the final media position when the user releases the slider.
326
+ """
242
327
  self.setPosition(self.progress_slider.value())
243
328
 
244
329
  def play_video(
245
- self, set_source_func: Callable[..., Any], *args: Any, **kwargs: Any
330
+ self,
331
+ io_device: PyQIODevice,
332
+ source_url: QUrl,
333
+ position: int = 0,
246
334
  ) -> None:
247
- """Play the video."""
335
+ """Play the video.
336
+
337
+ Stops current playback and starts a new video using the provided
338
+ source function with a delay to prevent freezing.
339
+
340
+ Args:
341
+ io_device: The PyQIODevice to use as the media source.
342
+ source_url: The QUrl representing the source location.
343
+ position: The position to start playback from in milliseconds.
344
+ """
248
345
  self.stop()
249
346
 
347
+ self.resume_func = partial(self.resume_to_position, position=position)
348
+ self.mediaStatusChanged.connect(self.resume_func)
349
+
250
350
  # prevents freezing when starting a new video while another is playing
251
351
  QTimer.singleShot(
252
- 100, partial(self.set_source_and_play, set_source_func, *args, **kwargs)
352
+ 100,
353
+ partial(
354
+ self.set_source_and_play, io_device=io_device, source_url=source_url
355
+ ),
253
356
  )
254
357
 
358
+ def resume_to_position(
359
+ self, status: QMediaPlayer.MediaStatus, position: int
360
+ ) -> None:
361
+ """Resume playback to a position.
362
+
363
+ Args:
364
+ status: The current media status.
365
+ position: The position to resume playback from in milliseconds.
366
+ """
367
+ if status == QMediaPlayer.MediaStatus.BufferedMedia:
368
+ self.setPosition(position)
369
+ self.mediaStatusChanged.disconnect(self.resume_func)
370
+
255
371
  def set_source_and_play(
256
- self, set_source_func: Callable[..., Any], *args: Any, **kwargs: Any
372
+ self,
373
+ io_device: PyQIODevice,
374
+ source_url: QUrl,
257
375
  ) -> None:
258
- """Set the source and play the video."""
259
- set_source_func(*args, **kwargs)
376
+ """Set the source and play the video.
377
+
378
+ Args:
379
+ io_device: The PyQIODevice to use as the media source.
380
+ source_url: The QUrl representing the source location.
381
+ """
382
+ self.set_source_device(io_device, source_url)
260
383
  self.play()
261
384
 
262
- def play_file(self, path: Path) -> None:
263
- """Play the video."""
385
+ def set_source_device(self, io_device: PyQIODevice, source_url: QUrl) -> None:
386
+ """Set the source device for playback.
387
+
388
+ Args:
389
+ io_device: The PyQIODevice to use as the media source.
390
+ source_url: The QUrl representing the source location.
391
+ """
392
+ self.source_url = source_url
393
+ self.io_device = io_device
394
+ self.setSourceDevice(self.io_device, self.source_url)
395
+
396
+ def play_file(self, path: Path, position: int = 0) -> None:
397
+ """Play a regular video file.
398
+
399
+ Args:
400
+ path: The file path to the video file to play.
401
+ position: The position to start playback from in milliseconds.
402
+ """
264
403
  self.play_video(
265
- self.set_source_device,
404
+ position=position,
266
405
  io_device=PyQFile(path),
267
406
  source_url=QUrl.fromLocalFile(path),
268
407
  )
269
408
 
270
- def set_source_device(self, io_device: PyQIODevice, source_url: QUrl) -> None:
271
- """Play the video."""
272
- self.source_url = source_url
273
- self.io_device = io_device
274
- self.setSourceDevice(self.io_device, self.source_url)
409
+ def play_encrypted_file(
410
+ self, path: Path, aes_gcm: AESGCM, position: int = 0
411
+ ) -> None:
412
+ """Play an encrypted video file.
413
+
414
+ Args:
415
+ path: The file path to the encrypted video file to play.
416
+ aes_gcm: The AES-GCM cipher instance for decryption.
417
+ position: The position to start playback from in milliseconds.
418
+ """
419
+ self.play_video(
420
+ position=position,
421
+ io_device=EncryptedPyQFile(path, aes_gcm),
422
+ source_url=QUrl.fromLocalFile(path),
423
+ )
@@ -26,11 +26,10 @@ class Notification(Toast): # type: ignore[misc]
26
26
  The notification is shown in the top middle of the screen.
27
27
 
28
28
  Args:
29
- parent (QWidget): The parent widget.
30
- title (str): The title of the notification.
31
- text (str): The text of the notification.
32
- icon (ToastIcon, optional): The icon of the notification.
33
- duration (int, optional): The duration of the notification in milliseconds.
29
+ title: The title of the notification.
30
+ text: The text of the notification.
31
+ icon: The icon of the notification. Defaults to INFORMATION.
32
+ duration: The duration of the notification in milliseconds. Defaults to 10000.
34
33
  """
35
34
  super().__init__(QApplication.activeWindow())
36
35
  self.setDuration(duration)
@@ -39,17 +38,39 @@ class Notification(Toast): # type: ignore[misc]
39
38
  self.set_text(text)
40
39
 
41
40
  def set_title(self, title: str) -> None:
42
- """Set the title of the notification."""
41
+ """Set the title of the notification.
42
+
43
+ Truncates the title to fit within half the window width before setting it.
44
+
45
+ Args:
46
+ title: The title text to set.
47
+ """
43
48
  title = self.str_to_half_window_width(title)
44
49
  self.setTitle(title)
45
50
 
46
51
  def set_text(self, text: str) -> None:
47
- """Set the text of the notification."""
52
+ """Set the text of the notification.
53
+
54
+ Truncates the text to fit within half the window width before setting it.
55
+
56
+ Args:
57
+ text: The notification text to set.
58
+ """
48
59
  text = self.str_to_half_window_width(text)
49
60
  self.setText(text)
50
61
 
51
62
  def str_to_half_window_width(self, string: str) -> str:
52
- """Truncate the string to the width of the active window."""
63
+ """Truncate the string to the width of the active window.
64
+
65
+ Calculates half the width of the active window and truncates the string
66
+ to fit within that width. Falls back to 500 pixels if no active window.
67
+
68
+ Args:
69
+ string: The string to truncate.
70
+
71
+ Returns:
72
+ The truncated string that fits within half the window width.
73
+ """
53
74
  main_window = QApplication.activeWindow()
54
75
  width = main_window.width() / 2 if main_window is not None else 500
55
76
  width = int(width)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: winipedia-utils
3
- Version: 0.1.39
3
+ Version: 0.1.41
4
4
  Summary: A package with many utility functions
5
5
  License: MIT
6
6
  Author: Winipedia
@@ -42,20 +42,20 @@ winipedia_utils/projects/project.py,sha256=2nz1Hh51A-shjgdPCgiDw-ODrVtOtiHEHQnMP
42
42
  winipedia_utils/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
43
43
  winipedia_utils/pyside/__init__.py,sha256=knliQxknKWqxCEfxlI1K02OtbBfnTkYUZFqyYcLJNDI,52
44
44
  winipedia_utils/pyside/core/__init__.py,sha256=5VmASY9rUuZ8_PeZt8KMqnZUGxhsnE0EKbDhCIXj6Gk,57
45
- winipedia_utils/pyside/core/py_qiodevice.py,sha256=YPBeEyLJizQPdb-c45XKmmqUIKe3iFZyJQHdgp8Lqfk,3182
45
+ winipedia_utils/pyside/core/py_qiodevice.py,sha256=NE5PuvwldxoBV0XUD0vsdEqrwloU7Tujde0GvDpfZak,15789
46
46
  winipedia_utils/pyside/ui/__init__.py,sha256=h5NJ6WyveMMqxIgt0x65_mqndtncxgNmx5816KD3ubU,55
47
47
  winipedia_utils/pyside/ui/base/__init__.py,sha256=p-maJQh7gYbXwhkqKU4wL6UNGzRAy988JP8_qLoTZDk,24
48
- winipedia_utils/pyside/ui/base/base.py,sha256=ru-CC-y9KQCHqkVHWGmV7ovIe4D149vBjpsNRPyoVAY,4146
48
+ winipedia_utils/pyside/ui/base/base.py,sha256=GthjIwaL-4n837TzK_sJNYDUzWu7Uc8z84CwGwzI_cI,5889
49
49
  winipedia_utils/pyside/ui/pages/__init__.py,sha256=p-maJQh7gYbXwhkqKU4wL6UNGzRAy988JP8_qLoTZDk,24
50
50
  winipedia_utils/pyside/ui/pages/base/__init__.py,sha256=p-maJQh7gYbXwhkqKU4wL6UNGzRAy988JP8_qLoTZDk,24
51
- winipedia_utils/pyside/ui/pages/base/base.py,sha256=9pt0Hsewo2q_sMu6xI5grF0dcoazRL73EiML8zKOzuE,2059
52
- winipedia_utils/pyside/ui/pages/browser.py,sha256=SzX356snnmEdzCrtIXl5mPaUkM1-_REfUurOotpVIkY,696
53
- winipedia_utils/pyside/ui/pages/player.py,sha256=PPmUNejMibKeNUm-a-1VA6i14G743YFxuxem0NZzFz4,795
51
+ winipedia_utils/pyside/ui/pages/base/base.py,sha256=-bn9rLc9hc98SZxlR7ohKZ1RU4GhccHG96462aqZ0CE,2624
52
+ winipedia_utils/pyside/ui/pages/browser.py,sha256=bAk3WYcn_4oRTQtkKIbf9_aJUZnRU9ZF9QkvhLVG7Nc,886
53
+ winipedia_utils/pyside/ui/pages/player.py,sha256=pE_DmOpiaFRnrtdxlSidg7eEo89pFwaOfLjpZqpu2OE,2861
54
54
  winipedia_utils/pyside/ui/widgets/__init__.py,sha256=p-maJQh7gYbXwhkqKU4wL6UNGzRAy988JP8_qLoTZDk,24
55
- winipedia_utils/pyside/ui/widgets/browser.py,sha256=5oUyQKYOYu9Zzx6adbewTtAIfnn_RVs8CLoX3rU2g28,6160
56
- winipedia_utils/pyside/ui/widgets/clickable_widget.py,sha256=nUZ7ngPFQ7utJuq9A-72TeT1wqCt_xubAxBg3xQSL64,856
57
- winipedia_utils/pyside/ui/widgets/media_player.py,sha256=jcAGeZo3ey177K2jAmqg1LOyARzn_5z26krZGKVj99c,10909
58
- winipedia_utils/pyside/ui/widgets/notification.py,sha256=_zBq9Q6SDFu3fV2AkbvyCNQmOEOy1l9mKOFAAdyteg4,1941
55
+ winipedia_utils/pyside/ui/widgets/browser.py,sha256=0wZ13XE_i2HV3fT6YZqjE7U_VcNv-jYPgifFCMvHQB0,8411
56
+ winipedia_utils/pyside/ui/widgets/clickable_widget.py,sha256=TIQsLYnzgo7hyVjq_mKxo7IoSdwb20qt6JgkJj59ZVE,1779
57
+ winipedia_utils/pyside/ui/widgets/media_player.py,sha256=09PM6XamO_NivAqLW4ZhXnbFSs8vSVGtQkERqeEkwjs,15924
58
+ winipedia_utils/pyside/ui/widgets/notification.py,sha256=vtD3KZvQoFWYat_poDZCywsVNciScFeA8dGC1Bih2so,2532
59
59
  winipedia_utils/pyside/ui/windows/__init__.py,sha256=p-maJQh7gYbXwhkqKU4wL6UNGzRAy988JP8_qLoTZDk,24
60
60
  winipedia_utils/pyside/ui/windows/base/__init__.py,sha256=p-maJQh7gYbXwhkqKU4wL6UNGzRAy988JP8_qLoTZDk,24
61
61
  winipedia_utils/pyside/ui/windows/base/base.py,sha256=httMbYu5QiHevD_2SrUTLnFat9nsvSNAtjTL84tRUao,1496
@@ -91,7 +91,7 @@ winipedia_utils/testing/tests/base/utils/utils.py,sha256=dUPDrgAxlfREQb33zz23Mfz
91
91
  winipedia_utils/testing/tests/conftest.py,sha256=8RounBlI8Jq1aLaLNpv84MW4ne8Qq0aavQextDOp5ng,920
92
92
  winipedia_utils/text/__init__.py,sha256=j2bwtK6kyeHI6SnoBjpRju0C1W2n2paXBDlNjNtaUxA,48
93
93
  winipedia_utils/text/string.py,sha256=1jbBftlgxffGgSlPnQh3aRPIr8XekEwpSenjFCW6JyM,3478
94
- winipedia_utils-0.1.39.dist-info/LICENSE,sha256=3PrKJ2CWNrnyyHaC_r0wPDSukVWgmjOxHr__eQVH7cw,1087
95
- winipedia_utils-0.1.39.dist-info/METADATA,sha256=PEZW6MkX2O4ZuDOXmfWUuhYLriJJQjGbOqKmvL8hOS4,12576
96
- winipedia_utils-0.1.39.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
97
- winipedia_utils-0.1.39.dist-info/RECORD,,
94
+ winipedia_utils-0.1.41.dist-info/LICENSE,sha256=3PrKJ2CWNrnyyHaC_r0wPDSukVWgmjOxHr__eQVH7cw,1087
95
+ winipedia_utils-0.1.41.dist-info/METADATA,sha256=jPd3kqsgclQyiazoqQv2bcs25VFA7_chKzQrwtTFAF4,12576
96
+ winipedia_utils-0.1.41.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
97
+ winipedia_utils-0.1.41.dist-info/RECORD,,