boris-behav-obs 9.3.5__tar.gz → 9.4__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 (122) hide show
  1. {boris_behav_obs-9.3.5/boris_behav_obs.egg-info → boris_behav_obs-9.4}/PKG-INFO +1 -1
  2. boris_behav_obs-9.4/boris/1.py +45 -0
  3. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/about.py +5 -4
  4. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/config_file.py +1 -1
  5. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/converters.py +1 -3
  6. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/core.py +86 -65
  7. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/dialog.py +4 -25
  8. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/utilities.py +170 -75
  9. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/version.py +2 -2
  10. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4/boris_behav_obs.egg-info}/PKG-INFO +1 -1
  11. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/SOURCES.txt +1 -0
  12. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/pyproject.toml +2 -2
  13. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/LICENSE.TXT +0 -0
  14. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/MANIFEST.in +0 -0
  15. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/README.TXT +0 -0
  16. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/README.md +0 -0
  17. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/__init__.py +0 -0
  18. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/__main__.py +0 -0
  19. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/add_modifier.py +0 -0
  20. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/add_modifier_ui.py +0 -0
  21. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/advanced_event_filtering.py +0 -0
  22. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/analysis_plugins/__init__.py +0 -0
  23. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/analysis_plugins/_latency.py +0 -0
  24. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/analysis_plugins/number_of_occurences.py +0 -0
  25. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/analysis_plugins/number_of_occurences_by_independent_variable.py +0 -0
  26. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/analysis_plugins/time_budget.py +0 -0
  27. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/behav_coding_map_creator.py +0 -0
  28. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/behavior_binary_table.py +0 -0
  29. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/behaviors_coding_map.py +0 -0
  30. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/boris_cli.py +0 -0
  31. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/cmd_arguments.py +0 -0
  32. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/coding_pad.py +0 -0
  33. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/config.py +0 -0
  34. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/connections.py +0 -0
  35. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/converters_ui.py +0 -0
  36. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/cooccurence.py +0 -0
  37. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/core_qrc.py +0 -0
  38. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/core_ui.py +0 -0
  39. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/db_functions.py +0 -0
  40. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/dev.py +0 -0
  41. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/duration_widget.py +0 -0
  42. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/edit_event.py +0 -0
  43. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/edit_event_ui.py +0 -0
  44. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/event_operations.py +0 -0
  45. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/events_cursor.py +0 -0
  46. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/events_snapshots.py +0 -0
  47. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/exclusion_matrix.py +0 -0
  48. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/export_events.py +0 -0
  49. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/export_observation.py +0 -0
  50. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/external_processes.py +0 -0
  51. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/geometric_measurement.py +0 -0
  52. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/gui_utilities.py +0 -0
  53. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/image_overlay.py +0 -0
  54. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/import_observations.py +0 -0
  55. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/irr.py +0 -0
  56. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/latency.py +0 -0
  57. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/measurement_widget.py +0 -0
  58. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/media_file.py +0 -0
  59. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/menu_options.py +0 -0
  60. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/modifier_coding_map_creator.py +0 -0
  61. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/modifiers_coding_map.py +0 -0
  62. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/mpv-1.0.3.py +0 -0
  63. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/mpv.py +0 -0
  64. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/mpv2.py +0 -0
  65. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/observation.py +0 -0
  66. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/observation_operations.py +0 -0
  67. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/observation_ui.py +0 -0
  68. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/observations_list.py +0 -0
  69. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/otx_parser.py +0 -0
  70. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/param_panel.py +0 -0
  71. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/param_panel_ui.py +0 -0
  72. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/player_dock_widget.py +0 -0
  73. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/plot_data_module.py +0 -0
  74. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/plot_events.py +0 -0
  75. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/plot_events_rt.py +0 -0
  76. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/plot_spectrogram_rt.py +0 -0
  77. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/plot_waveform_rt.py +0 -0
  78. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/plugins.py +0 -0
  79. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/portion/__init__.py +0 -0
  80. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/portion/const.py +0 -0
  81. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/portion/dict.py +0 -0
  82. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/portion/func.py +0 -0
  83. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/portion/interval.py +0 -0
  84. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/portion/io.py +0 -0
  85. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/preferences.py +0 -0
  86. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/preferences_ui.py +0 -0
  87. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/project.py +0 -0
  88. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/project_functions.py +0 -0
  89. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/project_import_export.py +0 -0
  90. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/project_ui.py +0 -0
  91. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/qrc_boris.py +0 -0
  92. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/qrc_boris5.py +0 -0
  93. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/select_modifiers.py +0 -0
  94. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/select_observations.py +0 -0
  95. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/select_subj_behav.py +0 -0
  96. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/state_events.py +0 -0
  97. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/subjects_pad.py +0 -0
  98. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/synthetic_time_budget.py +0 -0
  99. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/time_budget_functions.py +0 -0
  100. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/time_budget_widget.py +0 -0
  101. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/transitions.py +0 -0
  102. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/video_equalizer.py +0 -0
  103. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/video_equalizer_ui.py +0 -0
  104. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/video_operations.py +0 -0
  105. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/view_df.py +0 -0
  106. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/view_df_ui.py +0 -0
  107. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris/write_event.py +0 -0
  108. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/dependency_links.txt +0 -0
  109. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/entry_points.txt +0 -0
  110. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/requires.txt +0 -0
  111. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/boris_behav_obs.egg-info/top_level.txt +0 -0
  112. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/setup.cfg +0 -0
  113. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_db_functions.py +0 -0
  114. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_export_observation.py +0 -0
  115. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_irr.py +0 -0
  116. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_observation_gui.py +0 -0
  117. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_otx_parser.py +0 -0
  118. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_preferences_gui.py +0 -0
  119. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_project_functions.py +0 -0
  120. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_time_budget.py +0 -0
  121. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_utilities.py +0 -0
  122. {boris_behav_obs-9.3.5 → boris_behav_obs-9.4}/tests/test_utilities2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boris-behav-obs
