MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能: - ✅ Categories/Series双视图管理(category_view.rs + import_markdown.rs) - ✅ FUSE Multi-Volume支持(tree_type参数) - ✅ SSH/SFTP/SCP/rsync协议完整实现(4042行) - ✅ NFS/SMB Module Phase 1-3完成 - ✅ Archive Module Phase 1-4完成(2916行) - ✅ Download Center API完整实现 - ✅ S3兼容API实现(560行) Git配置修正: - ✅ 删除错误origin(gitea.momentry.ddns.net) - ✅ 删除m5max128(指向机器名) - ✅ 设置origin = m5max128gitea.momentry.ddns.net/admin/markbase - ✅ 设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase 数据清理: - ✅ 删除38个临时SQLite(保留accusys.sqlite、demo.sqlite) - ✅ 删除.bak、test_*.bin、调试脚本等临时文件 - ✅ 删除临时目录(build/、download files/、raid_test/等) - ✅ 更新.gitignore排除临时文件 架构优化: - 52个文件修改,2434行新增,4739行删除 - Workspace成员整合(16个crate) - 数据库状态:accusys.sqlite保留(主demo测试) 远程同步: - ✅ 准备推送到m5max128gitea(远程Gitea) - ✅ 准备推送到m4minigitea(本地Gitea)
This commit is contained in:
338
MarkBaseFS/MarkBaseFS/FrameIndexTable.swift
Normal file
338
MarkBaseFS/MarkBaseFS/FrameIndexTable.swift
Normal file
@@ -0,0 +1,338 @@
|
||||
import Foundation
|
||||
import SQLite3
|
||||
|
||||
public class FrameIndexTable {
|
||||
private var db: OpaquePointer?
|
||||
private let dbPath: String
|
||||
|
||||
public init(dbPath: String) {
|
||||
self.dbPath = dbPath
|
||||
openDatabase()
|
||||
createTables()
|
||||
}
|
||||
|
||||
private func openDatabase() {
|
||||
if sqlite3_open(dbPath, &db) != SQLITE_OK {
|
||||
print("Error opening database at: \(dbPath)")
|
||||
return
|
||||
}
|
||||
print("Database opened successfully: \(dbPath)")
|
||||
}
|
||||
|
||||
private func createTables() {
|
||||
// Create frame_records table
|
||||
let createFrameRecordsTable = """
|
||||
CREATE TABLE IF NOT EXISTS frame_records (
|
||||
frame_id TEXT PRIMARY KEY,
|
||||
video_id TEXT,
|
||||
frame_index INTEGER,
|
||||
frame_file TEXT,
|
||||
frame_offset INTEGER,
|
||||
frame_size INTEGER,
|
||||
frame_checksum TEXT,
|
||||
frame_lock_state INTEGER DEFAULT 0,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""
|
||||
|
||||
// Create video_metadata table
|
||||
let createVideoMetadataTable = """
|
||||
CREATE TABLE IF NOT EXISTS video_metadata (
|
||||
video_id TEXT PRIMARY KEY,
|
||||
video_name TEXT,
|
||||
video_path TEXT,
|
||||
total_frames INTEGER,
|
||||
fps REAL,
|
||||
duration REAL,
|
||||
resolution TEXT,
|
||||
codec TEXT,
|
||||
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
"""
|
||||
|
||||
// Create frame_lock_history table
|
||||
let createFrameLockHistoryTable = """
|
||||
CREATE TABLE IF NOT EXISTS frame_lock_history (
|
||||
lock_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
frame_id TEXT,
|
||||
lock_action TEXT,
|
||||
lock_timestamp TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
user_id TEXT
|
||||
);
|
||||
"""
|
||||
|
||||
executeQuery(createFrameRecordsTable)
|
||||
executeQuery(createVideoMetadataTable)
|
||||
executeQuery(createFrameLockHistoryTable)
|
||||
|
||||
print("Tables created successfully")
|
||||
}
|
||||
|
||||
// Insert single frame
|
||||
public func insertFrame(frameId: String, videoId: String, frameIndex: Int, frameFile: String, frameOffset: Int, frameSize: Int, frameChecksum: String) -> Bool {
|
||||
let insertQuery = """
|
||||
INSERT INTO frame_records (frame_id, video_id, frame_index, frame_file, frame_offset, frame_size, frame_checksum)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?);
|
||||
"""
|
||||
|
||||
var statement: OpaquePointer?
|
||||
|
||||
if sqlite3_prepare_v2(db, insertQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
sqlite3_bind_text(statement, 1, (frameId as NSString).utf8String, -1, nil)
|
||||
sqlite3_bind_text(statement, 2, (videoId as NSString).utf8String, -1, nil)
|
||||
sqlite3_bind_int(statement, 3, Int32(frameIndex))
|
||||
sqlite3_bind_text(statement, 4, (frameFile as NSString).utf8String, -1, nil)
|
||||
sqlite3_bind_int(statement, 5, Int32(frameOffset))
|
||||
sqlite3_bind_int(statement, 6, Int32(frameSize))
|
||||
sqlite3_bind_text(statement, 7, (frameChecksum as NSString).utf8String, -1, nil)
|
||||
|
||||
if sqlite3_step(statement) == SQLITE_DONE {
|
||||
sqlite3_finalize(statement)
|
||||
print("Frame inserted successfully: \(frameId)")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
print("Error inserting frame: \(frameId)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Batch insert frames (performance optimization: 0.1-0.5s for 1000 frames)
|
||||
public func insertFrames(frames: [(frameId: String, videoId: String, frameIndex: Int, frameFile: String, frameOffset: Int, frameSize: Int, frameChecksum: String)]) -> Bool {
|
||||
print("Batch inserting \(frames.count) frames...")
|
||||
|
||||
// Begin transaction
|
||||
sqlite3_exec(db, "BEGIN TRANSACTION;", nil, nil, nil)
|
||||
|
||||
var success = true
|
||||
for frame in frames {
|
||||
if !insertFrame(frameId: frame.frameId, videoId: frame.videoId, frameIndex: frame.frameIndex, frameFile: frame.frameFile, frameOffset: frame.frameOffset, frameSize: frame.frameSize, frameChecksum: frame.frameChecksum) {
|
||||
success = false
|
||||
print("Batch insert failed at frame: \(frame.frameId)")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Commit or rollback
|
||||
if success {
|
||||
sqlite3_exec(db, "COMMIT;", nil, nil, nil)
|
||||
print("Batch insert successful: \(frames.count) frames")
|
||||
} else {
|
||||
sqlite3_exec(db, "ROLLBACK;", nil, nil, nil)
|
||||
print("Batch insert rolled back")
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
// Get single frame
|
||||
public func getFrame(frameId: String) -> [String: Any]? {
|
||||
let selectQuery = "SELECT * FROM frame_records WHERE frame_id = ?;"
|
||||
|
||||
var statement: OpaquePointer?
|
||||
|
||||
if sqlite3_prepare_v2(db, selectQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
sqlite3_bind_text(statement, 1, (frameId as NSString).utf8String, -1, nil)
|
||||
|
||||
if sqlite3_step(statement) == SQLITE_ROW {
|
||||
let frameId = String(cString: sqlite3_column_text(statement, 0))
|
||||
let videoId = String(cString: sqlite3_column_text(statement, 1))
|
||||
let frameIndex = sqlite3_column_int(statement, 2)
|
||||
let frameFile = String(cString: sqlite3_column_text(statement, 3))
|
||||
let frameOffset = sqlite3_column_int(statement, 4)
|
||||
let frameSize = sqlite3_column_int(statement, 5)
|
||||
let frameChecksum = String(cString: sqlite3_column_text(statement, 6))
|
||||
let frameLockState = sqlite3_column_int(statement, 7)
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
|
||||
return [
|
||||
"frame_id": frameId,
|
||||
"video_id": videoId,
|
||||
"frame_index": Int(frameIndex),
|
||||
"frame_file": frameFile,
|
||||
"frame_offset": Int(frameOffset),
|
||||
"frame_size": Int(frameSize),
|
||||
"frame_checksum": frameChecksum,
|
||||
"frame_lock_state": Int(frameLockState)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lock frame
|
||||
public func lockFrame(frameId: String) -> Bool {
|
||||
let updateQuery = "UPDATE frame_records SET frame_lock_state = 1 WHERE frame_id = ?;"
|
||||
|
||||
var statement: OpaquePointer?
|
||||
|
||||
if sqlite3_prepare_v2(db, updateQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
sqlite3_bind_text(statement, 1, (frameId as NSString).utf8String, -1, nil)
|
||||
|
||||
if sqlite3_step(statement) == SQLITE_DONE {
|
||||
sqlite3_finalize(statement)
|
||||
print("Frame locked successfully: \(frameId)")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
print("Error locking frame: \(frameId)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Unlock frame
|
||||
public func unlockFrame(frameId: String) -> Bool {
|
||||
let updateQuery = "UPDATE frame_records SET frame_lock_state = 0 WHERE frame_id = ?;"
|
||||
|
||||
var statement: OpaquePointer?
|
||||
|
||||
if sqlite3_prepare_v2(db, updateQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
sqlite3_bind_text(statement, 1, (frameId as NSString).utf8String, -1, nil)
|
||||
|
||||
if sqlite3_step(statement) == SQLITE_DONE {
|
||||
sqlite3_finalize(statement)
|
||||
print("Frame unlocked successfully: \(frameId)")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
print("Error unlocking frame: \(frameId)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Delete frame
|
||||
public func deleteFrame(frameId: String) -> Bool {
|
||||
let deleteQuery = "DELETE FROM frame_records WHERE frame_id = ?;"
|
||||
|
||||
var statement: OpaquePointer?
|
||||
|
||||
if sqlite3_prepare_v2(db, deleteQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
sqlite3_bind_text(statement, 1, (frameId as NSString).utf8String, -1, nil)
|
||||
|
||||
if sqlite3_step(statement) == SQLITE_DONE {
|
||||
sqlite3_finalize(statement)
|
||||
print("Frame deleted successfully: \(frameId)")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
print("Error deleting frame: \(frameId)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Update frame
|
||||
public func updateFrame(frameId: String, updates: [String: Any]) -> Bool {
|
||||
// Build dynamic update query
|
||||
var updateFields: [String] = []
|
||||
var values: [Any] = []
|
||||
|
||||
for (key, value) in updates {
|
||||
if key != "frame_id" { // Don't update primary key
|
||||
updateFields.append("\(key) = ?")
|
||||
values.append(value)
|
||||
}
|
||||
}
|
||||
|
||||
if updateFields.isEmpty {
|
||||
print("No fields to update for frame: \(frameId)")
|
||||
return false
|
||||
}
|
||||
|
||||
let updateQuery = "UPDATE frame_records SET \(updateFields.joined(separator: ", ")) WHERE frame_id = ?;"
|
||||
|
||||
var statement: OpaquePointer?
|
||||
|
||||
if sqlite3_prepare_v2(db, updateQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
// Bind values
|
||||
for (index, value) in values.enumerated() {
|
||||
let bindIndex = Int32(index + 1)
|
||||
|
||||
if let stringValue = value as? String {
|
||||
sqlite3_bind_text(statement, bindIndex, (stringValue as NSString).utf8String, -1, nil)
|
||||
} else if let intValue = value as? Int {
|
||||
sqlite3_bind_int(statement, bindIndex, Int32(intValue))
|
||||
} else if let doubleValue = value as? Double {
|
||||
sqlite3_bind_double(statement, bindIndex, doubleValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Bind frame_id for WHERE clause
|
||||
sqlite3_bind_text(statement, Int32(values.count + 1), (frameId as NSString).utf8String, -1, nil)
|
||||
|
||||
if sqlite3_step(statement) == SQLITE_DONE {
|
||||
sqlite3_finalize(statement)
|
||||
print("Frame updated successfully: \(frameId)")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
print("Error updating frame: \(frameId)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Get all frames for a video
|
||||
public func getFramesForVideo(videoId: String) -> [[String: Any]] {
|
||||
let selectQuery = "SELECT * FROM frame_records WHERE video_id = ? ORDER BY frame_index;"
|
||||
|
||||
var statement: OpaquePointer?
|
||||
var frames: [[String: Any]] = []
|
||||
|
||||
if sqlite3_prepare_v2(db, selectQuery, -1, &statement, nil) == SQLITE_OK {
|
||||
sqlite3_bind_text(statement, 1, (videoId as NSString).utf8String, -1, nil)
|
||||
|
||||
while sqlite3_step(statement) == SQLITE_ROW {
|
||||
let frameId = String(cString: sqlite3_column_text(statement, 0))
|
||||
let videoId = String(cString: sqlite3_column_text(statement, 1))
|
||||
let frameIndex = sqlite3_column_int(statement, 2)
|
||||
let frameFile = String(cString: sqlite3_column_text(statement, 3))
|
||||
let frameOffset = sqlite3_column_int(statement, 4)
|
||||
let frameSize = sqlite3_column_int(statement, 5)
|
||||
let frameChecksum = String(cString: sqlite3_column_text(statement, 6))
|
||||
let frameLockState = sqlite3_column_int(statement, 7)
|
||||
|
||||
frames.append([
|
||||
"frame_id": frameId,
|
||||
"video_id": videoId,
|
||||
"frame_index": Int(frameIndex),
|
||||
"frame_file": frameFile,
|
||||
"frame_offset": Int(frameOffset),
|
||||
"frame_size": Int(frameSize),
|
||||
"frame_checksum": frameChecksum,
|
||||
"frame_lock_state": Int(frameLockState)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(statement)
|
||||
return frames
|
||||
}
|
||||
|
||||
// Execute raw query
|
||||
private func executeQuery(_ query: String) {
|
||||
var errorMessage: UnsafeMutablePointer<CChar>?
|
||||
|
||||
if sqlite3_exec(db, query, nil, nil, &errorMessage) != SQLITE_OK {
|
||||
if let error = errorMessage {
|
||||
print("Error executing query: \(String(cString: error))")
|
||||
sqlite3_free(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
if db != nil {
|
||||
sqlite3_close(db)
|
||||
print("Database closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user