Fix dashcam detection for whole-disk (superfloppy) SD card formats

First real-hardware test revealed the connected dashcam's SD card ships
factory-formatted with no partition table - lsblk reports it as a bare
"disk" with an fstype set directly, never as a child "part". The
previous detection logic only matched type=="part", so it silently
missed this real device entirely.

list_usb_storage_partitions() now also matches whole-disk USB devices
that have no children and an fstype set directly on them, in addition
to the existing partition-table case. Verified against a fixture built
from this dashcam's actual `lsblk -J` output.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-08 20:50:45 -04:00
co-authored by Copilot
parent 6d61b70a06
commit acaf1747f3
+20 -7
View File
@@ -7,12 +7,14 @@ etc. - all common dashcam brands are covered since we just walk the whole
filesystem for video extensions instead of assuming a specific folder name), filesystem for video extensions instead of assuming a specific folder name),
then unmounts it again. then unmounts it again.
Detection is done via `lsblk`, filtering for partitions whose parent disk is Detection is done via `lsblk`, filtering for volumes whose parent disk is
attached over USB (TRAN == "usb"). This is what actually distinguishes a attached over USB (TRAN == "usb"). This is what actually distinguishes a
plugged-in dashcam/USB stick from the Pi's own storage (the boot/root plugged-in dashcam/USB stick from the Pi's own storage (the boot/root
partitions are on the onboard SD card, transport "mmc", never "usb") - so partitions are on the onboard SD card, transport "mmc", never "usb") - so
it's safe to run against real hardware without risk of touching the Pi's own it's safe to run against real hardware without risk of touching the Pi's own
filesystem, regardless of which /dev/sdX letter the kernel happens to assign. filesystem, regardless of which /dev/sdX letter the kernel happens to assign.
Both real partitions and whole-disk "superfloppy" (no partition table)
layouts are matched - some dashcam SD cards ship factory-formatted that way.
""" """
import json import json
import os import os
@@ -32,12 +34,19 @@ DEFAULT_DESTINATION = os.path.join(
def list_usb_storage_partitions(): def list_usb_storage_partitions():
"""Return a list of dicts describing USB-attached storage partitions. """Return a list of dicts describing USB-attached storage volumes.
Each dict has: path, fstype, label, size, mountpoint. Only partitions Each dict has: path, fstype, label, size, mountpoint. Only volumes whose
whose parent disk reports TRAN "usb" are included - this is what makes parent disk reports TRAN "usb" are included - this is what makes the
the detection brand/device-agnostic (no hardcoded /dev/sda) while still detection brand/device-agnostic (no hardcoded /dev/sda) while still
being safe (never matches the Pi's own onboard mmc storage). being safe (never matches the Pi's own onboard mmc storage).
Includes both real partitions (type "part", the common case) *and*
whole-disk USB devices with an `fstype` set directly on them (type
"disk" but no partition table - a "superfloppy" layout with no MBR/GPT).
Several real dashcam SD cards ship formatted this way straight from the
factory - lsblk reports them as a bare "disk" with an fstype, never as a
child "part", so excluding them would silently miss those cameras.
""" """
try: try:
result = subprocess.run( result = subprocess.run(
@@ -61,7 +70,12 @@ def list_usb_storage_partitions():
def walk(devices, inherited_tran=None): def walk(devices, inherited_tran=None):
for device in devices: for device in devices:
tran = device.get("tran") or inherited_tran tran = device.get("tran") or inherited_tran
if device.get("type") == "part" and tran == "usb": children = device.get("children") or []
is_partition = device.get("type") == "part"
is_superfloppy_disk = (
device.get("type") == "disk" and not children and device.get("fstype")
)
if tran == "usb" and (is_partition or is_superfloppy_disk):
partitions.append( partitions.append(
{ {
"path": device.get("path"), "path": device.get("path"),
@@ -71,7 +85,6 @@ def list_usb_storage_partitions():
"mountpoint": device.get("mountpoint"), "mountpoint": device.get("mountpoint"),
} }
) )
children = device.get("children") or []
if children: if children:
walk(children, tran) walk(children, tran)