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? 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") } } }