creek

The AI Image Editor of 2030

lib/services/image_service.dart (3814B)


  1import 'dart:async';
  2import 'dart:convert';
  3import 'dart:io';
  4import 'package:flutter/foundation.dart';
  5import 'package:path_provider/path_provider.dart';
  6import 'package:uuid/uuid.dart';
  7import 'package:creekui/data/repos/image_repo.dart';
  8import 'package:creekui/data/models/image_model.dart';
  9import 'analysis_queue_manager.dart';
 10
 11class ImageService {
 12  final ImageRepo _repo = ImageRepo();
 13  final Uuid _uuid = const Uuid();
 14
 15  // Checks if image is a draft (from ShareHandler) and updates it, or saves new
 16  Future<String> saveOrUpdateImage(
 17    File file,
 18    int projectId, {
 19    List<String> tags = const [],
 20  }) async {
 21    final existing = await _repo.getByFilePath(file.path);
 22    if (existing != null) {
 23      debugPrint(
 24        "ImageService: Updating existing draft ${existing.id} -> Project $projectId",
 25      );
 26      await _repo.updateProject(existing.id, projectId);
 27      await updateTags(existing.id, tags);
 28      return existing.id;
 29    } else {
 30      debugPrint("ImageService: Saving new image");
 31      return await saveImage(file, projectId, tags: tags);
 32    }
 33  }
 34
 35  // --- PUBLIC METHODS ---
 36
 37  // Saves a single image and returns its ID
 38  Future<String> saveImage(
 39    File file,
 40    int projectId, {
 41    List<String> tags = const [],
 42    String? status,
 43  }) async {
 44    // 1. Prepare Directory
 45    final dir = await getApplicationDocumentsDirectory();
 46    final folder = Directory("${dir.path}/images");
 47    if (!await folder.exists()) await folder.create(recursive: true);
 48
 49    // 2. Generate ID and Path
 50    final String id = _uuid.v4();
 51    String ext = "jpg"; // Default
 52    try {
 53      if (file.path.contains('.')) {
 54        ext = file.path.split('.').last;
 55      }
 56    } catch (_) {}
 57
 58    final String newPath = "${folder.path}/$id.$ext";
 59    await file.copy(newPath);
 60
 61    // 3. Create & Save Model
 62    final image = ImageModel(
 63      id: id,
 64      projectId: projectId,
 65      filePath: newPath,
 66      name: file.path.split('/').last,
 67      createdAt: DateTime.now(),
 68      tags: tags,
 69      status: status ?? 'pending',
 70    );
 71    await _repo.addImage(image);
 72
 73    // 4. Trigger Analysis Queue
 74    AnalysisQueueManager().processQueue();
 75
 76    return id;
 77  }
 78
 79  // Bulk save method
 80  Future<List<String>> saveImages(
 81    List<File> files,
 82    int projectId, {
 83    List<String> tags = const [],
 84  }) async {
 85    List<String> ids = [];
 86    for (var file in files) {
 87      String id = await saveImage(file, projectId, tags: tags);
 88      ids.add(id);
 89    }
 90    return ids;
 91  }
 92
 93  // Updates tags and triggers relevant analysis
 94  Future<void> updateTags(String imageId, List<String> newTags) async {
 95    // 1. Update Tags in Database
 96    await _repo.updateTags(imageId, newTags);
 97
 98    // 2. Mark as pending and trigger queue
 99    await _repo.updateStatus(imageId, 'pending');
100    AnalysisQueueManager().processQueue();
101  }
102
103  Future<void> renameImage(String id, String newName) async {
104    await _repo.updateName(id, newName);
105  }
106
107  Future<void> updateAnalysis(String id, Map<String, dynamic> analysis) async {
108    final jsonString = jsonEncode(analysis);
109    await _repo.updateAnalysis(id, jsonString);
110  }
111
112  Future<ImageModel?> getImage(String id) async {
113    return await _repo.getById(id);
114  }
115
116  Future<List<String>> getTags(String imageId) async {
117    return await _repo.getTagsForImage(imageId);
118  }
119
120  Future<void> deleteImage(String id) async {
121    final img = await _repo.getById(id);
122    if (img != null) {
123      final file = File(img.filePath);
124      if (await file.exists()) {
125        try {
126          await file.delete();
127        } catch (e) {
128          debugPrint("Error deleting file: $e");
129        }
130      }
131      await _repo.deleteImage(id);
132    }
133  }
134
135  Future<List<ImageModel>> getImages(int projectId) async {
136    return await _repo.getImages(projectId);
137  }
138}