Skip to content

Commit 61c0539

Browse files
Merge remote-tracking branch 'upstream/main'
2 parents 024adfa + 39c6dfe commit 61c0539

File tree

7 files changed

+122
-56
lines changed

7 files changed

+122
-56
lines changed

LICENSE.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## License
22

3-
BSD 3-Clause License (Non-Commercial Use with Commercial Use by Permission)
3+
Modified BSD 3-Clause License (Non-Commercial Use with Commercial Use by Permission)
44

55
Copyright (c) 2025, Christopher Jacobs
66
All rights reserved.
@@ -19,13 +19,23 @@ Redistribution and use in source and binary forms, with or without modification,
1919
- A statement that the Software is used under the terms of this license.
2020
- If the Software is used in a product, application, or publicly visible project, a credit must be included in any user-facing "Credits", "About", or similar section, clearly identifying the Software and the original author.
2121

22-
5. **Commercial Use**: Commercial use of this software is prohibited without explicit, prior written permission from the copyright holder. This includes, but is not limited to:
22+
5. **Commercial Use**: Commercial use of this Software is prohibited without explicit, prior written permission from the copyright holder. Commercial use includes, but is not limited to:
23+
2324
- Using the Software or derivative works in products or services for which payment, subscription, or compensation is requested.
2425
- Offering the Software or services derived from it in exchange for **donations**, **pay-what-you-want models**, or **any other form of voluntary or suggested payment**, if payment is mandatory for access.
26+
- Permission must be obtained prior to any commercial distribution or use.
27+
28+
Requests for commercial use must include a brief description of the intended use and a means of contact, and must be submitted to:
29+
30+
- **Email:** [email protected]
31+
- **Discord:** @chrisbastion
32+
33+
6. **Non-Commercial Redistribution Notification**:
34+
Redistribution of this Software or derivative works for **non-commercial purposes** requires notification to the copyright holder.
2535

26-
6. **Notification Requirement**: Use of this Software under the non-commercial terms, including redistribution or creation of derivative works, requires notification to the original author. This notification must include a brief description of the intended use and a means of contact, and must be sent to:
36+
Such notification must include a brief description of the intended use and a means of contact, and must be sent to:
2737

28-
- Email: **[email protected]**
29-
- Discord: **@chrisbastion**
38+
- **Email:** [email protected]
39+
- **Discord:** @chrisbastion
3040

3141
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

main-ui/devices/abstract_device.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,4 +425,6 @@ def startup_init(self, include_wifi):
425425
def might_require_surface_format_conversion(self):
426426
pass
427427

428-
428+
@abstractmethod
429+
def perform_sdcard_ro_check(self):
430+
pass

main-ui/devices/device.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,6 @@ def startup_init(include_wifi):
469469
def might_require_surface_format_conversion():
470470
return Device._impl.might_require_surface_format_conversion()
471471

472+
@staticmethod
473+
def perform_sdcard_ro_check():
474+
return Device._impl.perform_sdcard_ro_check()

main-ui/devices/device_common.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import socket
55
import subprocess
66
import sys
7+
import tempfile
78
import time
89
from audio.audio_player_none import AudioPlayerNone
910
from controller.controller_inputs import ControllerInput
@@ -469,3 +470,16 @@ def _load_system_config(self, config_path, config_if_missing):
469470
os.remove(config_path)
470471
ConfigCopier.ensure_config(config_path, config_if_missing)
471472
self.system_config = SystemConfig(config_path)
473+
474+
475+
def is_filesystem_read_only(self,path="/mnt/SDCARD"):
476+
try:
477+
with tempfile.NamedTemporaryFile(dir=path, delete=True):
478+
pass
479+
return False
480+
except OSError:
481+
return True
482+
483+
def perform_sdcard_ro_check(self):
484+
if self.is_filesystem_read_only("/mnt/SDCARD"):
485+
Display.display_message("Warning: /mnt/SDCARD is read-only. Please check your SD card.", duration_ms=10000)

main-ui/devices/muos/muos_device.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,3 +379,6 @@ def supports_qoi(self):
379379

380380
def keep_running_on_error(self):
381381
return False
382+
383+
def perform_sdcard_ro_check(self):
384+
PyUiLogger.get_logger().info("MUOS Device does not check for read-only SD card status.")

main-ui/mainui.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ def main():
233233
Controller.init()
234234
Language.init()
235235

236+
Device.perform_sdcard_ro_check()
237+
236238
check_for_msg_display(args)
237239
check_for_msg_display_realtime(args)
238240
check_for_msg_display_socket_based(args)

main-ui/utils/logger.py

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -28,59 +28,91 @@ def init(cls, log_dir, logger_name):
2828
if cls._logger is not None:
2929
return cls._logger
3030