3
- Version: 9.3.5
3
+ Version: 9.4
4
4
  Summary: BORIS - Behavioral Observation Research Interactive Software
5
5
  Author-email: Olivier Friard <olivier.friard@unito.it>
6
6
  License-Expression: GPL-3.0-only
@@ -0,0 +1,45 @@
1
+ import time
2
+ from PIL import Image, ImageDraw, ImageFont
3
+ import mpv
4
+
5
+ player = mpv.MPV()
6
+
7
+ player.loop = True
8
+ player.play('/home/olivier/gdrive_sync/src/python/generate_video_test/video1.mp4')
9
+ player.wait_until_playing()
10
+
11
+ font = ImageFont.truetype('DejaVuSans.ttf', 40)
12
+
13
+ overlay = player.create_image_overlay()
14
+
15
+ img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
16
+ d = ImageDraw.Draw(img)
17
+ d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
18
+ #d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
19
+
20
+ pos = 100
21
+
22
+ overlay.update(img, pos=(2*pos, pos))
23
+
24
+
25
+ while not player.core_idle:
26
+ pass
27
+
28
+
29
+ '''
30
+ for pos in range(0, 500, 5):
31
+ ts = player.time_pos
32
+ if ts is None:
33
+ break
34
+
35
+ img = Image.new('RGBA', (400, 150), (255, 255, 255, 0))
36
+ d = ImageDraw.Draw(img)
37
+ d.text((10, 10), 'Hello World', font=font, fill=(0, 255, 255, 128))
38
+ d.text((10, 60), f't={ts:.3f}', font=font, fill=(255, 0, 255, 255))
39
+
40
+ overlay.update(img, pos=(2*pos, pos))
41
+ time.sleep(0.05)
42
+
43
+
44
+ overlay.remove()
45
+ '''
@@ -31,7 +31,6 @@ from . import config as cfg
31
31
  from . import utilities as util
32
32
 
33
33
 
34
- from PySide6.QtCore import qVersion
35
34
  from PySide6.QtGui import QPixmap
36
35
  from PySide6.QtWidgets import QMessageBox
37
36
 
@@ -41,8 +40,6 @@ def actionAbout_activated(self):
41
40
  About dialog
42
41
  """
43
42
 
44
- import PySide6
45
-
46
43
  programs_versions: list = ["MPV media player"]
47
44
 
48
45
  mpv_lib_version, mpv_lib_file_path, mpv_api_version = util.mpv_lib_version()
@@ -108,6 +105,7 @@ def actionAbout_activated(self):
108
105
  '<a href="https://besjournals.onlinelibrary.wiley.com/doi/full/10.1111/2041-210X.12584">DOI:10.1111/2041-210X.12584</a>'
109
106
  )
110
107
  )
108
+ """
111
109
  n = "\n"
112
110
  current_system = platform.uname()
113
111
  details = (
@@ -123,8 +121,11 @@ def actionAbout_activated(self):
123
121
  f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
124
122
  f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb\n\n"
125
123
  )
124
+ """
125
+
126
+ details = util.get_systeminfo()
126
127
 
127
- details += n.join(programs_versions)
128
+ details += "\n".join(programs_versions)
128
129
  """
129
130
  memory_in_use = f"{utilities.rss_memory_used(self.pid)} Mb" if utilities.rss_memory_used(self.pid) != -1 else "Not available"
