Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions Dayflow/Dayflow/Core/AI/GeminiDirectProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Foundation

final class GeminiDirectProvider: LLMProvider {
private let apiKey: String
private let genEndpoint = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:generateContent"
private let fileEndpoint = "https://generativelanguage.googleapis.com/upload/v1beta/files"
private let proModel = "gemini-2.5-pro"
private let flashModel = "gemini-2.5-flash"
Expand Down Expand Up @@ -317,7 +316,6 @@ final class GeminiDirectProvider: LLMProvider {
var lastError: Error?
var finalResponse = ""
var finalObservations: [Observation] = []
var finalUsedModel = proModel

// Model state for Flash fallback (persists across retries)
var currentModel = proModel
Expand Down Expand Up @@ -387,7 +385,6 @@ final class GeminiDirectProvider: LLMProvider {
print("✅ Video transcription succeeded on attempt \(attempt + 1)")
finalResponse = response
finalObservations = observations
finalUsedModel = usedModel
break

} catch {
Expand Down Expand Up @@ -548,8 +545,6 @@ final class GeminiDirectProvider: LLMProvider {
let existingCardsJSON = try encoder.encode(context.existingCards)
let existingCardsString = String(data: existingCardsJSON, encoding: .utf8) ?? "[]"

let exampleCategory = context.categories.first?.name ?? "Work"

let basePrompt = """
You are a digital anthropologist, observing a user's raw activity log. Your goal is to synthesize this log into a high-level, human-readable story of their session, presented as a series of timeline cards.
THE GOLDEN RULE:
Expand Down Expand Up @@ -1165,7 +1160,7 @@ private func uploadResumable(data: Data, mimeType: String) async throws -> Strin
// Prepare logging context
let responseHeaders: [String:String] = httpResponse.allHeaderFields.reduce(into: [:]) { acc, kv in
if let k = kv.key as? String, let v = kv.value as? CustomStringConvertible { acc[k] = v.description }
} ?? [:]
}
let modelName: String? = {
if let u = URL(string: urlWithKey) {
let last = u.path.split(separator: "/").last.map(String.init)
Expand Down Expand Up @@ -1475,7 +1470,7 @@ private func uploadResumable(data: Data, mimeType: String) async throws -> Strin
// Prepare logging context
let responseHeaders: [String:String] = httpResponse.allHeaderFields.reduce(into: [:]) { acc, kv in
if let k = kv.key as? String, let v = kv.value as? CustomStringConvertible { acc[k] = v.description }
} ?? [:]
}
let modelName: String? = {
if let u = URL(string: urlWithKey) {
let last = u.path.split(separator: "/").last.map(String.init)
Expand Down
7 changes: 3 additions & 4 deletions Dayflow/Dayflow/Core/AI/LLMService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,11 @@ final class LLMService: LLMServicing {
throw NSError(domain: "LLMService", code: 6, userInfo: [NSLocalizedDescriptionKey: "Failed to create composition track"])
}

for (index, filePath) in chunkFiles.enumerated() {
for (_, filePath) in chunkFiles.enumerated() {
let url = URL(fileURLWithPath: filePath)

let asset = AVAsset(url: url)
let duration = try await asset.load(.duration)
let durationSeconds = CMTimeGetSeconds(duration)


if let track = try await asset.loadTracks(withMediaType: .video).first {
Expand Down Expand Up @@ -216,7 +215,7 @@ final class LLMService: LLMServicing {
// Get batch start time for timestamp conversion
let batchStartDate = Date(timeIntervalSince1970: TimeInterval(batchStartTs))

let (observations, transcribeLog) = try await provider.transcribeVideo(
let (observations, _) = try await provider.transcribeVideo(
videoData: videoData,
mimeType: mimeType,
prompt: "Transcribe this video", // Provider will use its own prompt
Expand Down Expand Up @@ -292,7 +291,7 @@ final class LLMService: LLMServicing {
)

// Generate activity cards using sliding window observations
let (cards, cardsLog) = try await provider.generateActivityCards(
let (cards, _) = try await provider.generateActivityCards(
observations: recentObservations,
context: context,
batchId: batchId
Expand Down
5 changes: 1 addition & 4 deletions Dayflow/Dayflow/Core/AI/OllamaProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ final class OllamaProvider: LLMProvider {
// Step 1: Extract frames at intervals
let extractionStart = Date()
let frames = try await extractFrames(from: tempURL)
let extractionTime = Date().timeIntervalSince(extractionStart)


// Step 2: Get simple descriptions for each frame
Expand All @@ -77,7 +76,6 @@ final class OllamaProvider: LLMProvider {
videoDuration: videoDuration,
batchId: batchId
)
let mergeTime = Date().timeIntervalSince(mergeStart)


let totalTime = Date().timeIntervalSince(callStart)
Expand Down Expand Up @@ -428,7 +426,7 @@ final class OllamaProvider: LLMProvider {
}

// Build message content with image and text
var content: [MessageContent] = [
let content: [MessageContent] = [
MessageContent(type: "text", text: prompt, image_url: nil),
MessageContent(type: "image_url", text: nil, image_url: MessageContent.ImageURL(url: "data:image/jpeg;base64,\(base64String)"))
]
Expand Down Expand Up @@ -494,7 +492,6 @@ final class OllamaProvider: LLMProvider {
)
ctxForAttempt = ctx
let (data, response) = try await URLSession.shared.data(for: urlRequest)
let apiTime = Date().timeIntervalSince(apiStart)

guard let httpResponse = response as? HTTPURLResponse else {
throw NSError(domain: "OllamaProvider", code: 4, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])
Expand Down
17 changes: 2 additions & 15 deletions Dayflow/Dayflow/Core/Analysis/AnalysisManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ final class AnalysisManager: AnalysisManaging {

// 6. Process each batch sequentially
var processedCount = 0
var hasError = false
let hasError = false

for (index, batchId) in batchIds.enumerated() {
if hasError { break }
Expand All @@ -125,9 +125,6 @@ final class AnalysisManager: AnalysisManaging {
progressHandler("Processing batch \(index + 1) of \(batchIds.count)... (Total elapsed: \(self.formatDuration(elapsedTotal)))")
}

// Use a semaphore to wait for each batch to complete
let semaphore = DispatchSemaphore(value: 0)

self.queueGeminiRequest(batchId: batchId)

// Wait for batch to complete (check status periodically)
Expand Down Expand Up @@ -245,10 +242,8 @@ final class AnalysisManager: AnalysisManaging {

// Process batches
var processedCount = 0
var hasError = false

for (index, batchId) in batchIds.enumerated() {
if hasError { break }

let batchStartTime = Date()
let elapsedTotal = Date().timeIntervalSince(overallStartTime)
Expand All @@ -261,7 +256,7 @@ final class AnalysisManager: AnalysisManaging {

// Wait for batch to complete (check status periodically)
var isCompleted = false
while !isCompleted && !hasError {
while !isCompleted {
Thread.sleep(forTimeInterval: 2.0) // Check every 2 seconds

let allBatches = self.store.allBatches()
Expand Down Expand Up @@ -354,14 +349,6 @@ final class AnalysisManager: AnalysisManaging {

updateBatchStatus(batchId: batchId, status: "processing")

// Prepare file URLs for video processing
let chunkFileURLs: [URL] = chunksInBatch.compactMap { chunk in
// Assuming chunk.fileUrl is a String path, convert to URL
// Ensure this path is accessible. If it's a relative path, resolve it.
// For now, assuming it's an absolute file path string.
URL(fileURLWithPath: chunk.fileUrl)
}

llmService.processBatch(batchId) { [weak self] (result: Result<ProcessedBatchResult, Error>) in
guard let self else { return }

Expand Down
5 changes: 3 additions & 2 deletions Dayflow/Dayflow/Core/Recording/ScreenRecorder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ final class ScreenRecorder: NSObject, SCStreamOutput {
guard type == .screen else { return }
guard CMSampleBufferDataIsReady(sb) else { return }
guard isComplete(sb) else { return }
if let pb = CMSampleBufferGetImageBuffer(sb) {
if CMSampleBufferGetImageBuffer(sb) != nil {
// TEMPORARILY DISABLED to test if this causes corruption
// overlayClock(on: pb) // ← inject the clock into this frame
}
Expand All @@ -455,7 +455,8 @@ final class ScreenRecorder: NSObject, SCStreamOutput {

if firstPTS == nil {
firstPTS = sb.presentationTimeStamp
let started = w.startWriting()
let didStartWriting = w.startWriting()
assert(didStartWriting)
w.startSession(atSourceTime: firstPTS!)
}

Expand Down
6 changes: 0 additions & 6 deletions Dayflow/Dayflow/Core/Recording/StorageManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -915,12 +915,6 @@ final class StorageManager: StorageManaging, @unchecked Sendable {
OR (start_ts >= ? AND start_ts < ?)
""", arguments: [toTs, fromTs, fromTs, toTs])

for card in cardsToDelete {
let id: Int64 = card["id"]
let start: String = card["start"]
let end: String = card["end"]
let title: String = card["title"]
}

// Delete existing cards in the range using timestamp columns
try db.execute(sql: """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,6 @@ actor VideoProcessingService {
let actualSize = naturalSize.applying(preferredTransform)
let width = Int(abs(actualSize.width))
let height = Int(abs(actualSize.height))
let nominalFrameRate = try await assetTrack.load(.nominalFrameRate)

// Create composition with time mapping for speedup
let composition = AVMutableComposition()
Expand Down
1 change: 0 additions & 1 deletion Dayflow/Dayflow/Core/Thumbnails/ThumbnailCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ final class ThumbnailCache {

queue.addOperation { [weak self] in
guard let self = self else { return }
let t0 = CFAbsoluteTimeGetCurrent()
let image = self.generateThumbnail(urlString: normalizedURL, targetSize: targetSize)

if let image = image {
Expand Down
2 changes: 1 addition & 1 deletion Dayflow/Dayflow/System/AnalyticsService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ final class AnalyticsService {
registerInitialSuperProperties()

// Person properties via $set / $set_once
var set: [String: Any] = [
let set: [String: Any] = [
"analytics_opt_in": isOptedIn
]
var payload: [String: Any] = ["$set": sanitize(set)]
Expand Down
2 changes: 1 addition & 1 deletion Dayflow/Dayflow/System/SilentUserDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final class SilentUserDriver: NSObject, SPUUserDriver {
}

func showUpdateFound(with appcastItem: SUAppcastItem, state: SPUUserUpdateState, reply: @escaping (SPUUserUpdateChoice) -> Void) {
print("[Sparkle] Update found: \(appcastItem.displayVersionString ?? appcastItem.versionString)")
print("[Sparkle] Update found: \(appcastItem.displayVersionString)")
// Always proceed to install
reply(.install)
}
Expand Down
2 changes: 1 addition & 1 deletion Dayflow/Dayflow/System/UpdaterManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ extension UpdaterManager: SPUUpdaterDelegate {
nonisolated func updater(_ updater: SPUUpdater, didFindValidUpdate item: SUAppcastItem) {
Task { @MainActor in
self.updateAvailable = true
self.latestVersionString = item.displayVersionString ?? item.versionString
self.latestVersionString = item.displayVersionString
self.statusText = "Update available: v\(self.latestVersionString ?? "?")"
self.isChecking = false
AppDelegate.allowTermination = false
Expand Down
1 change: 0 additions & 1 deletion Dayflow/Dayflow/Views/UI/ScrubberView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ final class FilmstripGenerator {

var images: [NSImage] = Array(repeating: NSImage(), count: frameCount)
var produced = 0
let group = DispatchGroup()

// Use generateCGImagesAsynchronously for better throughput
generator.generateCGImagesAsynchronously(forTimes: times) { requestedTime, cg, actualTime, result, error in
Expand Down