31-
cls.rotate_logs(log_dir) # Ensure logs are rotated before initializing logger
32-
33-
logger = logging.getLogger(logger_name)
34-
logger.setLevel(logging.DEBUG)
35-
36-
if not logger.handlers:
37-
formatter = logging.Formatter(
38-
"%(asctime)s - %(filename)s:%(lineno)d - %(funcName)s - %(levelname)s - %(message)s"
39-
)
40-
41-
# Console handler
42-
console_handler = logging.StreamHandler()
43-
console_handler.setLevel(logging.DEBUG)
44-
console_handler.setFormatter(formatter)
45-
console_handler = logging.StreamHandler(sys.__stdout__)
46-
47-
# File handler
48-
file_handler = RotatingFileHandler(os.path.join(log_dir,"pyui.log"),
49-
maxBytes=1024 * 1024, # 1MB file size limit
50-
backupCount=5 # Keep up to 5 backup files)
51-
)
52-
file_handler.setLevel(logging.DEBUG)
53-
file_handler.setFormatter(formatter)
54-
55-
logger.addHandler(console_handler)
56-
logger.addHandler(file_handler)
57-
58-
# Redirect stdout and stderr to logger
59-
sys.stdout = StreamToLogger(logger, logging.INFO, sys.__stdout__)
60-
sys.stderr = StreamToLogger(logger, logging.ERROR, sys.__stderr__)
61-
62-
cls._logger = logger
63-
return cls._logger
31+
try:
32+
cls.rotate_logs(log_dir) # Ensure logs are rotated before initializing logger
33+
34+
logger = logging.getLogger(logger_name)
35+
logger.setLevel(logging.DEBUG)
36+
37+
if not logger.handlers:
38+
formatter = logging.Formatter(
39+
"%(asctime)s - %(filename)s:%(lineno)d - %(funcName)s - %(levelname)s - %(message)s"
40+
)
41+
42+
# Console handler (explicit stdout)
43+
console_handler = logging.StreamHandler(sys.__stdout__)
44+
console_handler.setLevel(logging.DEBUG)
45+
console_handler.setFormatter(formatter)
46+
47+
# File handler
48+
file_handler = RotatingFileHandler(
49+
os.path.join(log_dir, "pyui.log"),
50+
maxBytes=1024 * 1024,
51+
backupCount=5
52+
)
53+
file_handler.setLevel(logging.DEBUG)
54+
file_handler.setFormatter(formatter)
55+
56+
logger.addHandler(console_handler)
57+
logger.addHandler(file_handler)
58+
59+
# Redirect stdout/stderr
60+
sys.stdout = StreamToLogger(logger, logging.INFO, sys.__stdout__)
61+
sys.stderr = StreamToLogger(logger, logging.ERROR, sys.__stderr__)
62+
63+
cls._logger = logger
64+
return logger
65+
66+
except Exception as e:
67+
# --- Fallback logger ---
68+
fallback = logging.getLogger(f"{logger_name}_fallback")
69+
fallback.setLevel(logging.DEBUG)
70+
71+
if not fallback.handlers:
72+
formatter = logging.Formatter(
73+
"%(asctime)s - %(levelname)s - %(message)s"
74+
)
75+
76+
out_handler = logging.StreamHandler(sys.__stdout__)
77+
err_handler = logging.StreamHandler(sys.__stderr__)
78+
79+
out_handler.setLevel(logging.INFO)
80+
err_handler.setLevel(logging.ERROR)
81+
82+
out_handler.setFormatter(formatter)
83+
err_handler.setFormatter(formatter)
84+
85+
fallback.addHandler(out_handler)
86+
fallback.addHandler(err_handler)
87+
88+
fallback.error("Logger initialization failed, using fallback logger", exc_info=True)
89+
90+
cls._logger = fallback
91+
return fallback
92+
6493

6594
@staticmethod
6695
def rotate_logs(log_dir):
67-
# Perform log rotation before initializing the logger
68-
log_path = os.path.join(log_dir,"pyui.log")
69-
backup_path = os.path.join(log_dir,"pyui.log.5")
70-
71-
# Rotate logs manually before starting
72-
if os.path.exists(backup_path):
73-
os.remove(backup_path)
74-
for i in range(4, 0, -1):
75-
src = f"{log_path}.{i}" if i > 1 else log_path
76-
dest = f"{log_path}.{i + 1}"
77-
if os.path.exists(src):
78-
os.rename(src, dest)
79-
80-
# Optionally, delete the pyui-5.log if it exists
81-
if os.path.exists(f"{log_path}.5"):
82-
os.remove(f"{log_path}.5")
83-
96+
try:
97+
# Perform log rotation before initializing the logger
98+
log_path = os.path.join(log_dir,"pyui.log")
99+
backup_path = os.path.join(log_dir,"pyui.log.5")
100+
101+
# Rotate logs manually before starting
102+
if os.path.exists(backup_path):
103+
os.remove(backup_path)
104+
for i in range(4, 0, -1):
105+
src = f"{log_path}.{i}" if i > 1 else log_path
106+
dest = f"{log_path}.{i + 1}"
107+
if os.path.exists(src):
108+
os.rename(src, dest)
109+
110+
# Optionally, delete the pyui-5.log if it exists
111+
if os.path.exists(f"{log_path}.5"):
112+
os.remove(f"{log_path}.5")
113+
except Exception as e:
114+
print(f"Error rotating logs (likely means RO file system): {e}")
115+
84116
@classmethod
85117
def get_logger(cls):
86118
return cls._logger

0 commit comments

Comments
 (0)