130
131
  percent_memory_in_use = (f"({utilities.rss_memory_percent_used(self.pid):.1f} % of total memory)"
@@ -72,7 +72,7 @@ def read(self):
72
72
  if not isinstance(self.saved_state, QByteArray):
73
73
  self.saved_state = None
74
74
 
75
- logging.debug(f"saved state: {self.saved_state}")
75
+ # logging.debug(f"saved state: {self.saved_state}")
76
76
 
77
77
  self.timeFormat = cfg.HHMMSS
78
78
  try:
@@ -22,8 +22,6 @@ This file is part of BORIS.
22
22
 
23
23
  import sys
24
24
  import json
25
- import urllib.error
26
- import urllib.parse
27
25
  import urllib.request
28
26
 
29
27
 
@@ -314,7 +312,7 @@ def load_converters_from_file_repo(self, mode: str):
314
312
  QMessageBox.critical(
315
313
  self,
316
314
  "BORIS",
317
- (f"The code of {converter_name} converter produces an error: " f"<br><b>{sys.exc_info()[1]}</b>"),
315
+ (f"The code of {converter_name} converter produces an error: <br><b>{sys.exc_info()[1]}</b>"),
318
316
  )
319
317
 
320
318
  self.tw_converters.setRowCount(self.tw_converters.rowCount() + 1)
@@ -19,34 +19,31 @@ This file is part of BORIS.
19
19
  along with this program; if not see <http://www.gnu.org/licenses/>.
20
20
 
21
21
  """
22
+ # ruff: noqa: E402
22
23
 
23
24
  import os
24
25
  import sys
26
+ import pathlib as pl
25
27
 
26
- os.environ["PATH"] = os.path.dirname(__file__) + os.sep + "misc" + os.pathsep + os.environ["PATH"]
28
+ # os.environ["PATH"] = os.path.dirname(__file__) + os.sep + "misc" + os.pathsep + os.environ["PATH"]
27
29
 
30
+ os.environ["PATH"] = str(pl.Path(__file__).parent / "misc") + os.pathsep + os.environ["PATH"]
28
31
  sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".")))
29
32
 
30
-
31
33
  import datetime
32
-
33
34
  import json
34
35
  import logging
35
- import pathlib as pl
36
36
  import platform
37
37
  import re
38
38
  import PIL.Image
39
39
  import PIL.ImageEnhance
40
+ from PIL.ImageQt import Image
40
41
  import subprocess
41
-
42
42
  import locale
43
43
  import tempfile
44
44
  import time
45
- import urllib.error
46
- import urllib.parse
47
45
  import urllib.request
48
46
  from typing import Union, Tuple
49
-
50
47
  from decimal import Decimal as dec
51
48
  from decimal import ROUND_DOWN
52
49
  import gzip
@@ -57,7 +54,6 @@ import shutil
57
54
 
58
55
  matplotlib.use("QtAgg")
59
56
 
60
- import PySide6
61
57
  from PySide6.QtCore import (
62
58
  Qt,
63
59
  QPoint,
@@ -66,7 +62,6 @@ from PySide6.QtCore import (
66
62
  QDateTime,
67
63
  QUrl,
68
64
  QAbstractTableModel,
69
- qVersion,
70
65
  QElapsedTimer,
71
66
  QSettings,
72
67
  )
@@ -88,7 +83,28 @@ from PySide6.QtWidgets import (
88
83
  QStyledItemDelegate,
89
84
  QTableWidgetItem,
90
85
  )
91
- from PIL.ImageQt import Image
86
+
87
+
88
+ from . import cmd_arguments
89
+
90
+ # parse command line arguments
91
+ (options, args) = cmd_arguments.parse_arguments()
92
+
93
+ # set logging parameters
94
+ if options.debug:
95
+ logging.basicConfig(
96
+ format="%(asctime)s,%(msecs)d %(module)s l.%(lineno)d %(levelname)s %(message)s",
97
+ datefmt="%H:%M:%S",
98
+ level=logging.DEBUG,
99
+ )
100
+ else:
101
+ logging.basicConfig(
102
+ format="%(asctime)s,%(msecs)d %(message)s",
103
+ datefmt="%H:%M:%S",
104
+ level=logging.INFO,
105
+ )
106
+
107
+ from . import utilities as util
92
108
 
93
109
  from . import dialog
94
110
  from . import gui_utilities
@@ -105,21 +121,15 @@ from . import plot_waveform_rt
105
121
  from . import plot_events_rt
106
122
  from . import plugins
107
123
  from . import project_functions
108
-
109
124
  from . import select_observations
110
125
  from . import subjects_pad
111
126
  from . import version
112
127
  from . import event_operations
113
- from . import cmd_arguments
114
-
115
128
  from . import core_qrc
116
129
  from .core_ui import Ui_MainWindow
117
130
  from . import config as cfg
118
131
  from . import video_operations
119
-
120
132
  from . import project
121
- from . import utilities as util
122
-
123
133
  from . import menu_options as menu_options
124
134
  from . import connections as connections
125
135
  from . import config_file
@@ -128,7 +138,7 @@ from . import observation_operations
128
138
  from . import write_event
129
139
 
130
140
 
131
- # matplotlib.pyplot.switch_backend("Qt5Agg")
141
+ logging.debug("test")
132
142
 
133
143
  __version__ = version.__version__
134
144
  __version_date__ = version.__version_date__
@@ -143,44 +153,13 @@ if util.versiontuple(platform.python_version()) < util.versiontuple(MIN_PYTHON_V
143
153
  if sys.platform == "darwin": # for MacOS
144
154
  os.environ["LC_ALL"] = "en_US.UTF-8"
145
155
 
146
- # parse command line arguments
147
- (options, args) = cmd_arguments.parse_arguments()
148
-
149
- # set logging parameters
150
- if options.debug:
151
- logging.basicConfig(
152
- format="%(asctime)s,%(msecs)d %(module)s l.%(lineno)d %(levelname)s %(message)s",
153
- datefmt="%H:%M:%S",
154
- level=logging.DEBUG,
155
- )
156
- else:
157
- logging.basicConfig(
158
- format="%(asctime)s,%(msecs)d %(message)s",
159
- datefmt="%H:%M:%S",
160
- level=logging.INFO,
161
- )
162
156
 
163
157
  if options.version:
164
158
  print(f"version {__version__} release date: {__version_date__}")
165
159
  sys.exit(0)
166
160
 
167
-
168
161
  logging.debug("BORIS started")
169
- logging.info(f"BORIS version {__version__} release date: {__version_date__}")
170
- logging.info(f"Operating system: {platform.uname().system} {platform.uname().release} {platform.uname().version}")
171
- logging.info(f"CPU: {platform.uname().machine} {platform.uname().processor}")
172
- logging.info(f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})")
173
- logging.info(f"Qt {qVersion()} - PySide {PySide6.__version__}")
174
-
175
-
176
- (r, memory) = util.mem_info()
177
- if not r:
178
- logging.info(
179
- (
180
- f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
181
- f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb"
182
- )
183
- )
162
+ logging.info(util.get_systeminfo())
184
163
 
185
164
 
186
165
  def excepthook(exception_type, exception_value, traceback_object):
@@ -1362,7 +1341,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
1362
1341
  try:
1363
1342
  last_version = urllib.request.urlopen(version_URL).read().strip().decode("utf-8")
1364
1343
  except Exception:
1365
- QMessageBox.warning(self, cfg.programName, "Can not check for updates...")
1344
+ QMessageBox.warning(self, cfg.programName, "Can not check for updates. Check your connection.")
1366
1345
  return
1367
1346
 
1368
1347
  # record check timestamp
@@ -5709,21 +5688,54 @@ def main():
5709
5688
 
5710
5689
  locale.setlocale(locale.LC_NUMERIC, "C")
5711
5690
 
5712
- # splashscreen
5713
- # no splashscreen for Mac because it can mask the first use dialog box
5714
-
5715
- if (not options.nosplashscreen) and (sys.platform != "darwin"):
5716
- start = time.time()
5717
- splash = QSplashScreen(QPixmap(":/splash"))
5718
- splash.show()
5719
- splash.raise_()
5720
- app.processEvents()
5721
- while time.time() - start < 1:
5722
- time.sleep(0.001)
5723
-
5724
5691
  # check FFmpeg
5725
5692
  ret, msg = util.check_ffmpeg_path()
5726
5693
  if not ret:
5694
+ if sys.platform.startswith("win"):
5695
+ QMessageBox.warning(
5696
+ None,
5697
+ cfg.programName,
5698
+ "FFmpeg is not available.<br>It will be downloaded",
5699
+ QMessageBox.Ok | QMessageBox.Default,
5700
+ QMessageBox.NoButton,
5701
+ )
5702
+
5703
+ # download ffmpeg and ffprobe from https://github.com/boris-behav-obs/boris-behav-obs.github.io/releases/download/files/
5704
+ url = "https://github.com/boris-behav-obs/boris-behav-obs.github.io/releases/download/files/"
5705
+
5706
+ # search where to download ffmpeg
5707
+ ffmpeg_dir = pl.Path(__file__).parent / "misc"
5708
+
5709
+ logging.debug(f"{ffmpeg_dir=}")
5710
+
5711
+ if not ffmpeg_dir.is_dir():
5712
+ logging.info(f"Creating {ffmpeg_dir} directory")
5713
+ ffmpeg_dir.mkdir(parents=True, exist_ok=True)
5714
+
5715
+ for file_ in ("ffmpeg.exe", "ffprobe.exe"):
5716
+ local_filename = ffmpeg_dir / file_
5717
+ logging.info(f"Downloading {file_}...")
5718
+ try:
5719
+ urllib.request.urlretrieve(url + file_, local_filename)
5720
+ except Exception:
5721
+ logging.critical("The FFmpeg program can not be downloaded! Check your connection.")
5722
+ QMessageBox.warning(
5723
+ None,
5724
+ cfg.programName,
5725
+ "The FFmpeg program can not be downloaded!\nCheck your connection.",
5726
+ QMessageBox.Ok | QMessageBox.Default,
5727
+ QMessageBox.NoButton,
5728
+ )
5729
+ sys.exit(3)
5730
+
5731
+ logging.info(f"File downloaded as {local_filename}")
5732
+
5733
+ # re-test for ffmpeg
5734
+ ret, msg = util.check_ffmpeg_path()
5735
+
5736
+ if ret:
5737
+ ffmpeg_bin = msg
5738
+ else:
5727
5739
  QMessageBox.critical(
5728
5740
  None,
5729
5741
  cfg.programName,
@@ -5732,8 +5744,17 @@ def main():
5732
5744
  QMessageBox.NoButton,
5733
5745
  )
5734
5746
  sys.exit(3)
5735
- else:
5736
- ffmpeg_bin = msg
5747
+
5748
+ # splashscreen
5749
+ # no splashscreen for Mac because it can mask the first use dialog box
5750
+ if (not options.nosplashscreen) and (sys.platform != "darwin"):
5751
+ start = time.time()
5752
+ splash = QSplashScreen(QPixmap(":/splash"))
5753
+ splash.show()
5754
+ splash.raise_()
5755
+ app.processEvents()
5756
+ while time.time() - start < 1:
5757
+ time.sleep(0.001)
5737
5758
 
5738
5759
  app.setApplicationName(cfg.programName)
5739
5760
 
@@ -26,7 +26,6 @@ import logging
26
26
  import math
27
27
  import pathlib as pl
28
28
  import platform
29
- import subprocess
30
29
  import sys
31
30
  import traceback
32
31
  from typing import Union
@@ -98,29 +97,11 @@ def global_error_message(exception_type, exception_value, traceback_object):
98
97
  Global error management
99
98
  save error using loggin.critical and stdout
100
99
  """
101
- import PySide6
102
-
103
- error_text: str = (
104
- f"BORIS version: {version.__version__}\n"
105
- f"OS: {platform.uname().system} {platform.uname().release} {platform.uname().version}\n"
106
- f"CPU: {platform.uname().machine} {platform.uname().processor}\n"
107
- f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})\n"
108
- f"Qt {qVersion()} - PySide {PySide6.__version__}\n"
109
- f"MPV library version: {util.mpv_lib_version()[0]}\n"
110
- f"MPV API version: {util.mpv_lib_version()[2]}\n"
111
- f"MPV library file path: {util.mpv_lib_version()[1]}\n\n"
112
- f"Error succeded at {dt.datetime.now():%Y-%m-%d %H:%M}\n\n"
113
- )
114
- error_text += "".join(traceback.format_exception(exception_type, exception_value, traceback_object))
115
100
 
