creek
The AI Image Editor of 2030git clone https://github.com/maydayv7/creek
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}