Fix video playback for files in root directory

- Handle __root__ special case in video streaming and image endpoints
- Support locations with files not organized by date folders
- Add visual indicator for root-level file collections in UI
This commit is contained in:
Alihan
2025-10-12 23:42:21 +03:00
parent dec49a43f9
commit a7b7ad41e9
2 changed files with 40 additions and 5 deletions

View File

@@ -176,6 +176,8 @@ async def get_dates(location: str) -> List[Dict]:
raise HTTPException(status_code=404, detail="Location not found")
dates = []
has_files_in_root = False
for item in location_path.iterdir():
if item.is_dir():
stat = await aiofiles.os.stat(item)
@@ -183,6 +185,16 @@ async def get_dates(location: str) -> List[Dict]:
"name": item.name,
"modified": datetime.fromtimestamp(stat.st_mtime).isoformat()
})
elif item.is_file():
has_files_in_root = True
# If no date folders but has files in root, return special marker
if not dates and has_files_in_root:
dates.append({
"name": "__root__",
"modified": None,
"message": "📁 Files not organized by date"
})
# Cache the result
directory_cache.set(cache_key, dates)
@@ -202,7 +214,11 @@ async def get_files(location: str, date: str) -> List[Dict]:
if ".." in location or ".." in date or "/" in location or "/" in date:
raise HTTPException(status_code=400, detail="Invalid path characters")
files_path = (FOOTAGES_PATH / location / date).resolve()
# Handle special __root__ marker for locations with files in root
if date == "__root__":
files_path = (FOOTAGES_PATH / location).resolve()
else:
files_path = (FOOTAGES_PATH / location / date).resolve()
# Ensure resolved path is still within FOOTAGES_PATH
try:
@@ -230,7 +246,11 @@ async def stream_video(location: str, date: str, filename: str, request: Request
if ".." in location or ".." in date or ".." in filename or "/" in location or "/" in date:
raise HTTPException(status_code=400, detail="Invalid path characters")
file_path = (FOOTAGES_PATH / location / date / filename).resolve()
# Handle __root__ case (files not in date subdirectories)
if date == "__root__":
file_path = (FOOTAGES_PATH / location / filename).resolve()
else:
file_path = (FOOTAGES_PATH / location / date / filename).resolve()
# Ensure resolved path is still within FOOTAGES_PATH
try:
@@ -304,7 +324,11 @@ async def get_image(location: str, date: str, filename: str):
if ".." in location or ".." in date or ".." in filename or "/" in location or "/" in date:
raise HTTPException(status_code=400, detail="Invalid path characters")
file_path = (FOOTAGES_PATH / location / date / filename).resolve()
# Handle __root__ case (files not in date subdirectories)
if date == "__root__":
file_path = (FOOTAGES_PATH / location / filename).resolve()
else:
file_path = (FOOTAGES_PATH / location / date / filename).resolve()
# Ensure resolved path is still within FOOTAGES_PATH
try:

View File

@@ -409,15 +409,26 @@ function App() {
<ul className="space-y-1">
{getSortedDates().map((date) => {
const dateName = date.name || date
const isRootFiles = dateName === "__root__"
const displayName = isRootFiles ? "All Files" : dateName
const message = date.message || null
return (
<li key={dateName}>
<button
onClick={() => handleDateClick(dateName)}
className={`w-full text-left px-3 py-2 rounded hover:bg-blue-50 transition ${
className={`w-full text-left px-3 py-2 rounded transition ${
isRootFiles
? 'bg-yellow-50 border border-yellow-200 hover:bg-yellow-100'
: 'hover:bg-blue-50'
} ${
selectedDate === dateName ? 'bg-blue-100 font-semibold' : ''
}`}
>
{dateName}
<div className="font-medium">{displayName}</div>
{message && (
<div className="text-xs text-yellow-700 mt-1">{message}</div>
)}
</button>
</li>
)