116
- # system info
117
- systeminfo = ""
118
- if sys.platform.startswith("win"):
119
- systeminfo = subprocess.getoutput("systeminfo")
120
- if sys.platform.startswith("linux"):
121
- systeminfo = subprocess.getoutput("cat /etc/*rel*; uname -a")
122
-
123
- error_text += f"\n\nSystem info\n===========\n\n{systeminfo}"
101
+ error_text = "\n\nSystem info\n===========\n\n"
102
+ error_text += util.get_systeminfo()
103
+ error_text += f"Error succeded at {dt.datetime.now():%Y-%m-%d %H:%M}\n\n"
104
+ error_text += "".join(traceback.format_exception(exception_type, exception_value, traceback_object))
124
105
 
125
106
  # write to stdout
126
107
  logging.critical(error_text)
@@ -129,8 +110,6 @@ def global_error_message(exception_type, exception_value, traceback_object):
129
110
  try:
130
111
  with open(pl.Path.home() / "boris_error.log", "w") as f_error:
131
112
  f_error.write(error_text)
132
- f_error.write("\nSystem info:\n")
133
- f_error.write(systeminfo + "\n")
134
113
  except Exception:
135
114
  logging.critical(f"Impossible to write to {pl.Path.home() / 'boris_error.log'}")
136
115
 
@@ -19,54 +19,101 @@ Copyright 2012-2025 Olivier Friard
19
19
  MA 02110-1301, USA.
20
20
  """
21
21
 
22
+ from decimal import Decimal as dec
23
+ from decimal import getcontext, ROUND_DOWN
24
+ from hachoir.metadata import extractMetadata
25
+ from hachoir.parser import createParser
26
+ from shutil import copyfile
27
+ from typing import Union, Tuple
22
28
  import csv
29
+ import datetime
23
30
  import datetime as dt
31
+ import exifread
24
32
  import json
25
33
  import logging
26
34
  import math
35
+ import numpy as np
27
36
  import os
28
37
  import pathlib as pl
38
+ from PIL.ImageQt import Image
39
+ import platform
29
40
  import re
41
+ import shutil
30
42
  import subprocess
31
43
  import sys
32
44
  import urllib.parse
45
+ import urllib.request
33
46
  import wave
34
- import exifread
35
- import datetime
36
- from decimal import Decimal as dec
37
- from decimal import getcontext, ROUND_DOWN
38
- from shutil import copyfile
39
- from typing import Union, Tuple
40
47
 
41
- from hachoir.parser import createParser
42
- from hachoir.metadata import extractMetadata
43
-
44
-
45
- import numpy as np
48
+ from PySide6 import __version__ as pyside6_version
46
49
  from PySide6.QtGui import QPixmap, QImage
47
-
48
- from PIL.ImageQt import Image
50
+ from PySide6.QtCore import qVersion
49
51
 
50
52
  from . import config as cfg
53
+ from . import version
51
54
 
52
- from . import mpv2 as mpv
55
+ logger = logging.getLogger(__name__)
53
56
 
54
- """
55
57
  try:
56
58
  from . import mpv2 as mpv
59
+ except Exception:
60
+ logger.warning("MPV library not found")
57
61
 
58
- # check if MPV API v. 1
59
- # is v. 1 use the old version of mpv.py
60
- try:
61
- if "libmpv.so.1" in mpv.sofile:
62
- from . import mpv as mpv
63
- except AttributeError:
64
- if "mpv-1.dll" in mpv.dll:
65
- from . import mpv as mpv
66
-
67
- except RuntimeError: # libmpv found but version too old
68
- from . import mpv as mpv
69
- """
62
+ if sys.platform.startswith("win"):
63
+ import ctypes
64
+
65
+ ctypes.windll.user32.MessageBoxW(0, "The MPV library was not found!\nIt will be downloaded.", "BORIS", 0)
66
+
67
+ # download libmpv2.dll and ffprobe from https://github.com/boris-behav-obs/boris-behav-obs.github.io/releases/download/files/
68
+
69
+ url = "https://github.com/boris-behav-obs/boris-behav-obs.github.io/releases/download/files/"
70
+
71
+ external_files_dir = ""
72
+ # search where to download libmpv-2.dll
73
+
74
+ external_files_dir = pl.Path(__file__).parent / "misc"
75
+ if not external_files_dir.is_dir():
76
+ logger.info(f"Creating {external_files_dir} directory")
77
+ external_files_dir.mkdir(parents=True, exist_ok=True)
78
+
79
+ logger.info(f"MPV library directory: {external_files_dir}")
80
+
81
+ local_filename = external_files_dir / "libmpv-2.dll"
82
+ logger.info("Downloading libmpv-2.dll...")
83
+ try:
84
+ urllib.request.urlretrieve(url + "libmpv-2.dll", local_filename)
85
+ logger.info(f"File downloaded as {local_filename}")
86
+ except Exception:
87
+ logger.critical("The MPV library can not be downloaded! Check your connection.")
88
+ ctypes.windll.user32.MessageBoxW(0, "The MPV library can not be downloaded!\nCheck your connection.", "BORIS", 0)
89
+ sys.exit(5)
90
+ # reload package
91
+ try:
92
+ from . import mpv2 as mpv
93
+ except Exception:
94
+ logger.warning("MPV library not found after dowloading")
95
+ sys.exit(5)
96
+
97
+ elif sys.platform.startswith("linux"):
98
+ text = (
99
+ "The MPV library was not found!\nInstall it\n\n"
100
+ "With Debian/Ubuntu/Mint:\nsudo apt install libmpv2\n\n"
101
+ "With Fedora:\nsudo dnf install mpv-libs\n\n"
102
+ "With OpenSUSE:\nsudo zypper install mpv\n\n"
103
+ "Arch Linux / Manjaro:\nsudo pacman -S mpv\n\n"
104
+ )
105
+ if shutil.which("zenity") is not None:
106
+ subprocess.run(["zenity", "--error", f"--text={text}"])
107
+ elif shutil.which("kdialog"):
108
+ subprocess.run(["kdialog", "--msgbox", text])
109
+ elif shutil.which("gxmessage"):
110
+ subprocess.run(["gxmessage", text])
111
+ elif shutil.which("xmessage"):
112
+ subprocess.run(["xmessage", text])
113
+
114
+ sys.exit(5)
115
+ else:
116
+ sys.exit(5)
70
117
 
71
118
 
72
119
  def extract_exif_DateTimeOriginal(file_path: str) -> int:
@@ -110,10 +157,10 @@ def extract_video_creation_date(file_path: str) -> int | None:
110
157
  returns the timestamp of the media creation date time with Hachoir
111
158
  """
112
159
 
113
- logging.debug(f"extract_video_creation_date for {file_path}")
160
+ logger.debug(f"extract_video_creation_date for {file_path}")
114
161
 
115
162
  if not pl.Path(file_path).is_file():
116
- logging.debug(f"{file_path} not found")
163
+ logger.debug(f"{file_path} not found")
117
164
  return None
118
165
  try:
119
166
  parser = createParser(file_path)
@@ -140,14 +187,14 @@ def extract_date_time_from_file_name(file_path: str) -> int | None:
140
187
 
141
188
  if matches:
142
189
  if pattern == r"\d{4}-\d{2}-\d{2}_\d{6}":
