Skip to content

Commit

Permalink
added find sdcard mountpoint feature for macos and linux
Browse files Browse the repository at this point in the history
  • Loading branch information
qlrd committed Oct 25, 2024
1 parent 871bbe7 commit 3084c34
Showing 1 changed file with 136 additions and 22 deletions.
158 changes: 136 additions & 22 deletions src/app/screens/base_screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
if sys.platform.startswith("win32"):
import win32file # pylint: disable=import-error

Check warning on line 48 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L48

Added line #L48 was not covered by tests

if sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
import subprocess


class BaseScreen(Screen, Trigger):
"""Main screen is the 'Home' page"""
Expand Down Expand Up @@ -306,7 +309,9 @@ def on_size(instance, value):
file_chooser.id = f"{wid}_chooser"
file_chooser.dirselect = True

Check warning on line 310 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L308-L310

Added lines #L308 - L310 were not covered by tests

self.detect_usb_windows(file_chooser=file_chooser, btn=btn)
self.on_get_removable_drives_linux(file_chooser=file_chooser, btn=btn)
self.on_get_removable_drives_macos(file_chooser=file_chooser, btn=btn)
self.on_get_removable_drives_windows(file_chooser=file_chooser, btn=btn)

Check warning on line 314 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L312-L314

Added lines #L312 - L314 were not covered by tests

# pytlint: disable=unused-argument
def on_selection(fc, selection):
Expand All @@ -318,7 +323,112 @@ def on_selection(fc, selection):
self.ids[box.id].add_widget(file_chooser)
self.ids[file_chooser.id] = WeakProxy(file_chooser)

Check warning on line 324 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L322-L324

Added lines #L322 - L324 were not covered by tests

def detect_usb_windows(self, file_chooser, btn):
def on_get_removable_drives_linux(self, file_chooser, btn):
"""
Linux put their removable drives on /mnt or /media
and to get them is necessary to use lsblk
"""
if sys.platform == "linux":
drive_list = []

Check warning on line 332 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L332

Added line #L332 was not covered by tests

# Use the 'lsblk' command to list block devices and their mount points
try:

Check warning on line 335 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L335

Added line #L335 was not covered by tests

# pylint: disable=possibly-used-before-assignment
result = subprocess.run(

Check warning on line 338 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L338

Added line #L338 was not covered by tests
["lsblk", "-P", "-o", "NAME,TYPE,RM,MOUNTPOINT"],
capture_output=True,
text=True,
check=True,
)
lines = result.stdout.split("\n")

Check warning on line 344 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L344

Added line #L344 was not covered by tests

# Process the output to find removable devices
for line in lines:
# Parse key-value pairs (lsblk -P outputs in NAME="value" format)
if 'RM="1"' in line and 'TYPE="part"' in line:
# Split by spaces and parse each key-value pair
attributes = {}
parts = line.split()

Check warning on line 352 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L351-L352

Added lines #L351 - L352 were not covered by tests

for part in parts:
key, value = part.split("=", 1)
attributes[key] = value.strip('"')

Check warning on line 356 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L355-L356

Added lines #L355 - L356 were not covered by tests

# Check if the device is mounted
if "MOUNTPOINT" in attributes and attributes["MOUNTPOINT"]:
drive_list.append(attributes["MOUNTPOINT"])

Check warning on line 360 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L360

Added line #L360 was not covered by tests

file_chooser.path = drive_list[0]
copy = self.translate("Copy firmware to")
btn.text = f"{copy} {file_chooser.path}"

Check warning on line 364 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L362-L364

Added lines #L362 - L364 were not covered by tests

except subprocess.CalledProcessError as e:
exc = RuntimeError(f"Error detecting removable drives:\n{e}")
self.redirect_exception(exception=exc)

Check warning on line 368 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L366-L368

Added lines #L366 - L368 were not covered by tests

def on_get_removable_drives_macos(self, file_chooser, btn):
"""
MacOS put their removable drives on /dev/disk and mounted on /Volumes
and to get them is necessary to use diskutil
"""
if sys.platform == "darwin":
drive_list = []

Check warning on line 376 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L376

Added line #L376 was not covered by tests

try:

Check warning on line 378 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L378

Added line #L378 was not covered by tests
# Use 'diskutil' to list all disks, including external ones
result = subprocess.run(

Check warning on line 380 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L380

Added line #L380 was not covered by tests
["diskutil", "info", "-all"],
capture_output=True,
text=True,
check=True,
)

# diskutil separate blocks with a bunch of *
blocks = result.stdout.split("**********")

Check warning on line 388 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L388

Added line #L388 was not covered by tests

