import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:path_provider/path_provider.dart'; import 'package:photo_manager/photo_manager.dart'; import 'download_history.dart'; import 'image_format.dart'; import 'media_cache.dart'; class MediaSaveResult { final bool ok; final bool toGallery; final String? location; final String? error; const MediaSaveResult({ required this.ok, this.toGallery = false, this.location, this.error, }); } Future saveImageFromUrl(String url) async { if (url.isEmpty) { return const MediaSaveResult(ok: false, error: 'нет ссылки'); } try { final cacheName = 'avatar_${url.hashCode & 0x7fffffff}.jpg'; final file = await MediaCache.getOrDownload(cacheName, url); if (file == null) { return const MediaSaveResult(ok: false, error: 'не удалось загрузить'); } return await _persist( file, saveName: 'avatar_${DateTime.now().millisecondsSinceEpoch}.jpg', kind: SaveMediaKind.image, ); } catch (e) { return MediaSaveResult(ok: false, error: e.toString()); } } enum SaveMediaKind { image, video, file } Future saveMediaFile({ required String cacheName, required Future Function() resolveUrl, required String saveName, required SaveMediaKind kind, DownloadMetadata? download, }) async { try { var file = await MediaCache.existing(cacheName); if (file == null) { final url = await resolveUrl(); if (url == null || url.isEmpty) { return const MediaSaveResult(ok: false, error: 'нет ссылки'); } file = await MediaCache.getOrDownload(cacheName, url); } if (file == null) { return const MediaSaveResult(ok: false, error: 'не удалось загрузить'); } final result = await _persist(file, saveName: saveName, kind: kind); if (result.ok && download != null) { try { await DownloadHistory.record(download, file); } catch (_) {} } return result; } catch (e) { return MediaSaveResult(ok: false, error: e.toString()); } } Future saveLocalImage(String path, {String? saveName}) async { try { final file = File(path); if (!await file.exists()) { return const MediaSaveResult(ok: false, error: 'файл не найден'); } return await _persist( file, saveName: saveName ?? 'IMG_${DateTime.now().millisecondsSinceEpoch}.jpg', kind: SaveMediaKind.image, ); } catch (e) { return MediaSaveResult(ok: false, error: e.toString()); } } Future _persist( File file, { required String saveName, required SaveMediaKind kind, }) async { if (kind != SaveMediaKind.image) { return _write(file, saveName: saveName, kind: kind); } final image = await prepareImageForSave(file); if (image == null) return _write(file, saveName: saveName, kind: kind); try { return await _write( image.file, saveName: withImageExtension(saveName, image.extension), kind: kind, ); } finally { await image.discard(); } } Future _write( File file, { required String saveName, required SaveMediaKind kind, }) async { final toGallery = kind == SaveMediaKind.image || kind == SaveMediaKind.video; if (!kIsWeb && (Platform.isAndroid || Platform.isIOS) && toGallery) { final state = await PhotoManager.requestPermissionExtend(); if (!state.isAuth && !state.hasAccess) { return const MediaSaveResult(ok: false, error: 'нет доступа к галерее'); } if (kind == SaveMediaKind.video) { await PhotoManager.editor.saveVideo(file, title: saveName); } else { final bytes = await file.readAsBytes(); await PhotoManager.editor.saveImage(bytes, filename: saveName); } return const MediaSaveResult(ok: true, toGallery: true); } final dir = await _targetDirectory(); final target = File('${dir.path}${Platform.pathSeparator}$saveName'); await file.copy(target.path); return MediaSaveResult(ok: true, location: target.path); } Future _targetDirectory() async { try { final downloads = await getDownloadsDirectory(); if (downloads != null) return downloads; } catch (_) {} return getApplicationDocumentsDirectory(); }