diff --git a/lib/_tools/artifact_download_helper.dart b/lib/_tools/artifact_download_helper.dart new file mode 100644 index 00000000..fe5c5404 --- /dev/null +++ b/lib/_tools/artifact_download_helper.dart @@ -0,0 +1,137 @@ +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/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 '../common_libs.dart'; +import '../logic/data/collectible_data.dart'; +import '../logic/data/wonders_data/colosseum_data.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'), + ), + ); + } + + 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); + print('img saved @ ${file.path}'); + return true; + } + + Future downloadImageAndJson(String id) async { + File imgFile = File('$imagesDir/$id.jpg'); + if (imgFile.existsSync()) { + print('Skipping $id'); + await resizeImage(id, 600); + return; + } + Uri uri = Uri.parse('https://collectionapi.metmuseum.org/public/collection/v1/objects/$id'); + print('Downloading $id'); + final response = await http.get(uri); + Map json = jsonDecode(response.body) as Map; + if (!json.containsKey('primaryImage') || json['primaryImage'].isEmpty) { + print('Missing $id'); + missingIds.add(id); + return; + } + final url = json['primaryImage'] as String; + //bool isPublicDomain = json['isPublicDomain'] as bool; + final downloadSuccess = await downloadImage(id, url); + if (downloadSuccess) { + File file = File('$imagesDir/$id.json'); + file.writeAsStringSync(response.body); + print('json saved @ ${file.path}'); + } else { + print('Missing $id'); + missingIds.add(id); + } + } + + void downloadArtifacts() async { + /// Download collectibles + // for (var c in collectiblesData) { + // downloadImageAndJson(c.artifactId); + // } + missingIds.clear(); + + /// 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) { + await downloadImageAndJson(a.id.toString()); + print('${searchData.indexOf(a) + 1}/${searchData.length}'); + } + print('Missing IDs: $missingIds'); + } + + Future resizeImage(String id, int size) async { + final resizedFile = File('$imagesDir/${id}_$size.jpg'); + final srcFile = File('$imagesDir/$id.jpg'); + print('Resizing $id...'); + if (resizedFile.existsSync() || !srcFile.existsSync()) return; + final img = decodeJpg(srcFile.readAsBytesSync()); + if (img != null) { + final resizedImg = copyResize(img, width: size); + resizedFile.writeAsBytesSync(encodeJpg(resizedImg)); + print('Resized $id'); + } else { + print('Failed to resize $id'); + } + } +} diff --git a/lib/logic/data/wonders_data/search/search_data.dart b/lib/logic/data/wonders_data/search/search_data.dart index 7932fa33..dd13fd9d 100644 --- a/lib/logic/data/wonders_data/search/search_data.dart +++ b/lib/logic/data/wonders_data/search/search_data.dart @@ -1,6 +1,6 @@ class SearchData { static const String baseImagePath = 'https://images.metmuseum.org/CRDImages/'; - + static const missingIds = [313256, 327544, 327596, 545776, 38549, 38578, 38473, 38598, 38153, 38203, 64486, 64487]; const SearchData(this.year, this.id, this.title, this.keywords, this.imagePath, [this.aspectRatio = 0]); final int year; final int id; diff --git a/lib/main.dart b/lib/main.dart index b9a2eb71..92fe2edf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -11,6 +11,8 @@ import 'package:wonders/logic/unsplash_logic.dart'; import 'package:wonders/logic/wallpaper_logic.dart'; import 'package:wonders/logic/wonders_logic.dart'; +import '_tools/artifact_download_helper.dart'; + void main() async { WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); // Keep native splash screen up until app is finished bootstrapping @@ -30,6 +32,7 @@ class WondersApp extends StatelessWidget with GetItMixin { WondersApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { + return MaterialApp(home: ArtifactDownloadHelper()); final locale = watchX((SettingsLogic s) => s.currentLocale); return MaterialApp.router( routeInformationProvider: appRouter.routeInformationProvider, diff --git a/pubspec.lock b/pubspec.lock index c9d6838b..9a812318 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + sha256: "20071638cbe4e5964a427cfa0e86dce55d060bc7d82d56f3554095d7239a8765" url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.4.2" args: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -377,13 +377,13 @@ packages: source: hosted version: "2.1.3" image: - dependency: transitive + dependency: "direct main" description: name: image - sha256: a72242c9a0ffb65d03de1b7113bc4e189686fc07c7147b8b41811d0dd0e0d9bf + sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" url: "https://pub.dev" source: hosted - version: "4.0.17" + version: "4.1.3" image_fade: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a142c7bd..cd343439 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,6 +33,7 @@ dependencies: google_maps_flutter_web: ^0.5.4+2 go_router: ^6.5.5 http: ^0.13.5 + image: ^4.1.3 image_fade: ^0.6.2 image_gallery_saver: ^2.0.3 intl: ^0.18.1 diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d2071..903f4899 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS