import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart'; import 'package:image/image.dart'; import 'package:path_provider/path_provider.dart'; import 'package:wonders/logic/data/collectible_data.dart'; import 'package:wonders/logic/data/highlight_data.dart'; import 'package:wonders/logic/data/wonders_data/chichen_itza_data.dart'; import 'package:wonders/logic/data/wonders_data/christ_redeemer_data.dart'; import 'package:wonders/logic/data/wonders_data/great_wall_data.dart'; import 'package:wonders/logic/data/wonders_data/machu_picchu_data.dart'; import 'package:wonders/logic/data/wonders_data/petra_data.dart'; import 'package:wonders/logic/data/wonders_data/pyramids_giza_data.dart'; import 'package:wonders/logic/data/wonders_data/taj_mahal_data.dart'; import 'package:wonders/logic/data/wonders_data/colosseum_data.dart'; import 'package:wonders/common_libs.dart'; class ArtifactDownloadHelper extends StatefulWidget { const ArtifactDownloadHelper({super.key}); @override State createState() => _ArtifactDownloadHelperState(); } /// Using collectiblesData fetch the data for each artifact and download the image. /// Resize all images to have multiple sizes (small, medium, large) /// Save images using format [ID].jpg and [ID].json /// OR modify CollectibleData_helper.html to include all data in the collectiblesData list so no JSON is required. class _ArtifactDownloadHelperState extends State { late String imagesDir; final http = Client(); final List missingIds = []; @override void initState() { super.initState(); createDirectory(); } Future createDirectory() async { final rootDir = await getApplicationDocumentsDirectory(); imagesDir = '${rootDir.path}/met_collectibles'; await Directory(imagesDir).create(recursive: true); } @override Widget build(BuildContext context) { return Center( child: TextButton( onPressed: downloadArtifacts, child: Text('Download Artifacts'), ), ); } void downloadArtifacts() async { missingIds.clear(); /// Download collectibles for (var c in collectiblesData) { if (await downloadImageAndJson(c.artifactId) == false) { missingIds.add(c.artifactId); } } /// Download Highights for (var h in HighlightData.all) { if (await downloadImageAndJson(h.artifactId) == false) { missingIds.add(h.artifactId); } } /// Download search artifacts final searchData = ChichenItzaData().searchData + ChristRedeemerData().searchData + ColosseumData().searchData + GreatWallData().searchData + MachuPicchuData().searchData + PetraData().searchData + PyramidsGizaData().searchData + TajMahalData().searchData; for (var a in searchData) { final id = a.id.toString(); if (await downloadImageAndJson(id) == false) { missingIds.add(id); } final index = searchData.indexOf(a) + 1; if (index % 100 == 0) { debugPrint('$index/${searchData.length}'); } } debugPrint('Download complete :) Missing IDs: $missingIds'); } Future downloadImageAndJson(String id) async { File jsonFile = File('$imagesDir/$id.json'); late Map json; if (jsonFile.existsSync()) { json = jsonDecode(jsonFile.readAsStringSync()) as Map; } else { debugPrint('Downloading $id'); // Fetch JSON for id Uri uri = Uri.parse('https://collectionapi.metmuseum.org/public/collection/v1/objects/$id'); final response = await http.get(uri); json = jsonDecode(response.body) as Map; } // Check if primaryImage field is valid if (!json.containsKey('primaryImage') || json['primaryImage'].isEmpty) { return false; } // Download image final url = json['primaryImage'] as String; //bool isPublicDomain = json['isPublicDomain'] as bool; File imgFile = File('$imagesDir/$id.jpg'); // If image does not already exist, download it if (!imgFile.existsSync()) { await downloadImage(id, url); if (!imgFile.existsSync()) return false; } // Try to resize image if (await resizeImage(id, [600, 2000]) == false) { debugPrint('Failed to resize $id'); imgFile.deleteSync(); return false; } // Write JSON to file if (!jsonFile.existsSync()) { jsonFile.writeAsStringSync(jsonEncode(json)); debugPrint('json saved @ ${jsonFile.path}'); } return true; } Future downloadImage(String id, String url) async { //final sizes = [400, 800, 1600, 3000]; debugPrint('Downloading $url to $imagesDir'); final imgResponse = await get(Uri.parse(url)); // If the image is less than a KB, it's probably a 404 image. if (imgResponse.bodyBytes.lengthInBytes < 2000) { return false; } File file = File('$imagesDir/$id.jpg'); file.writeAsBytesSync(imgResponse.bodyBytes); debugPrint('img saved @ ${file.path}'); return true; } Future resizeImage(String id, List sizes) async { final srcFile = File('$imagesDir/$id.jpg'); //debugPrint('Resizing $id...'); try { final img = decodeJpg(srcFile.readAsBytesSync()); if (img != null) { // Write various sizes to disk for (var size in sizes) { final resizedFile = File('$imagesDir/${id}_$size.jpg'); if (await resizedFile.exists()) continue; final resizedImg = copyResize(img, width: size); await resizedFile.writeAsBytes(encodeJpg(resizedImg, quality: 90)); debugPrint('Resized ${id}_$size'); } return true; } } catch (e) { debugPrint('Failed to resize $id'); debugPrint(e.toString()); } return false; } }