Skip to content

Commit 81c2792

Browse files
committed
fix: support .fcpxmld bundle import + URL-decoded Korean paths
- NSOpenPanel: canChooseDirectories=true for .fcpxmld bundles - Resolve .fcpxmld → Info.fcpxml inside the bundle - handle_resub: .fcpxmld directory detection - findVideoInFCPXML: URL-decode percent-encoded Korean filenames - Support .MOV/.MP4 uppercase extensions
1 parent 2e24d87 commit 81c2792

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

SilenciApp/Sources/ContentView.swift

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,28 @@ struct ContentView: View {
242242
let panel = NSOpenPanel()
243243
panel.allowedContentTypes = [.xml, .data]
244244
panel.allowsMultipleSelection = false
245+
panel.canChooseDirectories = true // .fcpxmld is a directory bundle
246+
panel.canChooseFiles = true
245247
panel.message = L10n.tr("toolbar.import_fcpxml_message")
246248
panel.begin { response in
247249
guard response == .OK, let url = panel.url else { return }
250+
// Resolve .fcpxmld bundle → Info.fcpxml inside
251+
let resolvedURL: URL
252+
if url.pathExtension == "fcpxmld" {
253+
resolvedURL = url.appendingPathComponent("Info.fcpxml")
254+
guard FileManager.default.fileExists(atPath: resolvedURL.path) else {
255+
print("[Silenci] Error: Info.fcpxml not found inside .fcpxmld bundle")
256+
return
257+
}
258+
} else {
259+
resolvedURL = url
260+
}
248261
// Try to load the source video from FCPXML for preview
249-
if let videoURL = Self.findVideoInFCPXML(url) {
262+
if let videoURL = Self.findVideoInFCPXML(resolvedURL) {
250263
videoModel.loadVideo(url: videoURL)
251264
}
252265
settings.save()
253-
analysisService.startResub(fcpxmlURL: url, environment: pythonEnv, settings: settings)
266+
analysisService.startResub(fcpxmlURL: resolvedURL, environment: pythonEnv, settings: settings)
254267
}
255268
}
256269

@@ -259,9 +272,18 @@ struct ContentView: View {
259272
guard let data = try? Data(contentsOf: fcpxmlURL),
260273
let xmlString = String(data: data, encoding: .utf8) else { return nil }
261274

262-
// Simple regex to find file:// URL in media-rep src
263-
if let range = xmlString.range(of: #"file://[^"]*\.(mov|mp4|m4v|avi|mkv)"#, options: .regularExpression, range: nil, locale: nil) {
275+
// Find file:// URL in media-rep src attribute
276+
if let range = xmlString.range(of: #"file://[^"]*\.(mov|mp4|m4v|avi|mkv|MOV|MP4)"#, options: .regularExpression) {
264277
let urlString = String(xmlString[range])
278+
// URL decode percent-encoded paths (e.g. Korean filenames)
279+
if let decoded = urlString.removingPercentEncoding,
280+
let url = URL(string: decoded.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? urlString) {
281+
let path = decoded.replacingOccurrences(of: "file://", with: "")
282+
if FileManager.default.fileExists(atPath: path) {
283+
return URL(fileURLWithPath: path)
284+
}
285+
}
286+
// Fallback: try direct URL construction
265287
if let url = URL(string: urlString), FileManager.default.fileExists(atPath: url.path) {
266288
return url
267289
}

silence_cutter/server.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,15 @@ def handle_resub(params: dict) -> dict:
505505

506506
_progress("analyze", 0, "Reading FCPXML…")
507507

508+
# Handle .fcpxmld bundle (directory)
509+
from pathlib import Path as _P
510+
fcpxml_p = _P(fcpxml_path)
511+
if fcpxml_p.is_dir():
512+
info_path = fcpxml_p / "Info.fcpxml"
513+
if not info_path.exists():
514+
raise FileNotFoundError(f"Info.fcpxml not found in: {fcpxml_p}")
515+
fcpxml_path = str(info_path)
516+
508517
# 1. Parse FCPXML
509518
tree = ET.parse(str(fcpxml_path))
510519
root = tree.getroot()

0 commit comments

Comments
 (0)