for block in blocks:
# Process the output to find external (removable) drives
lines = block.split("\n")
node = None
fat32 = False
external = False
mounted = False
mnt = False

Check warning on line 397 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L392-L397

Added lines #L392 - L397 were not covered by tests
for line in lines:
line = line.strip()

Check warning on line 399 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L399

Added line #L399 was not covered by tests

# Identify if a new device starts (e.g., /dev/disk2)
if "Device Node" in line and "/dev/disk" in line:
node = line.split("Device Node:")[-1].strip()

Check warning on line 403 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L403

Added line #L403 was not covered by tests

# check if it is FAT32 (the supported by krux devices)
if "File System Personality" in line and "FAT32" in line:
fat32 = True

Check warning on line 407 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L407

Added line #L407 was not covered by tests

# Look for external devices
if "Device Location" in line and "External" in line:
external = True

Check warning on line 411 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L411

Added line #L411 was not covered by tests

# Find the mount point, if it exists
if "Mounted" in line and "Yes" in line:
mounted = True

Check warning on line 415 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L415

Added line #L415 was not covered by tests

if "Mount Point" in line:
mnt = line.split("Mount Point:")[-1].strip()

Check warning on line 418 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L418

Added line #L418 was not covered by tests

if node and fat32 and external and mounted and mnt:
drive_list.append(mnt)

Check warning on line 421 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L421

Added line #L421 was not covered by tests

file_chooser.path = drive_list[0]
copy = self.translate("Copy firmware to")
btn.text = f"{copy} {file_chooser.path}"

Check warning on line 425 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L423-L425

Added lines #L423 - L425 were not covered by tests

except subprocess.CalledProcessError as e:
exc = RuntimeError(f"Error detecting removable drives:\n{e}")
self.redirect_exception(exception=exc)

Check warning on line 429 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L427-L429

Added lines #L427 - L429 were not covered by tests

def on_get_removable_drives_windows(self, file_chooser, btn):
"""
Windows do not show non-C drivers. So to show them
will follow a mixed approach:
Expand All @@ -328,29 +438,33 @@ def detect_usb_windows(self, file_chooser, btn):
python-kivy-how-to-use-filechooser-access-files-outside-c-drive
"""
if sys.platform == "win32":

# the placeholder to where we will find
drive_list = []

Check warning on line 442 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L442

Added line #L442 was not covered by tests

# Get the USB
# pylint: disable=possibly-used-before-assignment
drivebits = win32file.GetLogicalDrives()
for d in range(1, 26):
mask = 1 << d
if drivebits & mask:
# here if the drive is at least there
# pylint: disable=consider-using-f-string
drname = "%c:\\" % chr(ord("A") + d)

# pylint: disable=possibly-used-before-assignment
t = win32file.GetDriveType(drname)
if t == win32file.DRIVE_REMOVABLE:
drive_list.append(drname)

# now gotcha the first
file_chooser.path = drive_list[0]
copy = self.translate("Copy firmware to")
btn.text = f"{copy} {file_chooser.path}"
try:

Check warning on line 444 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L444

Added line #L444 was not covered by tests
# Get the USB
# pylint: disable=possibly-used-before-assignment
drivebits = win32file.GetLogicalDrives()

Check warning on line 447 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L447

Added line #L447 was not covered by tests
for d in range(1, 26):
mask = 1 << d

Check warning on line 449 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L449

Added line #L449 was not covered by tests
if drivebits & mask:
# here if the drive is at least there
# pylint: disable=consider-using-f-string
drname = "%c:\\" % chr(ord("A") + d)

Check warning on line 453 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L453

Added line #L453 was not covered by tests

# pylint: disable=possibly-used-before-assignment
t = win32file.GetDriveType(drname)

Check warning on line 456 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L456

Added line #L456 was not covered by tests
if t == win32file.DRIVE_REMOVABLE:
drive_list.append(drname)

Check warning on line 458 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L458

Added line #L458 was not covered by tests

file_chooser.path = drive_list[0]
copy = self.translate("Copy firmware to")
btn.text = f"{copy} {file_chooser.path}"

Check warning on line 462 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L460-L462

Added lines #L460 - L462 were not covered by tests

# pylint: disable=broad-exception-caught
except Exception as e:
exc = RuntimeError(f"Error detecting removable drives:\n{e}")
self.redirect_exception(exception=exc)

Check warning on line 467 in src/app/screens/base_screen.py

View check run for this annotation

Codecov / codecov/patch

src/app/screens/base_screen.py#L465-L467

Added lines #L465 - L467 were not covered by tests

def redirect_exception(self, exception: Exception):
"""Get an exception and prepare a ErrorScreen rendering"""
Expand Down

0 comments on commit 3084c34

Please sign in to comment.