creek
The AI Image Editor of 2030git clone https://github.com/maydayv7/creek
commit bfa770ef10c8d33e979114fbc40de9c06d8fb83c parent b8875c159450f4e3e6a5c7752616c93af2895e7f Author: Aditya Samal <152994566+aditya-samal@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:06:02 +0530 Merge pull request #3 from nilotpal-n7/maydayv7 Fix Instagram image download
Diffstat:
| M | .gitignore | | | 1 | + |
| M | android/app/src/main/python/instagram_downloader.py | | | 66 | +++++++++++++++++++++++++++++------------------------------------- |
| M | lib/services/instagram_download_service.dart | | | 33 | +++++++++++++++++++++------------ |
| M | lib/ui/pages/share_handler_page.dart | | | 64 | ++++++++++++++++++++++++++++++++++++++++------------------------ |
4 files changed, 91 insertions(+), 73 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -27,6 +27,7 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +.gradle # Symbolication related app.*.symbols diff --git a/android/app/src/main/python/instagram_downloader.py b/android/app/src/main/python/instagram_downloader.py @@ -6,16 +6,11 @@ from pathlib import Path def download_instagram_image(url, output_dir): """ - Downloads an image from an Instagram URL using Instaloader. - - Args: - url (str): The full Instagram URL (e.g., https://www.instagram.com/p/ShortCode/) - output_dir (str): Directory where the image should be saved - - Returns: - dict: Result with success status and file path or error message + Downloads all images from an Instagram post. """ try: + os.makedirs(output_dir, exist_ok=True) + # Initialize Instaloader L = instaloader.Instaloader( download_pictures=True, @@ -24,60 +19,56 @@ def download_instagram_image(url, output_dir): download_geotags=False, download_comments=False, save_metadata=False, - compress_json=False + compress_json=False, ) + L.filename_pattern = "{shortcode}" # Extract shortcode from URL - # Matches /p/ShortCode, /reel/ShortCode, etc. shortcode_match = re.search(r'instagram\.com/(?:p|reel|tv)/([^/?#&]+)', url) - if not shortcode_match: return json.dumps({ "success": False, - "error": "Could not extract shortcode from URL. Please ensure the URL is in the format: https://www.instagram.com/p/ShortCode/" + "error": "Could not extract shortcode. Use format: https://instagram.com/p/ShortCode/" }) - shortcode = shortcode_match.group(1) - # Load the post using the shortcode + # Download post post = instaloader.Post.from_shortcode(L.context, shortcode) - - # Create output directory if it doesn't exist - os.makedirs(output_dir, exist_ok=True) - - # Download the post - L.download_post(post, target=output_dir) - - # Find the downloaded image file - # Instaloader saves files with pattern: {shortcode}_*.jpg or {shortcode}_*.png + L.download_post(post, target=Path(output_dir)) + + found_files = [] + extensions = ['jpg', 'png', 'webp', 'jpeg'] + patterns = [f"{shortcode}.{{ext}}", f"{shortcode}_*.{{ext}}"] + output_path = Path(output_dir) - image_files = list(output_path.glob(f"{shortcode}_*.jpg")) + list(output_path.glob(f"{shortcode}_*.png")) - - if not image_files: - # Try alternative pattern - image_files = list(output_path.glob(f"{shortcode}.*")) - - if image_files: - image_path = str(image_files[0]) + for ext in extensions: + for pattern in patterns: + matches = list(output_path.glob(pattern.format(ext=ext))) + for match in matches: + found_files.append(str(match.absolute())) + + found_files = sorted(list(set(found_files))) + + if found_files: return json.dumps({ "success": True, - "file_path": image_path, + "file_paths": found_files, "shortcode": shortcode }) else: + all_files = [f.name for f in output_path.glob("*")] return json.dumps({ "success": False, - "error": "Image downloaded but file not found" + "error": f"Downloaded but no images found. Files in dir: {all_files}" }) except instaloader.exceptions.InstaloaderException as e: return json.dumps({ "success": False, - "error": f"Instaloader error: {str(e)}. Note: This works best for PUBLIC posts. Private posts require login." + "error": f"Instaloader error: {str(e)}" }) except Exception as e: return json.dumps({ "success": False, - "error": f"Unexpected error: {str(e)}" - }) - + "error": f"Python error: {str(e)}" + }) +\ No newline at end of file diff --git a/lib/services/instagram_download_service.dart b/lib/services/instagram_download_service.dart @@ -12,7 +12,7 @@ class InstagramDownloadService { final _repo = ImageRepository(); final _uuid = const Uuid(); - Future<String?> downloadInstagramImage(String url) async { + Future<List<String>?> downloadInstagramImage(String url) async { try { // Get output directory final dir = await getApplicationDocumentsDirectory(); @@ -31,26 +31,35 @@ class InstagramDownloadService { final Map<String, dynamic> jsonResult = json.decode(result); if (jsonResult['success'] == true) { - final downloadedPath = jsonResult['file_path'] as String; + final List<dynamic> paths = jsonResult['file_paths']; + final List<String> savedImageIds = []; - // Copy to images directory final imagesDir = Directory('${dir.path}/images'); if (!await imagesDir.exists()) { await imagesDir.create(recursive: true); } - final String imageId = _uuid.v4(); - final extension = downloadedPath.split('.').last; - final String targetPath = '${imagesDir.path}/$imageId.$extension'; + // Iterate through all downloaded files + for (var path in paths) { + final String sourcePath = path as String; + + final String imageId = _uuid.v4(); + final extension = sourcePath.split('.').last; + final String targetPath = '${imagesDir.path}/$imageId.$extension'; - // Copy file - final sourceFile = File(downloadedPath); - await sourceFile.copy(targetPath); + // Copy file + final sourceFile = File(sourcePath); + if (await sourceFile.exists()) { + await sourceFile.copy(targetPath); - // Save to database - await _repo.insertImage(imageId, targetPath); + // Save to database + await _repo.insertImage(imageId, targetPath); + + savedImageIds.add(imageId); + } + } - return imageId; + return savedImageIds.isNotEmpty ? savedImageIds : null; } else { throw Exception(jsonResult['error'] ?? 'Unknown error'); } diff --git a/lib/ui/pages/share_handler_page.dart b/lib/ui/pages/share_handler_page.dart @@ -29,7 +29,7 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { final _imageRepo = ImageRepository(); final _uuid = const Uuid(); - String? _downloadedImageId; + List<String> _downloadedImageIds = []; bool _isDownloading = true; bool _hasError = false; String? _errorMessage; @@ -54,7 +54,7 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { final id = await _handleDirectImage(sharedPath); if (mounted) { setState(() { - _downloadedImageId = id; + if (id != null) _downloadedImageIds = [id]; _isDownloading = false; if (id == null) { _hasError = true; @@ -82,12 +82,12 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { // Check if it's an Instagram URL if (url.contains('instagram.com')) { try { - final id = await _instagramService.downloadInstagramImage(url); + final ids = await _instagramService.downloadInstagramImage(url); if (mounted) { setState(() { - _downloadedImageId = id; + if (ids != null) _downloadedImageIds = ids; _isDownloading = false; - if (id == null) { + if (ids == null || ids.isEmpty) { _hasError = true; _errorMessage = "Failed to download from Instagram"; } @@ -107,7 +107,7 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { final id = await _downloadService.downloadAndSaveImage(url); if (mounted) { setState(() { - _downloadedImageId = id; + if (id != null) _downloadedImageIds = [id]; // Wrap single ID in list _isDownloading = false; if (id == null) { _hasError = true; @@ -151,29 +151,33 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { } Future<void> _saveToBoard(int boardId) async { - if (_downloadedImageId == null) return; + if (_downloadedImageIds.isEmpty) return; - await _boardImageRepo.saveToBoard(boardId, _downloadedImageId!); + for (final id in _downloadedImageIds) { + await _boardImageRepo.saveToBoard(boardId, id); + } - // Get image path and trigger analysis final images = await _imageRepo.getAllImages(); - final imageData = images.firstWhere( - (img) => img['id'] == _downloadedImageId, - orElse: () => <String, dynamic>{}, - ); - - if (imageData.isNotEmpty && imageData['filePath'] != null) { - _analyzeImageInBackground( - _downloadedImageId!, - imageData['filePath'] as String, + + for (final id in _downloadedImageIds) { + final imageData = images.firstWhere( + (img) => img['id'] == id, + orElse: () => <String, dynamic>{}, ); + + if (imageData.isNotEmpty && imageData['filePath'] != null) { + _analyzeImageInBackground( + id, + imageData['filePath'] as String, + ); + } } if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Saved to board! Analysis will run in background."), + SnackBar( + content: Text("Saved ${_downloadedImageIds.length} image(s) to board! Analysis running."), ), ); @@ -235,7 +239,7 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { children: [ CircularProgressIndicator(), SizedBox(height: 16), - Text("Downloading image..."), + Text("Downloading image(s)..."), ], ), ); @@ -246,9 +250,21 @@ class _ShareHandlerPageState extends State<ShareHandlerPage> { children: [ Padding( padding: const EdgeInsets.all(16.0), - child: Text( - "Select a board", - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + child: Column( + children: [ + Text( + "Select a board", + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + if (_downloadedImageIds.length > 1) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + "(${_downloadedImageIds.length} images found)", + style: TextStyle(color: Colors.grey[600]), + ), + ), + ], ), ), Expanded(