143
- logging.debug(
190
+ logger.debug(
144
191
  f"extract_date_time_from_file_name timestamp from {file_path}: {int(datetime.datetime.strptime(matches[0], '%Y-%m-%d_%H%M%S').timestamp())}"
145
192
  )
146
193
 
147
194
  return int(datetime.datetime.strptime(matches[0], "%Y-%m-%d_%H%M%S").timestamp())
148
195
 
149
196
  if pattern == r"\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2}":
150
- logging.debug(
197
+ logger.debug(
151
198
  f"extract_date_time_from_file_name timestamp from {file_path}: {int(datetime.datetime.strptime(matches[0], '%Y-%m-%d_%H:%M:%S').timestamp())}"
152
199
  )
153
200
 
@@ -798,9 +845,9 @@ def extract_wav(ffmpeg_bin: str, media_file_path: str, tmp_dir: str) -> str:
798
845
  try:
799
846
  wav = wave.open(media_file_path, "r")
800
847
  wav.close()
801
- logging.debug(f"{media_file_path} is a WAV file. Copying in the temp directory...")
848
+ logger.debug(f"{media_file_path} is a WAV file. Copying in the temp directory...")
802
849
  copyfile(media_file_path, wav_file_path)
803
- logging.debug(f"{media_file_path} copied in {wav_file_path}")
850
+ logger.debug(f"{media_file_path} copied in {wav_file_path}")
804
851
  return str(wav_file_path)
805
852
  except Exception:
806
853
  if wav_file_path.is_file():
@@ -815,7 +862,7 @@ def extract_wav(ffmpeg_bin: str, media_file_path: str, tmp_dir: str) -> str:
815
862
  )
816
863
  out, error = p.communicate()
817
864
  out, error = out.decode("utf-8"), error.decode("utf-8")
818
- logging.debug(f"{out}, {error}")
865
+ logger.debug(f"{out}, {error}")
819
866
 
820
867
  if "does not contain any stream" not in error:
821
868
  if wav_file_path.is_file():
@@ -866,8 +913,8 @@ def seconds_of_day(timestamp: dt.datetime) -> dec:
866
913
  dev: number of seconds since the start of the day
867
914
  """
868
915
 
869
- # logging.debug("function: seconds_of_day")
870
- # logging.debug(f"{timestamp = }")
916
+ # logger.debug("function: seconds_of_day")
917
+ # logger.debug(f"{timestamp = }")
871
918
 
872
919
  t = timestamp.time()
873
920
  return dec(t.hour * 3600 + t.minute * 60 + t.second + t.microsecond / 1000000).quantize(dec("0.001"))
@@ -1200,8 +1247,8 @@ def test_ffmpeg_path(FFmpegPath: str) -> Tuple[bool, str]:
1200
1247
  """
1201
1248
 
1202
1249
  out, error = subprocess.Popen(f'"{FFmpegPath}" -version', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()
1203
- logging.debug(f"test ffmpeg path output: {out}")
1204
- logging.debug(f"test ffmpeg path error: {error}")
1250
+ logger.debug(f"test ffmpeg path output: {out}")
1251
+ logger.debug(f"test ffmpeg path error: {error}")
1205
1252
 
1206
1253
  if (b"avconv" in out) or (b"the Libav developers" in error):
1207
1254
  return False, "Please use FFmpeg from https://www.ffmpeg.org in place of FFmpeg from Libav project."
@@ -1223,43 +1270,24 @@ def check_ffmpeg_path() -> Tuple[bool, str]:
1223
1270
  str: if bool True returns ffmpegpath else returns error message
1224
1271
  """
1225
1272
 
1273
+ # search embedded ffmpeg
1226
1274
  if sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
1227
- ffmpeg_path = pl.Path("")
1228
- # search embedded ffmpeg
1229
- if sys.argv[0].endswith("start_boris.py"):
1230
- ffmpeg_path = pl.Path(sys.argv[0]).resolve().parent / "boris" / "misc" / "ffmpeg"
1231
- if sys.argv[0].endswith("__main__.py"):
1232
- ffmpeg_path = pl.Path(sys.argv[0]).resolve().parent / "misc" / "ffmpeg"
1233
-
1234
- if not ffmpeg_path.is_file():
1235
- # search global ffmpeg
1236
- ffmpeg_path = "ffmpeg"
1237
-
1238
- # test ffmpeg
1239
- r, msg = test_ffmpeg_path(str(ffmpeg_path))
1240
- if r:
1241
- return True, str(ffmpeg_path)
1242
- else:
1243
- return False, "FFmpeg is not available"
1275
+ ffmpeg_executable = pl.Path("ffmpeg")
1276
+ elif sys.platform.startswith("win"):
1277
+ ffmpeg_executable = pl.Path("ffmpeg.exe")
1244
1278
 
1245
- if sys.platform.startswith("win"):
1246
- ffmpeg_path = pl.Path("")
1247
- # search embedded ffmpeg
1248
- if sys.argv[0].endswith("start_boris.py"):
1249
- ffmpeg_path = pl.Path(sys.argv[0]).resolve().parent / "boris" / "misc" / "ffmpeg.exe"
1250
- if sys.argv[0].endswith("__main__.py"):
1251
- ffmpeg_path = pl.Path(sys.argv[0]).resolve().parent / "misc" / "ffmpeg.exe"
1252
-
1253
- if not ffmpeg_path.is_file():
1254
- # search global ffmpeg
1255
- ffmpeg_path = "ffmpeg"
1256
-
1257
- # test ffmpeg
1258
- r, msg = test_ffmpeg_path(str(ffmpeg_path))
1259
- if r:
1260
- return True, str(ffmpeg_path)
1261
- else:
1262
- return False, "FFmpeg is not available"
1279
+ ffmpeg_path = pl.Path(__file__).parent / "misc" / ffmpeg_executable
1280
+
1281
+ if not ffmpeg_path.is_file():
1282
+ # search global ffmpeg
1283
+ ffmpeg_path = ffmpeg_executable
1284
+
1285
+ # test ffmpeg
1286
+ r, msg = test_ffmpeg_path(str(ffmpeg_path))
1287
+ if r:
1288
+ return True, str(ffmpeg_path)
1289
+ else:
1290
+ return False, "FFmpeg is not available"
1263
1291
 
1264
1292
 
1265
1293
  def smart_size_format(n: Union[float, int, str, None]) -> str:
@@ -1281,6 +1309,73 @@ def smart_size_format(n: Union[float, int, str, None]) -> str:
1281
1309
  return f"{n / 1_000_000_000:,.1f} Gb"
1282
1310
 
1283
1311
 
1312
+ def get_systeminfo() -> str:
1313
+ """
1314
+ returns info about the system
1315
+ """
1316
+
1317
+ mpv_lib_version_, mpv_lib_file_path, mpv_api_version = mpv_lib_version()
1318
+
1319
+ system_info = (
1320
+ f"BORIS version: {version.__version__}\n"
1321
+ f"OS: {platform.uname().system} {platform.uname().release} {platform.uname().version}\n"
1322
+ f"CPU: {platform.uname().machine} {platform.uname().processor}\n"
1323
+ f"Python {platform.python_version()} ({'64-bit' if sys.maxsize > 2**32 else '32-bit'})\n"
1324
+ f"Qt {qVersion()} - PySide {pyside6_version}\n"
1325
+ f"MPV library version: {mpv_lib_version_}\n"
1326
+ f"MPV API version: {mpv_api_version}\n"
1327
+ f"MPV library file path: {mpv_lib_file_path}\n\n"
1328
+ )
1329
+
1330
+ r, memory = mem_info()
1331
+ if not r:
1332
+ system_info += (
1333
+ f"Memory (RAM) Total: {memory.get('total_memory', 'Not available'):.2f} Mb "
1334
+ f"Free: {memory.get('free_memory', 'Not available'):.2f} Mb\n\n"
1335
+ )
1336
+
1337
+ return system_info
1338
+
1339
+ """
1340
+ # system info
1341
+ systeminfo = ""
1342
+ if sys.platform.startswith("win"):
1343
+ # systeminfo = subprocess.getoutput("systeminfo")
1344
+ systeminfo = subprocess.run("systeminfo /FO csv /NH", capture_output=True, text=True, encoding="mbcs", shell=True).stdout
1345
+
1346
+ import csv
1347
+ from io import StringIO
1348
+
1349
+ # Parse it as CSV
1350
+ f = StringIO(systeminfo)
1351
+ reader = csv.reader(f)
1352
+ parsed_data = list(reader)[0]
1353
+ # Print specific fields by index
1354
+ info_to_show = ""
1355
+ info_to_show += f"Computer Name: {parsed_data[0]}\n"
1356
+ info_to_show += f"OS Name: {parsed_data[1]}\n"
1357
+ info_to_show += f"OS Version: {parsed_data[2]}\n"
1358
+ info_to_show += f"System Manufacturer: {parsed_data[11]}\n"
1359
+ info_to_show += f"System Model: {parsed_data[12]}\n"
1360
+ info_to_show += f"Processor: {parsed_data[14]}\n"
1361
+ info_to_show += f"Locale: {parsed_data[19]}\n"
1362
+ info_to_show += f"Installed Memory: {parsed_data[22]}\n"
1363
+
1364
+ # info about graphic card
1365
+ graphic_info = subprocess.run(
1366
+ "wmic path win32_videocontroller get name", capture_output=True, text=True, encoding="mbcs", shell=True
1367
+ ).stdout
1368
+ info_to_show += graphic_info.replace("\n", "").replace("Name", "Graphic card model")
1369
+
1370
+ systeminfo = info_to_show
1371
+
1372
+ if sys.platform.startswith("linux"):
1373
+ systeminfo = subprocess.getoutput("cat /etc/*rel*; uname -a")
1374
+
1375
+ return systeminfo
1376
+ """
1377
+
1378
+
1284
1379
  def ffprobe_media_analysis(ffmpeg_bin: str, file_name: str) -> dict:
1285
1380
  """
1286
1381
  analyse video parameters with ffprobe (if available)
@@ -1436,8 +1531,8 @@ def accurate_media_analysis(ffmpeg_bin: str, file_name: str) -> dict:
1436
1531
 
1437
1532
  ffprobe_results = ffprobe_media_analysis(ffmpeg_bin, file_name)
1438
1533
 
1439
- logging.debug(f"file_name: {file_name}")
1440
- logging.debug(f"ffprobe_results: {ffprobe_results}")
1534
+ logger.debug(f"file_name: {file_name}")
1535
+ logger.debug(f"ffprobe_results: {ffprobe_results}")
1441
1536
 
1442
1537
  if ("error" not in ffprobe_results) and (ffprobe_results["bitrate"] is not None):
1443
1538
  return ffprobe_results
@@ -20,5 +20,5 @@ This file is part of BORIS.
20
20
 
21
21
  """
22
22
 
23
- __version__ = "9.3.5"
24
- __version_date__ = "2025-05-12"
23
+ __version__ = "9.4"
24
+ __version_date__ = "2025-05-15"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boris-behav-obs
3
- Version: 9.3.5
3
+ Version: 9.4
4
4
  Summary: BORIS - Behavioral Observation Research Interactive Software
5
5
  Author-email: Olivier Friard <olivier.friard@unito.it>
6
6
  License-Expression: GPL-3.0-only
@@ -3,6 +3,7 @@ MANIFEST.in
3
3
  README.TXT
4
4
  README.md
5
5
  pyproject.toml
6
+ boris/1.py
6
7
  boris/__init__.py
7
8
  boris/__main__.py
8
9
  boris/about.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "boris-behav-obs"
3
- version = "9.3.5"
3
+ version = "9.4"
4
4
  description = "BORIS - Behavioral Observation Research Interactive Software"
5
5
  authors = [{ name="Olivier Friard", email="olivier.friard@unito.it" }]
6
6
  readme = "README.md"
@@ -54,7 +54,7 @@ line-length = 140
54
54
  exclude = ["*_ui.py", "mpv*"]
55
55
 
56
56
  [tool.bumpver]
57
- current_version = "9.3.5"
57
+ current_version = "9.4"
58
58
  version_pattern = "MAJOR.MINOR.PATCH"
59
59
 
60
60
 
File without changes
File without changes