From 8e818428bfa7ed91cd43f1164013e85f3fb80882 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 8 Mar 2023 21:10:16 +0100 Subject: [PATCH 001/150] gen code --- android/gradle.properties | 1 + lib/bridge_definitions.dart | 6 +- lib/bridge_generated.dart | 167 ++++++++++++++++++++++++++++++++- lib/main.dart | 1 + native/src/bridge_generated.rs | 9 +- pubspec.yaml | 2 +- 6 files changed, 169 insertions(+), 17 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 94adc3a..5c76181 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +ANDROID_NDK=/home/julien/Android/Sdk/ndk-bundle/ diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index fadd6b6..a2ace8d 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -1,9 +1,7 @@ // AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.62.1. -// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member +// Generated by `flutter_rust_bridge`@ 1.68.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const -import 'bridge_generated.io.dart' - if (dart.library.html) 'bridge_generated.web.dart'; import 'dart:convert'; import 'dart:async'; import 'package:meta/meta.dart'; diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 7501e4b..227be62 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -1,14 +1,19 @@ // AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.62.1. -// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member +// Generated by `flutter_rust_bridge`@ 1.68.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const import "bridge_definitions.dart"; import 'dart:convert'; import 'dart:async'; import 'package:meta/meta.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; -import 'bridge_generated.io.dart' - if (dart.library.html) 'bridge_generated.web.dart'; + +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; + +import 'dart:ffi' as ffi; class NativeImpl implements Native { final NativePlatform _platform; @@ -72,3 +77,157 @@ class NativeImpl implements Native { // Section: api2wire // Section: finalizer + +class NativePlatform extends FlutterRustBridgeBase { + NativePlatform(ffi.DynamicLibrary dylib) : super(NativeWire(dylib)); + +// Section: api2wire + +// Section: finalizer + +// Section: api_fill_to_wire +} + +// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. + +/// generated by flutter_rust_bridge +class NativeWire implements FlutterRustBridgeWireBase { + @internal + late final dartApi = DartApiDl(init_frb_dart_api_dl); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + NativeWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + NativeWire.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + void store_dart_post_cobject( + DartPostCObjectFnType ptr, + ) { + return _store_dart_post_cobject( + ptr, + ); + } + + late final _store_dart_post_cobjectPtr = + _lookup>( + 'store_dart_post_cobject'); + late final _store_dart_post_cobject = _store_dart_post_cobjectPtr + .asFunction(); + + Object get_dart_object( + int ptr, + ) { + return _get_dart_object( + ptr, + ); + } + + late final _get_dart_objectPtr = + _lookup>( + 'get_dart_object'); + late final _get_dart_object = + _get_dart_objectPtr.asFunction(); + + void drop_dart_object( + int ptr, + ) { + return _drop_dart_object( + ptr, + ); + } + + late final _drop_dart_objectPtr = + _lookup>( + 'drop_dart_object'); + late final _drop_dart_object = + _drop_dart_objectPtr.asFunction(); + + int new_dart_opaque( + Object handle, + ) { + return _new_dart_opaque( + handle, + ); + } + + late final _new_dart_opaquePtr = + _lookup>( + 'new_dart_opaque'); + late final _new_dart_opaque = + _new_dart_opaquePtr.asFunction(); + + int init_frb_dart_api_dl( + ffi.Pointer obj, + ) { + return _init_frb_dart_api_dl( + obj, + ); + } + + late final _init_frb_dart_api_dlPtr = + _lookup)>>( + 'init_frb_dart_api_dl'); + late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr + .asFunction)>(); + + void wire_platform( + int port_, + ) { + return _wire_platform( + port_, + ); + } + + late final _wire_platformPtr = + _lookup>( + 'wire_platform'); + late final _wire_platform = + _wire_platformPtr.asFunction(); + + void wire_rust_release_mode( + int port_, + ) { + return _wire_rust_release_mode( + port_, + ); + } + + late final _wire_rust_release_modePtr = + _lookup>( + 'wire_rust_release_mode'); + late final _wire_rust_release_mode = + _wire_rust_release_modePtr.asFunction(); + + void free_WireSyncReturn( + WireSyncReturn ptr, + ) { + return _free_WireSyncReturn( + ptr, + ); + } + + late final _free_WireSyncReturnPtr = + _lookup>( + 'free_WireSyncReturn'); + late final _free_WireSyncReturn = + _free_WireSyncReturnPtr.asFunction(); +} + +class _Dart_Handle extends ffi.Opaque {} + +typedef DartPostCObjectFnType = ffi.Pointer< + ffi.NativeFunction)>>; +typedef DartPort = ffi.Int64; diff --git a/lib/main.dart b/lib/main.dart index d151ca4..f4f0614 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; void main() { diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 9f63b66..dac5a8e 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -9,7 +9,7 @@ clippy::too_many_arguments )] // AUTO GENERATED FILE, DO NOT EDIT. -// Generated by `flutter_rust_bridge`@ 1.62.1. +// Generated by `flutter_rust_bridge`@ 1.68.0. use crate::api::*; use core::panic::UnwindSafe; @@ -86,13 +86,6 @@ support::lazy_static! { pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); } -/// cbindgen:ignore -#[cfg(target_family = "wasm")] -#[path = "bridge_generated.web.rs"] -mod web; -#[cfg(target_family = "wasm")] -pub use web::*; - #[cfg(not(target_family = "wasm"))] #[path = "bridge_generated.io.rs"] mod io; diff --git a/pubspec.yaml b/pubspec.yaml index e7f3e0b..298476d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,7 +48,7 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 - ffigen: ^7.2.4 + ffigen: ^7.2.7 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From ec9c83359a0dc81ec64f52b9acf257b22dc0e293 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 9 Mar 2023 19:20:33 +0100 Subject: [PATCH 002/150] Import BookMetadataFinder rust code and basic UI --- lib/bridge_definitions.dart | 34 +- lib/bridge_generated.dart | 152 +++-- lib/main.dart | 129 ++-- native/.gitignore | 1 + native/Cargo.toml | 8 + native/src/api.rs | 160 +++-- native/src/babelio.rs | 91 +++ native/src/babelio/parser.rs | 212 +++++++ native/src/babelio/request.rs | 69 +++ native/src/babelio/test/get_book.html | 581 ++++++++++++++++++ native/src/babelio/test/get_book_minimal.html | 147 +++++ native/src/babelio/test/get_see_more.html | 1 + native/src/babelio/test/search_by_isbn | 0 native/src/bridge_generated.io.rs | 61 +- native/src/bridge_generated.rs | 49 +- native/src/cached_client.rs | 22 + native/src/common.rs | 30 + native/src/google_books.rs | 17 + native/src/google_books/parser.rs | 174 ++++++ native/src/google_books/request.rs | 13 + .../src/google_books/test/isbn_response.html | 67 ++ .../google_books/test/self_link_response.html | 63 ++ native/src/image_tools.rs | 11 + native/src/jwt_decoder.rs | 23 + native/src/leboncoin.rs | 52 ++ native/src/leboncoin/parser.rs | 61 ++ native/src/leboncoin/request.rs | 347 +++++++++++ native/src/lib.rs | 8 + native/src/publisher.rs | 5 + 29 files changed, 2390 insertions(+), 198 deletions(-) create mode 100644 native/.gitignore create mode 100644 native/src/babelio.rs create mode 100644 native/src/babelio/parser.rs create mode 100644 native/src/babelio/request.rs create mode 100644 native/src/babelio/test/get_book.html create mode 100644 native/src/babelio/test/get_book_minimal.html create mode 100644 native/src/babelio/test/get_see_more.html create mode 100644 native/src/babelio/test/search_by_isbn create mode 100644 native/src/cached_client.rs create mode 100644 native/src/common.rs create mode 100644 native/src/google_books.rs create mode 100644 native/src/google_books/parser.rs create mode 100644 native/src/google_books/request.rs create mode 100644 native/src/google_books/test/isbn_response.html create mode 100644 native/src/google_books/test/self_link_response.html create mode 100644 native/src/image_tools.rs create mode 100644 native/src/jwt_decoder.rs create mode 100644 native/src/leboncoin.rs create mode 100644 native/src/leboncoin/parser.rs create mode 100644 native/src/leboncoin/request.rs create mode 100644 native/src/publisher.rs diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index a2ace8d..29fd663 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -2,28 +2,28 @@ // Generated by `flutter_rust_bridge`@ 1.68.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const -import 'dart:convert'; import 'dart:async'; -import 'package:meta/meta.dart'; +import 'dart:convert'; + import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:meta/meta.dart'; abstract class Native { - Future platform({dynamic hint}); - - FlutterRustBridgeTaskConstMeta get kPlatformConstMeta; + Future getMetadataFromImages({required List imgsPath, dynamic hint}); - Future rustReleaseMode({dynamic hint}); - - FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta; + FlutterRustBridgeTaskConstMeta get kGetMetadataFromImagesConstMeta; } -enum Platform { - Unknown, - Android, - Ios, - Windows, - Unix, - MacIntel, - MacApple, - Wasm, +class Ad { + String title; + String description; + int priceCent; + List imgsPath; + + Ad({ + required this.title, + required this.description, + required this.priceCent, + required this.imgsPath, + }); } diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 227be62..7ae6c5e 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -24,36 +24,23 @@ class NativeImpl implements Native { factory NativeImpl.wasm(FutureOr module) => NativeImpl(module as ExternalLibrary); NativeImpl.raw(this._platform); - Future platform({dynamic hint}) { + Future getMetadataFromImages( + {required List imgsPath, dynamic hint}) { + var arg0 = _platform.api2wire_StringList(imgsPath); return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_platform(port_), - parseSuccessData: _wire2api_platform, - constMeta: kPlatformConstMeta, - argValues: [], + callFfi: (port_) => + _platform.inner.wire_get_metadata_from_images(port_, arg0), + parseSuccessData: _wire2api_ad, + constMeta: kGetMetadataFromImagesConstMeta, + argValues: [imgsPath], hint: hint, )); } - FlutterRustBridgeTaskConstMeta get kPlatformConstMeta => + FlutterRustBridgeTaskConstMeta get kGetMetadataFromImagesConstMeta => const FlutterRustBridgeTaskConstMeta( - debugName: "platform", - argNames: [], - ); - - Future rustReleaseMode({dynamic hint}) { - return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_rust_release_mode(port_), - parseSuccessData: _wire2api_bool, - constMeta: kRustReleaseModeConstMeta, - argValues: [], - hint: hint, - )); - } - - FlutterRustBridgeTaskConstMeta get kRustReleaseModeConstMeta => - const FlutterRustBridgeTaskConstMeta( - debugName: "rust_release_mode", - argNames: [], + debugName: "get_metadata_from_images", + argNames: ["imgsPath"], ); void dispose() { @@ -61,21 +48,46 @@ class NativeImpl implements Native { } // Section: wire2api - bool _wire2api_bool(dynamic raw) { - return raw as bool; + String _wire2api_String(dynamic raw) { + return raw as String; + } + + List _wire2api_StringList(dynamic raw) { + return (raw as List).cast(); + } + + Ad _wire2api_ad(dynamic raw) { + final arr = raw as List; + if (arr.length != 4) + throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); + return Ad( + title: _wire2api_String(arr[0]), + description: _wire2api_String(arr[1]), + priceCent: _wire2api_i32(arr[2]), + imgsPath: _wire2api_StringList(arr[3]), + ); } int _wire2api_i32(dynamic raw) { return raw as int; } - Platform _wire2api_platform(dynamic raw) { - return Platform.values[raw]; + int _wire2api_u8(dynamic raw) { + return raw as int; + } + + Uint8List _wire2api_uint_8_list(dynamic raw) { + return raw as Uint8List; } } // Section: api2wire +@protected +int api2wire_u8(int raw) { + return raw; +} + // Section: finalizer class NativePlatform extends FlutterRustBridgeBase { @@ -83,6 +95,26 @@ class NativePlatform extends FlutterRustBridgeBase { // Section: api2wire + @protected + ffi.Pointer api2wire_String(String raw) { + return api2wire_uint_8_list(utf8.encoder.convert(raw)); + } + + @protected + ffi.Pointer api2wire_StringList(List raw) { + final ans = inner.new_StringList_0(raw.length); + for (var i = 0; i < raw.length; i++) { + ans.ref.ptr[i] = api2wire_String(raw[i]); + } + return ans; + } + + @protected + ffi.Pointer api2wire_uint_8_list(Uint8List raw) { + final ans = inner.new_uint_8_list_0(raw.length); + ans.ref.ptr.asTypedList(raw.length).setAll(0, raw); + return ans; + } // Section: finalizer // Section: api_fill_to_wire @@ -183,33 +215,51 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr .asFunction)>(); - void wire_platform( + void wire_get_metadata_from_images( int port_, + ffi.Pointer imgs_path, ) { - return _wire_platform( + return _wire_get_metadata_from_images( port_, + imgs_path, ); } - late final _wire_platformPtr = - _lookup>( - 'wire_platform'); - late final _wire_platform = - _wire_platformPtr.asFunction(); + late final _wire_get_metadata_from_imagesPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, + ffi.Pointer)>>('wire_get_metadata_from_images'); + late final _wire_get_metadata_from_images = _wire_get_metadata_from_imagesPtr + .asFunction)>(); - void wire_rust_release_mode( - int port_, + ffi.Pointer new_StringList_0( + int len, ) { - return _wire_rust_release_mode( - port_, + return _new_StringList_0( + len, + ); + } + + late final _new_StringList_0Ptr = _lookup< + ffi.NativeFunction Function(ffi.Int32)>>( + 'new_StringList_0'); + late final _new_StringList_0 = _new_StringList_0Ptr + .asFunction Function(int)>(); + + ffi.Pointer new_uint_8_list_0( + int len, + ) { + return _new_uint_8_list_0( + len, ); } - late final _wire_rust_release_modePtr = - _lookup>( - 'wire_rust_release_mode'); - late final _wire_rust_release_mode = - _wire_rust_release_modePtr.asFunction(); + late final _new_uint_8_list_0Ptr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Int32)>>('new_uint_8_list_0'); + late final _new_uint_8_list_0 = _new_uint_8_list_0Ptr + .asFunction Function(int)>(); void free_WireSyncReturn( WireSyncReturn ptr, @@ -228,6 +278,20 @@ class NativeWire implements FlutterRustBridgeWireBase { class _Dart_Handle extends ffi.Opaque {} +class wire_uint_8_list extends ffi.Struct { + external ffi.Pointer ptr; + + @ffi.Int32() + external int len; +} + +class wire_StringList extends ffi.Struct { + external ffi.Pointer> ptr; + + @ffi.Int32() + external int len; +} + typedef DartPostCObjectFnType = ffi.Pointer< ffi.NativeFunction)>>; typedef DartPort = ffi.Int64; diff --git a/lib/main.dart b/lib/main.dart index f4f0614..29b4a08 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -50,52 +50,25 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - // These futures belong to the state and are only initialized once, - // in the initState method. - late Future platform; - late Future isRelease; - + late Future ad; @override void initState() { super.initState(); - platform = api.platform(); - isRelease = api.rustReleaseMode(); + ad = api.getMetadataFromImages(imgsPath: [ + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg' + ]); //.then((ad) => print('ad = $ad')); } @override Widget build(BuildContext context) { - // This method is rerun every time setState is called. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. return Scaffold( appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. title: Text(widget.title), ), body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Invoke "debug painting" (press "p" in the console, choose the - // "Toggle Debug Paint" action from the Flutter Inspector in Android - // Studio, or the "Toggle Debug Paint" command in Visual Studio Code) - // to see the wireframe for each widget. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text("You're running on"), // To render the results of a Future, a FutureBuilder is used which // turns a Future into an AsyncSnapshot, which can be used to // extract the error state, the loading state and the data if @@ -104,10 +77,8 @@ class _MyHomePageState extends State { // Here, the generic type that the FutureBuilder manages is // explicitly named, because if omitted the snapshot will have the // type of AsyncSnapshot. - FutureBuilder>( - // We await two unrelated futures here, so the type has to be - // List. - future: Future.wait([platform, isRelease]), + FutureBuilder( + future: ad, builder: (context, snap) { final style = Theme.of(context).textTheme.headline4; if (snap.error != null) { @@ -116,29 +87,14 @@ class _MyHomePageState extends State { debugPrint(snap.error.toString()); return Tooltip( message: snap.error.toString(), - child: Text('Unknown OS', style: style), + child: Text('Error during image decoding', style: style), ); } - // Guard return here, the data is not ready yet. - final data = snap.data; - if (data == null) return const CircularProgressIndicator(); - - // Finally, retrieve the data expected in the same order provided - // to the FutureBuilder.future. - final Platform platform = data[0]; - final release = data[1] ? 'Release' : 'Debug'; - final text = const { - Platform.Android: 'Android', - Platform.Ios: 'iOS', - Platform.MacApple: 'MacOS with Apple Silicon', - Platform.MacIntel: 'MacOS', - Platform.Windows: 'Windows', - Platform.Unix: 'Unix', - Platform.Wasm: 'the Web', - }[platform] ?? - 'Unknown OS'; - return Text('$text ($release)', style: style); + final ad = snap.data; + if (ad == null) return const Text("Extracting info from images"); + + return AdPage(ad: ad); }, ) ], @@ -147,3 +103,66 @@ class _MyHomePageState extends State { ); } } + +extension IntExt on int { + int divide(int other) => this ~/ other; +} + +extension DoubleExt on double { + double multiply(double other) => this * other; +} + +class AdPage extends StatefulWidget { + AdPage({super.key, required Ad ad}) : initialAd = ad; + + final Ad initialAd; + + @override + State createState() => _AdPageState(); +} + +class _AdPageState extends State { + late Ad ad; + + @override + void initState() { + super.initState(); + ad = widget.initialAd; + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TextFormField( + initialValue: ad.title, + onChanged: (newText) { + setState(() { + ad.title = newText; + }); + }, + ), + TextFormField( + initialValue: ad.description, + maxLines: 20, + onChanged: (newText) { + setState(() { + ad.description = newText; + }); + }, + ), + TextFormField( + initialValue: ad.priceCent /*?*/ .divide(100).toString(), + onChanged: (newText) => setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), + ), + ElevatedButton( + onPressed: ad.priceCent == null + ? null + : () { + print('Try to publish'); + }, + child: const Text("Publish")) + ], + ); + } +} diff --git a/native/.gitignore b/native/.gitignore new file mode 100644 index 0000000..107eafc --- /dev/null +++ b/native/.gitignore @@ -0,0 +1 @@ +personal_info.rs diff --git a/native/Cargo.toml b/native/Cargo.toml index 667e107..a013ab0 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -11,3 +11,11 @@ crate-type = ["cdylib", "staticlib"] [dependencies] anyhow = "1" flutter_rust_bridge = "1" +#reqwest = "0.11.14" +base64 = "0.21.0" +itertools = "0.10.5" +regex = "1.7.1" +reqwest = { version = "0.11.14", features = ["blocking", "json", "multipart"] } +scraper = "0.14.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.91" \ No newline at end of file diff --git a/native/src/api.rs b/native/src/api.rs index 976bb9d..65e1ea0 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,59 +1,111 @@ -// This is the entry point of your Rust library. -// When adding new code to your project, note that only items used -// here will be transformed to their Dart equivalents. - -// A plain enum without any fields. This is similar to Dart- or C-style enums. -// flutter_rust_bridge is capable of generating code for enums with fields -// (@freezed classes in Dart and tagged unions in C). -pub enum Platform { - Unknown, - Android, - Ios, - Windows, - Unix, - MacIntel, - MacApple, - Wasm, -} +use std::process::Command; +use itertools::Itertools; +use crate::{babelio, common, google_books, leboncoin}; +use crate::common::{Ad, BookMetaData}; + +pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { + let isbns: Vec = imgs_path + .clone() + .into_iter() + .map(|picture_path| { + println!("{picture_path}"); + let output = Command::new( + "/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode", + ) + .arg("-in=".to_string() + &picture_path) + .output() + .expect("failed to execute process"); + let output = std::str::from_utf8(&output.stdout).unwrap(); + println!("output is {:?}", output); + output + .split_ascii_whitespace() + .map(|x| x.to_string()) + .collect_vec() + }) + .flatten() + .unique() + .collect(); + + println!("isbns {:?}", isbns); + + let book_metadata_providers: Vec> = vec![ + Box::new(babelio::Babelio {}), + Box::new(google_books::GoogleBooks {}), + ]; -// A function definition in Rust. Similar to Dart, the return type must always be named -// and is never inferred. -pub fn platform() -> Platform { - // This is a macro, a special expression that expands into code. In Rust, all macros - // end with an exclamation mark and can be invoked with all kinds of brackets (parentheses, - // brackets and curly braces). However, certain conventions exist, for example the - // vector macro is almost always invoked as vec![..]. - // - // The cfg!() macro returns a boolean value based on the current compiler configuration. - // When attached to expressions (#[cfg(..)] form), they show or hide the expression at compile time. - // Here, however, they evaluate to runtime values, which may or may not be optimized out - // by the compiler. A variety of configurations are demonstrated here which cover most of - // the modern oeprating systems. Try running the Flutter application on different machines - // and see if it matches your expected OS. - // - // Furthermore, in Rust, the last expression in a function is the return value and does - // not have the trailing semicolon. This entire if-else chain forms a single expression. - if cfg!(windows) { - Platform::Windows - } else if cfg!(target_os = "android") { - Platform::Android - } else if cfg!(target_os = "ios") { - Platform::Ios - } else if cfg!(all(target_os = "macos", target_arch = "aarch64")) { - Platform::MacApple - } else if cfg!(target_os = "macos") { - Platform::MacIntel - } else if cfg!(target_family = "wasm") { - Platform::Wasm - } else if cfg!(unix) { - Platform::Unix - } else { - Platform::Unknown + let books: Vec = isbns + .iter() + .map(|isbn| { + for provider in &book_metadata_providers { + let res = provider.get_book_metadata_from_isbn(&isbn); + if let Some(r) = res { + return r; + } + } + panic!("No provider find any information on book {}", isbn) + /* book_metadata_providers[0] + .get_book_metadata_from_isbn(&isbn) + .unwrap() */ + }) + .collect(); + let books_titles = books.iter().map(book_format_title_and_author).join("\n"); + let blurbs = books + .iter() + .map(|b| { + format!( + "{}:\n{}\n", + book_format_title_and_author(b), + b.blurb.as_ref().unwrap() + ) + }) + .join("\n"); + let keywords = books.iter().flat_map(|b| &b.keywords).unique().join(", "); + + let custom_message = leboncoin::personal_info::CUSTOM_MESSAGE; + + let mut ad_description = books_titles + "\n\nRésumé:\n" + &blurbs + "\n" + &custom_message; + if !keywords.is_empty() { + ad_description = ad_description + "\n\nMots-clés:\n" + &keywords; } + + println!("ad_description: {:#?}", ad_description); + println!("ad_description: {}", ad_description); + + common::Ad { + title: if books.len() == 1 { + books.first().unwrap().title.clone() + } else { + todo!() + }, + description: ad_description, + price_cent: 1000, + imgs_path, + } + + /*let publisher = leboncoin::Leboncoin {}; + + + publisher::Publisher::publish(&publisher, ad);*/ } -// The convention for Rust identifiers is the snake_case, -// and they are automatically converted to camelCase on the Dart side. -pub fn rust_release_mode() -> bool { - cfg!(not(debug_assertions)) +fn book_format_title_and_author(book: &BookMetaData) -> String { + format!( + "\"{}\" {}", + book.title, + vec_fmt( + book.authors + .iter() + .map(|a| format!("{} {}", a.first_name, a.last_name)) + .collect_vec() + ) + ) +} + +fn vec_fmt(vec: Vec) -> String { + match vec.len() { + 0 => "".to_string(), + 1 => format!("de {}", vec[0]), + 2 => format!("de {} et {}", vec[0], vec[1]), + _ => panic!("More than 2 authors"), + } } diff --git a/native/src/babelio.rs b/native/src/babelio.rs new file mode 100644 index 0000000..9878b25 --- /dev/null +++ b/native/src/babelio.rs @@ -0,0 +1,91 @@ +use crate::{cached_client::CachedClient, common}; +mod parser; +mod request; + +pub struct Babelio; + +impl common::Provider for Babelio { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let client = reqwest::blocking::Client::builder().build().unwrap(); + let cached_client = CachedClient { + http_client: client, + }; + let book_url = request::get_book_url(&cached_client, isbn)?; + let book_page = request::get_book_page(&cached_client, book_url); + let blurb_res = parser::extract_blurb(&book_page); + + let raw_blurb = match blurb_res { + parser::BlurbRes::SmallBlurb(blurb) => blurb, + parser::BlurbRes::BigBlurb(id_obj) => { + request::get_book_blurb_see_more(&cached_client, &id_obj) + } + }; + + let mut res = parser::extract_title_author_keywords(&book_page); + res.blurb = parser::parse_blurb(&raw_blurb); + Some(res) + } +} + +#[cfg(test)] +mod tests { + use crate::common::{Author, BookMetaData, Provider}; + + use super::*; + + #[test] + fn get_metadata_from_normal_book() { + let isbn = "9782266071529"; + let md = Babelio {}.get_book_metadata_from_isbn(isbn); + assert_eq!(md, Some(BookMetaData { + title: "Le nom de la bête".to_string(), + authors: vec![Author{first_name:"Daniel".to_string(), last_name: "Easterman".to_string()}], + blurb: Some("Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède\n".to_string()), + keywords: + [ + "roman", + "fantastique", + "policier historique", + "romans policiers et polars", + "thriller", + "terreur", + "action", + "démocratie", + "mystique", + "islam", + "intégrisme religieux", + "catholicisme", + "religion", + "terrorisme", + "extrémisme", + "egypte", + "médias", + "thriller religieux", + "littérature irlandaise", + "irlande" + ] + .map(|s| s.to_string()) + .to_vec(), + })); + } + + #[test] + fn get_metadata_from_book_with_see_more_bug() { + let isbn = "9782070541898"; + let md = Babelio {}.get_book_metadata_from_isbn(isbn); + assert_eq!(md, Some(BookMetaData { + title: "À la croisée des mondes, tome 2 : La tour des anges".to_string(), + authors: vec![Author{first_name:"Philip".to_string(), last_name: "Pullman".to_string()}], + blurb: Some(r#"Le jeune Will, à la recherche de son père disparu depuis de longues années, est persuadé d’avoir tué un homme. Dans sa fuite, il franchit une brèche presque invisible qui lui permet de passer dans un monde parallèle. +Là, à Cittàgazze, la ville au-delà de l’Aurore, il rencontre Lyra, l’héroïne des "Royaumes du Nord". Elle aussi cherche à rejoindre son père, elle aussi est investie d’une mission dont elle ne connaît pas encore toute l’importance. +Ensemble, les deux enfants devront lutter contre les forces obscures du mal et, pour accomplir leur quête, pénétrer dans la mystérieuse tour des Anges… +"#.to_string()), + keywords: + [ + "aventure", "saga", "roman", "fantasy", "fantastique", "littérature jeunesse", "jeunesse", "steampunk", "littérature pour adolescents", "enfants", "magie", "amitié", "enfance", "science-fiction", "univers parallèles", "religion", "adolescence", "littérature anglaise", "littérature britannique", "20ème siècle", + ] + .map(|s| s.to_string()) + .to_vec(), + })); + } +} diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs new file mode 100644 index 0000000..d12517e --- /dev/null +++ b/native/src/babelio/parser.rs @@ -0,0 +1,212 @@ +use crate::common::{html_select, BookMetaData}; +use itertools::Itertools; + +#[derive(PartialEq, Debug)] +pub enum BlurbRes { + SmallBlurb(String), + BigBlurb(String), +} + +pub fn extract_blurb(html: &str) -> BlurbRes { + let doc = scraper::Html::parse_document(html); + + let selector = scraper::Selector::parse("#d_bio").expect( + format!( + "Response should contain a element whose id is 'd_bio', html is {:?}", + html + ) + .as_str(), + ); + let mut res = doc.select(&selector); + + let d_bio = res.next().expect( + format!( + "There should be exactly one element with id 'd_bio', html {:?}", + html + ) + .as_str(), + ); + + // Some books do not folow the general strucuture: https://www.babelio.com/livres/Pullman--la-croisee-des-mondes-tome-2--La-tour-des-anges/59278 + // It looks like a bug from Babelio because the style span do not close + // So I must use a css-style selector instead of going down the DOM tree + let s = scraper::Selector::parse("a[onclick^=\"javascript\"]").unwrap(); + let mut onclick_elements = d_bio.select(&s); + let on_click_element = onclick_elements.next(); + if let Some(_) = onclick_elements.next() { + panic!("There should be one or zero element with onclick attribute in the d_bio element"); + } + match on_click_element { + None => { + let dbio_second_to_last_child = d_bio + .children() + .rev() + .nth(1) + .expect("d_bio should have a second to last children (the style span)"); + BlurbRes::SmallBlurb( + dbio_second_to_last_child + .value() + .as_text() + .unwrap() + .to_string(), + ) + } + Some(on_click_element) => { + let on_click = on_click_element + .value() + .attr("onclick") + .expect(" should have a 'onclick' attribute"); + let re = regex::Regex::new(r"javascript:voir_plus_a\('#d_bio',1,(\d+)\);").unwrap(); + + let single_capture = re + .captures_iter(on_click) + .next() + .expect("The onclick should match with the regex"); + let id_obj = &single_capture[1]; + BlurbRes::BigBlurb(String::from(id_obj)) + } + } +} + +pub fn extract_title_author_keywords(html: &str) -> BookMetaData { + let doc = scraper::Html::parse_document(html); + + let book_select = html_select("div[itemscope][itemtype=\"https://schema.org/Book\"]"); + let res = doc.select(&book_select); + let book_scope = res.exactly_one().expect(format!( + "Response should contain a element whose with id is itemscope and itemtype=\"https://schema.org/Book\", html is {:?}", + html + ) + .as_str()); + let title_select = html_select("[itemprop=\"name\"]"); + let mut res2 = book_scope.select(&title_select).into_iter(); + let title = res2 + .next() + .expect("There should be at least one element with itemprop=\"name\"") + .first_child() + .unwrap() + .first_child() + .unwrap() + .value() + .as_text() + .unwrap() + .trim() + .to_string(); + + let binding = + html_select("[itemprop=\"author\"][itemscope][itemtype=\"https://schema.org/Person\"]"); + let r = book_scope.select(&binding); + + let authors = r + .map(|author_scope| { + let author_span = author_scope + .first_child() + .expect("author_scope shoud have a first child ") + .first_child() + .expect("author scope > a shoud have a first child "); + let first_name = author_span + .first_child() + .expect("author scope > a > span shoud have a first child which is first name") + .value() + .as_text() + .expect("should be a text") + .trim() + .to_string(); + let last_name = author_span + .children() + .nth(1) + .expect("author scope > a > span shoud have a second child which is the last name") + .first_child() + .unwrap() + .value() + .as_text() + .expect("should be a text") + .trim() + .to_string(); + crate::common::Author { + first_name, + last_name, + } + }) + .collect_vec(); + + let keywords_scope = book_scope + .select(&html_select("[class=\"tags\"]")) + .exactly_one() + .unwrap(); + let keywords = keywords_scope + .children() + .filter_map(|c| { + Some( + c.first_child()? + .value() + .as_text() + .expect("c should be a text") + .trim() + .to_string(), + ) + }) + .collect(); + BookMetaData { + title, + authors, + keywords, + ..Default::default() + } +} + +pub fn parse_blurb(raw_blurb: &str) -> Option { + Some(raw_blurb.trim().replace("
", "\n")) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn extract_id_obj_from_file() { + let html = std::fs::read_to_string("src/babelio/test/get_book.html").unwrap(); + let id_obj = extract_blurb(&html); + assert_eq!(id_obj, BlurbRes::BigBlurb("827593".to_string())); + } + #[test] + pub fn extract_title_author_keywords_from_file() { + let html = std::fs::read_to_string("src/babelio/test/get_book_minimal.html").unwrap(); + let title_author_keywords = extract_title_author_keywords(&html); + assert_eq!( + title_author_keywords, + BookMetaData { + title: "Le nom de la bête".to_string(), + authors: vec![crate::common::Author { + first_name: "Daniel".to_string(), + last_name: "Easterman".to_string() + }], + blurb: None, + keywords: [ + "roman", + "fantastique", + "policier historique", + "romans policiers et polars", + "thriller", + "terreur", + "action", + "démocratie", + "mystique", + "islam", + "intégrisme religieux", + "catholicisme", + "religion", + "terrorisme", + "extrémisme", + "egypte", + "médias", + "thriller religieux", + "littérature irlandaise", + "irlande" + ] + .map(|s| s.to_string()) + .to_vec(), + } + ); + } +} diff --git a/native/src/babelio/request.rs b/native/src/babelio/request.rs new file mode 100644 index 0000000..79735a8 --- /dev/null +++ b/native/src/babelio/request.rs @@ -0,0 +1,69 @@ +use crate::cached_client::CachedClient; +use itertools::Itertools; + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +struct BabelioISBNResponse { + id_oeuvre: String, + titre: String, + couverture: String, + id: String, + id_auteur: String, + prenoms: String, + nom: String, + ca_copies: String, + ca_note: String, + id_edition: String, + r#type: String, + url: String, +} + +pub fn get_book_url(client: &CachedClient, isbn: &str) -> Option { + let raw_search_results = client.get_from_cache( + format!("cache/babelio/get_book_url_{}.html", isbn).as_str(), + |http_client| { + http_client + .post("https://www.babelio.com/aj_recherche.php") + .body(format!("{{\"isMobile\":false,\"term\":\"{}\"}}", isbn)) + .send() + .unwrap() + .text() + .unwrap() + }, + ); + let parsed: Vec = serde_json::from_str(&raw_search_results).unwrap(); + let s = parsed.iter().exactly_one().ok()?.url.clone(); + Some(s) +} + +pub fn get_book_page(client: &CachedClient, url: String) -> String { + client.get_from_cache( + format!( + "cache/babelio/get_book_page_{}.html", + url.replace("/", "_slash_") + ) + .as_str(), + |http_client| { + let resp = http_client + .get(format!("https://www.babelio.com{url}")) + .send() + .unwrap(); + resp.text().unwrap() + }, + ) +} + +pub fn get_book_blurb_see_more(client: &CachedClient, id_obj: &str) -> String { + client.get_from_cache( + format!("cache/babelio/get_book_blurb_see_more_{}.html", id_obj).as_str(), + |http_client| { + let params = std::collections::HashMap::from([("type", "1"), ("id_obj", id_obj)]); + + let voir_plus_resp = http_client + .post("https://www.babelio.com/aj_voir_plus_a.php") + .form(¶ms) + .send() + .unwrap(); + voir_plus_resp.text().unwrap() + }, + ) +} diff --git a/native/src/babelio/test/get_book.html b/native/src/babelio/test/get_book.html new file mode 100644 index 0000000..0157c2a --- /dev/null +++ b/native/src/babelio/test/get_book.html @@ -0,0 +1,581 @@ +Le nom de la bête - Daniel Easterman - Babelio

Bernard Ferry (Traducteur)
+ + EAN : 9782266071529
+ 5423 pages
Pocket + + (12/03/1999) + +
3.42/5 +   + 53 notes +
+ Résumé :
+ Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prê... >Voir plus
+ Acheter ce livre sur + +
FnacAmazonRakutenCulturaMomox
Toutes les offres à partir de 1.45€
+ étiquettes + + + + + Ajouter des étiquettes
+ Critiques, Analyses et Avis (5) + + + + Ajouter une critique
MacAlpine
  + 10 mars 2010
+ + 1999 : l'Egypte est plongée dans le chaos et la terreur. Porté au pouvoir à la faveur d'un coup d'état sanguinaire, l'homme fort du nouveau régime est al Kourtoubi, chef intégriste musulman. Naturellement, le monde entier a les yeux rivés sur lui. Les services secrets britanniques dépêchent au Caire Michael Hunt, l'un de leurs meilleurs agents. Avec l'aide de partenaires tout à fait inattendus - un prêtre du Vatican et une jeune archéologue - il tentera de déjouer un machiavélique complot international qui a pour but de déstabiliser l'Occident et d'anéantir la démocratie. + + +
Commenter  J’apprécie          70
Chiwi
  + 03 février 2013
+ + A la relecture de ce thriller (je l'avais lu il y a presque treize ans) je suis partagé.
D'un côté il y a un aspect réaliste qui fait froid dans le dos. Les descriptions des exactions des islamistes font écho à celles que l'on peut voir dans les médias. Il y avait comme une prémonition de ce qui allait arriver avec le terrorisme islamiste.
D'un autre côté Easterman introduit des éléments qui font que le roman prend à certains moments un tournant ésotérique voire un peu mystique. L'utilisation d'éléments de l'Apocalypse rend des fois le roman un peu délirant. le dirigeant islamiste est un intégriste pur et dur mais n'hésite pas à se présenter comme l'Antéchrist. C'est un mélange des genres que je trouve un peu gros.
Le personnage de Michael Hunt, héros du roman, est trop ambigu. Catholique, il refuse le divorce à sa femme alors qu'ils ne vivent plus ensemble et on ne sait pas bien pourquoi. Mais lorsqu'il rencontre une jeune archéologue, il n'hésite pas à coucher avec et pratiquer sans remords l'adultère. Au fil du roman, il donne l'impression de toujours être hésitant, de ne pas savoir sur quel pied danser, de subir les évènements. Cela est particulièrement vrai au moment du dénouement final.
Don le Nom de la Bête est un thriller qui vaut le coup pour les aspects géopolitiques, toujours actuels, mais qui est peu fouillé en ce qui concerne la psychologie des personnages.
+ Lire la suite
Commenter  J’apprécie          10
anthony44
  + 16 novembre 2014
Le Nom de la Bete est un roman de l'écrivain Irlandais Daniel Easterman. Michael Hunt, ancien agent des services secrets des Etats Unis doit reprendre du service afin de lutter contre un régime Islamique mené par un dangereux chef intégriste.
+Le roman a été écrit (il me semble) en 1997 et on peut dire qu'il y'avait une sorte de prémonition quand l'auteur a écrit ce récit d'aventure. On entend énormément parler d'intégrisme religieux dans les médias. le roman est composé d'énormément de rebondissements ce qui tient en haleine le lecteur. Malheureusement, on ne s'identifie pas vraiment aux personnages qui sont assez stéréotypés et la fin est vite bâclée je trouve.
+J'aime beaucoup les livres de cet auteur (Daniel Easterman) mais celui là bien qu'honnête ne m'a pas passionné malgré une intrigue intéressante et malheureusement actuelle. Un petit en-cas avant de passer à un autre gros livre.
Commenter  J’apprécie          10
bfauriaux
  + 26 septembre 2021
+ + Une plongee en Egypte pour un thriller contemporain qui nous replonge en 1999 et l'arrivée des integristes au pouvoir.Un agrnt consulaire britannique est envoye sur place et est charge de dejouer le complot qui doit viser jusqu'au pape afin de déstabiliser le monde europeen.Un thriller sans temps mort qui se devore d'un trait.A deguster sans moderation. + + +
Commenter  J’apprécie          10
Ellioth
  + 07 janvier 2012
+ + Encore un théme cher à Eastermann, l'extrémisme religieux, en particulier islamique. Descente aux enfers pour les héros du livre, le lecteur est encore rivé à ses pages, pour une action qui ne perd pas un instant de son intensité....on ne sort pas indemne de ce bouquin. Eastermann pousse encore les lecteurs à s'interroger sur notre monde... + + +
Commenter  J’apprécie          10

+ Citations et extraits (13) + Voir plus + + Ajouter une citation
rkhettaouirkhettaoui   08 janvier 2016
+ Les chefs de bureau ne voyagent pas, ils ne mettent en danger ni leur vie ni leurs connaissances. Ils observent à distance, et si nécessaire se retranchent derrière les voiles que le service a prévus pour eux. Il y en avait sept pour se dissimuler, autant que pour la danse : l’Honneur, la Discrétion, la Sécurité, la Diplomatie, le Secret, le Tact et la Connerie. C’est surtout ce dernier voile qui sert à masquer la nudité des serviteurs du royaume. + +
+ Lire la suite
Commenter  J’apprécie          20
rkhettaouirkhettaoui   08 janvier 2016
+ Tous les Arabes, tous les Iraniens, tous les Turcs étaient considérés comme des terroristes potentiels. Ou en activité. Des gens devenaient paranoïaques, ils les voyaient tous, hommes, femmes, enfants, en train de poser des bombes dans les rues. Des musulmans se faisaient agresser. Simplement parce qu’ils étaient barbus et portaient d’autres vêtements que les vêtements occidentaux. + +
Commenter  J’apprécie          20
rkhettaouirkhettaoui   08 janvier 2016
+ Une épidémie de peste peut être dévastatrice. Et s’il s’agit d’un virus mutant, rien ne pourra l’enrayer + +
Commenter  J’apprécie          70
rkhettaouirkhettaoui   08 janvier 2016
+ L’immunité diplomatique n’a aucune valeur pour eux. Les Iraniens ont déjà créé un précédent. L’art de la diplomatie est une ruse occidentale, une manière d’échapper aux lois des peuples autochtones. + +
Commenter  J’apprécie          20
rkhettaouirkhettaoui   08 janvier 2016
+ — Dans ce cas, il lui faudrait un garde du corps en permanence.
+— On le lui a dit, mais elle ne veut pas en entendre parler. Elle pense que ce serait lui accorder trop d’importance. Elle estime être plus en sûreté sans protection, parce que ça signifie qu’elle ne fait pas partie de ceux qu’on protège. Les gens importants ont des gardes du corps, mais ça ne les empêche pas d’être enlevés. En outre, elle dit que si elle bénéficiait d’une protection, en cas d’enlèvement elle serait traitée comme une personnalité, ce que précisément elle ne veut pas. + +
+ Lire la suite
Commenter  J’apprécie          00

+ autres livres classés : thrillerVoir plus
+ Notre sélection Polar et thriller + Voir plus
+ Acheter ce livre sur + +
FnacAmazonRakutenCulturaMomox
Toutes les offres à partir de 1.45€





+ Quiz + Voir plus

Retrouvez le bon adjectif dans le titre - (6 - polars et thrillers )

Roger-Jon Ellory : " **** le silence"

seul
profond
terrible
intense

20 questions
+ 2498 lecteurs ont répondu +
+ + Thèmes : + littérature + , thriller + , romans policiers et polarsCréer un quiz sur ce livre

\ No newline at end of file diff --git a/native/src/babelio/test/get_book_minimal.html b/native/src/babelio/test/get_book_minimal.html new file mode 100644 index 0000000..08d0afc --- /dev/null +++ b/native/src/babelio/test/get_book_minimal.html @@ -0,0 +1,147 @@ + + + + + + + +
+
+
+
+ +
+
+ +

Bernard Ferry (Traducteur) +
+ + EAN : 9782266071529
+ 5423 pages
Pocket + + (12/03/1999) + +
3.42/5 +   + 53 notes + + + +
+ Résumé :
+
+ Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se + multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les + fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. + Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de + reprendre du service pour enquêter sur place. Aidé par son frère Paul, prê... >Voir plus + +
+
+ +
+ + +
+ étiquettes + + + + + Ajouter des étiquettes +
+ +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/native/src/babelio/test/get_see_more.html b/native/src/babelio/test/get_see_more.html new file mode 100644 index 0000000..3e2b424 --- /dev/null +++ b/native/src/babelio/test/get_see_more.html @@ -0,0 +1 @@ +Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède
\ No newline at end of file diff --git a/native/src/babelio/test/search_by_isbn b/native/src/babelio/test/search_by_isbn new file mode 100644 index 0000000..e69de29 diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index a4ec960..f335968 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -2,23 +2,74 @@ use super::*; // Section: wire functions #[no_mangle] -pub extern "C" fn wire_platform(port_: i64) { - wire_platform_impl(port_) +pub extern "C" fn wire_get_metadata_from_images(port_: i64, imgs_path: *mut wire_StringList) { + wire_get_metadata_from_images_impl(port_, imgs_path) } +// Section: allocate functions + #[no_mangle] -pub extern "C" fn wire_rust_release_mode(port_: i64) { - wire_rust_release_mode_impl(port_) +pub extern "C" fn new_StringList_0(len: i32) -> *mut wire_StringList { + let wrap = wire_StringList { + ptr: support::new_leak_vec_ptr(<*mut wire_uint_8_list>::new_with_null_ptr(), len), + len, + }; + support::new_leak_box_ptr(wrap) } -// Section: allocate functions +#[no_mangle] +pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { + let ans = wire_uint_8_list { + ptr: support::new_leak_vec_ptr(Default::default(), len), + len, + }; + support::new_leak_box_ptr(ans) +} // Section: related functions // Section: impl Wire2Api +impl Wire2Api for *mut wire_uint_8_list { + fn wire2api(self) -> String { + let vec: Vec = self.wire2api(); + String::from_utf8_lossy(&vec).into_owned() + } +} +impl Wire2Api> for *mut wire_StringList { + fn wire2api(self) -> Vec { + let vec = unsafe { + let wrap = support::box_from_leak_ptr(self); + support::vec_from_leak_ptr(wrap.ptr, wrap.len) + }; + vec.into_iter().map(Wire2Api::wire2api).collect() + } +} + +impl Wire2Api> for *mut wire_uint_8_list { + fn wire2api(self) -> Vec { + unsafe { + let wrap = support::box_from_leak_ptr(self); + support::vec_from_leak_ptr(wrap.ptr, wrap.len) + } + } +} // Section: wire structs +#[repr(C)] +#[derive(Clone)] +pub struct wire_StringList { + ptr: *mut *mut wire_uint_8_list, + len: i32, +} + +#[repr(C)] +#[derive(Clone)] +pub struct wire_uint_8_list { + ptr: *mut u8, + len: i32, +} + // Section: impl NewWithNullPtr pub trait NewWithNullPtr { diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index dac5a8e..991de3c 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -19,26 +19,24 @@ use std::sync::Arc; // Section: imports +use crate::common::Ad; + // Section: wire functions -fn wire_platform_impl(port_: MessagePort) { +fn wire_get_metadata_from_images_impl( + port_: MessagePort, + imgs_path: impl Wire2Api> + UnwindSafe, +) { FLUTTER_RUST_BRIDGE_HANDLER.wrap( WrapInfo { - debug_name: "platform", + debug_name: "get_metadata_from_images", port: Some(port_), mode: FfiCallMode::Normal, }, - move || move |task_callback| Ok(platform()), - ) -} -fn wire_rust_release_mode_impl(port_: MessagePort) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap( - WrapInfo { - debug_name: "rust_release_mode", - port: Some(port_), - mode: FfiCallMode::Normal, + move || { + let api_imgs_path = imgs_path.wire2api(); + move |task_callback| Ok(get_metadata_from_images(api_imgs_path)) }, - move || move |task_callback| Ok(rust_release_mode()), ) } // Section: wrapper structs @@ -63,23 +61,28 @@ where (!self.is_null()).then(|| self.wire2api()) } } + +impl Wire2Api for u8 { + fn wire2api(self) -> u8 { + self + } +} + // Section: impl IntoDart -impl support::IntoDart for Platform { +impl support::IntoDart for Ad { fn into_dart(self) -> support::DartAbi { - match self { - Self::Unknown => 0, - Self::Android => 1, - Self::Ios => 2, - Self::Windows => 3, - Self::Unix => 4, - Self::MacIntel => 5, - Self::MacApple => 6, - Self::Wasm => 7, - } + vec![ + self.title.into_dart(), + self.description.into_dart(), + self.price_cent.into_dart(), + self.imgs_path.into_dart(), + ] .into_dart() } } +impl support::IntoDartExceptPrimitive for Ad {} + // Section: executor support::lazy_static! { diff --git a/native/src/cached_client.rs b/native/src/cached_client.rs new file mode 100644 index 0000000..55783ea --- /dev/null +++ b/native/src/cached_client.rs @@ -0,0 +1,22 @@ +pub struct CachedClient { + pub http_client: reqwest::blocking::Client, +} + +impl CachedClient { + pub fn get_from_cache String>( + &self, + cache_file_path: &str, + make_request: F, + ) -> String { + let html = std::fs::read_to_string(cache_file_path); + match html { + Ok(f) => f, + Err(_) => { + let resp = make_request(&self.http_client); + let write_res = std::fs::write(cache_file_path, &resp); + write_res.expect(format!("Can't write to file {}", cache_file_path).as_str()); + resp + } + } + } +} diff --git a/native/src/common.rs b/native/src/common.rs new file mode 100644 index 0000000..4723920 --- /dev/null +++ b/native/src/common.rs @@ -0,0 +1,30 @@ +#[derive(Default, Debug, PartialEq)] +pub struct BookMetaData { + pub title: String, + pub authors: Vec, + // A book blurb is a short promotional description. + // A synopsis summarizes the twists, turns, and conclusion of the story. + pub blurb: Option, + pub keywords: Vec, +} + +#[derive(Debug, PartialEq)] +pub struct Author { + pub first_name: String, + pub last_name: String, +} + +pub fn html_select(sel: &str) -> scraper::Selector { + scraper::Selector::parse(sel).unwrap() +} + +pub trait Provider { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option; +} + +pub struct Ad { + pub title: String, + pub description: String, + pub price_cent: i32, + pub imgs_path: Vec, +} diff --git a/native/src/google_books.rs b/native/src/google_books.rs new file mode 100644 index 0000000..a2dd9aa --- /dev/null +++ b/native/src/google_books.rs @@ -0,0 +1,17 @@ +use crate::common; +mod parser; +mod request; + +pub struct GoogleBooks; + +impl common::Provider for GoogleBooks { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let client = reqwest::blocking::Client::builder().build().unwrap(); + let isbn_search_response = request::search_by_isbn(&client, isbn); + let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response); + let book_page = request::get_volume(&client, &self_link); + Some(parser::extract_metadata_from_self_link_response(&book_page)) + } +} + + diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs new file mode 100644 index 0000000..c563d33 --- /dev/null +++ b/native/src/google_books/parser.rs @@ -0,0 +1,174 @@ +use itertools::Itertools; + +use crate::common; + +pub fn extract_self_link_from_isbn_response(html: &str) -> String { + let s: structs::Root = serde_json::from_str(html).unwrap(); + s.items[0].self_link.to_string() +} + +pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaData { + let s: structs::Item = serde_json::from_str(html).unwrap(); + let first_book = &s.volume_info; + common::BookMetaData { + title: first_book.title.to_string(), + authors: first_book + .authors + .iter() + .map(|s| common::Author { + first_name: "".to_string(), + last_name: s.to_string(), + }) + .collect_vec(), + + blurb: first_book.description.map(|d| d.to_string()), + ..Default::default() + } +} + +#[cfg(test)] +mod tests { + use crate::common::BookMetaData; + + use super::*; + + #[test] + fn extract_self_link_from_file() { + let html = std::fs::read_to_string("src/google_books/test/isbn_response.html").unwrap(); + let self_link = extract_self_link_from_isbn_response(&html); + assert_eq!( + self_link, + "https://www.googleapis.com/books/v1/volumes/DQUFSQAACAAJ" + ) + } + + #[test] + fn extract_metadata_from_file() { + let html = + std::fs::read_to_string("src/google_books/test/self_link_response.html").unwrap(); + let metadata = extract_metadata_from_self_link_response(&html); + assert_eq!(metadata, BookMetaData{ + title: "La cité de Dieu".to_string(), + authors:vec![common::Author{first_name: "".to_string(), last_name: "Paulo Lins".to_string()}], + blurb: Some("Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang et devient, quelques années plus tard, le maître de la cité.".to_string()), + ..Default::default() + }); + } +} + +mod structs { + use serde::{Deserialize, Serialize}; + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Root<'a> { + pub kind: &'a str, + pub total_items: i64, + pub items: Vec>, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Item<'a> { + pub kind: &'a str, + pub id: &'a str, + pub etag: &'a str, + pub self_link: &'a str, + pub volume_info: VolumeInfo<'a>, + pub sale_info: SaleInfo<'a>, + pub access_info: AccessInfo<'a>, + pub search_info: Option>, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct VolumeInfo<'a> { + pub title: &'a str, + pub subtitle: Option<&'a str>, + pub authors: Vec<&'a str>, + pub publisher: Option<&'a str>, + pub published_date: &'a str, + pub description: Option<&'a str>, + pub industry_identifiers: Vec>, + pub reading_modes: ReadingModes, + pub page_count: i64, + pub print_type: &'a str, + pub categories: Option>, + pub maturity_rating: &'a str, + pub image_links: Option>, + pub language: &'a str, + pub preview_link: &'a str, + pub info_link: &'a str, + pub canonical_volume_link: &'a str, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct IndustryIdentifier<'a> { + #[serde(rename = "type")] + pub type_field: &'a str, + pub identifier: &'a str, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct ReadingModes { + pub text: bool, + pub image: bool, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct PanelizationSummary { + pub contains_epub_bubbles: bool, + pub contains_image_bubbles: bool, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct ImageLinks<'a> { + pub small_thumbnail: &'a str, + pub thumbnail: &'a str, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct SaleInfo<'a> { + pub country: &'a str, + pub saleability: &'a str, + pub is_ebook: bool, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct AccessInfo<'a> { + pub country: &'a str, + pub viewability: &'a str, + pub embeddable: bool, + pub public_domain: bool, + pub text_to_speech_permission: &'a str, + pub epub: Epub, + pub pdf: Pdf, + pub web_reader_link: &'a str, + pub access_view_status: &'a str, + pub quote_sharing_allowed: bool, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Epub { + pub is_available: bool, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct Pdf { + pub is_available: bool, + } + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct SearchInfo<'a> { + pub text_snippet: &'a str, + } +} diff --git a/native/src/google_books/request.rs b/native/src/google_books/request.rs new file mode 100644 index 0000000..f0df992 --- /dev/null +++ b/native/src/google_books/request.rs @@ -0,0 +1,13 @@ +pub fn search_by_isbn(client: &reqwest::blocking::Client, isbn: &str) -> String { + let resp = client + .get(format!( + "https://www.googleapis.com/books/v1/volumes?q=isbn:{isbn}" + )) + .send() + .unwrap(); + resp.text().unwrap() +} +pub fn get_volume(client: &reqwest::blocking::Client, url: &str) -> String { + let resp = client.get(url).send().unwrap(); + resp.text().unwrap() +} diff --git a/native/src/google_books/test/isbn_response.html b/native/src/google_books/test/isbn_response.html new file mode 100644 index 0000000..b551f83 --- /dev/null +++ b/native/src/google_books/test/isbn_response.html @@ -0,0 +1,67 @@ +{ + "kind": "books#volumes", + "totalItems": 1, + "items": [ + { + "kind": "books#volume", + "id": "DQUFSQAACAAJ", + "etag": "QyXi2Vzw2K0", + "selfLink": "https://www.googleapis.com/books/v1/volumes/DQUFSQAACAAJ", + "volumeInfo": { + "title": "La Cité de Dieu", + "subtitle": "roman", + "authors": [ + "Paulo Lins" + ], + "publishedDate": "2004", + "industryIdentifiers": [ + { + "type": "ISBN_10", + "identifier": "274417081X" + }, + { + "type": "ISBN_13", + "identifier": "9782744170812" + } + ], + "readingModes": { + "text": false, + "image": false + }, + "pageCount": 413, + "printType": "BOOK", + "maturityRating": "NOT_MATURE", + "allowAnonLogging": false, + "contentVersion": "preview-1.0.0", + "language": "fr", + "previewLink": "http://books.google.fr/books?id=DQUFSQAACAAJ&dq=isbn:9782744170812&hl=&cd=1&source=gbs_api", + "infoLink": "http://books.google.fr/books?id=DQUFSQAACAAJ&dq=isbn:9782744170812&hl=&source=gbs_api", + "canonicalVolumeLink": "https://books.google.com/books/about/La_Cit%C3%A9_de_Dieu.html?hl=&id=DQUFSQAACAAJ" + }, + "saleInfo": { + "country": "FR", + "saleability": "NOT_FOR_SALE", + "isEbook": false + }, + "accessInfo": { + "country": "FR", + "viewability": "NO_PAGES", + "embeddable": false, + "publicDomain": false, + "textToSpeechPermission": "ALLOWED", + "epub": { + "isAvailable": false + }, + "pdf": { + "isAvailable": false + }, + "webReaderLink": "http://play.google.com/books/reader?id=DQUFSQAACAAJ&hl=&source=gbs_api", + "accessViewStatus": "NONE", + "quoteSharingAllowed": false + }, + "searchInfo": { + "textSnippet": "Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang ..." + } + } + ] +} \ No newline at end of file diff --git a/native/src/google_books/test/self_link_response.html b/native/src/google_books/test/self_link_response.html new file mode 100644 index 0000000..90564ce --- /dev/null +++ b/native/src/google_books/test/self_link_response.html @@ -0,0 +1,63 @@ +{ + "kind": "books#volume", + "id": "DQUFSQAACAAJ", + "etag": "pZz4IWl62XA", + "selfLink": "https://www.googleapis.com/books/v1/volumes/DQUFSQAACAAJ", + "volumeInfo": { + "title": "La cité de Dieu", + "authors": [ + "Paulo Lins" + ], + "publisher": "Gallimard", + "publishedDate": "2005", + "description": "Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang et devient, quelques années plus tard, le maître de la cité.", + "industryIdentifiers": [ + { + "type": "ISBN_10", + "identifier": "274417081X" + }, + { + "type": "ISBN_13", + "identifier": "9782744170812" + } + ], + "readingModes": { + "text": false, + "image": false + }, + "pageCount": 581, + "printedPageCount": 581, + "dimensions": { + "height": "22.00 cm" + }, + "printType": "BOOK", + "maturityRating": "NOT_MATURE", + "allowAnonLogging": false, + "contentVersion": "preview-1.0.0", + "language": "fr", + "previewLink": "http://books.google.fr/books?id=DQUFSQAACAAJ&hl=&source=gbs_api", + "infoLink": "https://play.google.com/store/books/details?id=DQUFSQAACAAJ&source=gbs_api", + "canonicalVolumeLink": "https://play.google.com/store/books/details?id=DQUFSQAACAAJ" + }, + "saleInfo": { + "country": "FR", + "saleability": "NOT_FOR_SALE", + "isEbook": false + }, + "accessInfo": { + "country": "FR", + "viewability": "NO_PAGES", + "embeddable": false, + "publicDomain": false, + "textToSpeechPermission": "ALLOWED", + "epub": { + "isAvailable": false + }, + "pdf": { + "isAvailable": false + }, + "webReaderLink": "http://play.google.com/books/reader?id=DQUFSQAACAAJ&hl=&source=gbs_api", + "accessViewStatus": "NONE", + "quoteSharingAllowed": false + } +} \ No newline at end of file diff --git a/native/src/image_tools.rs b/native/src/image_tools.rs new file mode 100644 index 0000000..c904ecd --- /dev/null +++ b/native/src/image_tools.rs @@ -0,0 +1,11 @@ +use std::{path::Path, process::Command}; + +pub fn downsize_image(widht: u32, height: u32, input_filepath: &Path, output_filepath: &Path) { + Command::new("convert") + .arg(input_filepath.to_str().unwrap()) + .arg("-resize") + .arg(format!("{}x{}^>", widht, height)) + .arg(output_filepath.to_str().unwrap()) + .output() + .unwrap(); +} diff --git a/native/src/jwt_decoder.rs b/native/src/jwt_decoder.rs new file mode 100644 index 0000000..d24e1b4 --- /dev/null +++ b/native/src/jwt_decoder.rs @@ -0,0 +1,23 @@ +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +struct JWT { + exp: u64, +} +use std::time::SystemTime; + +pub fn check_jwt_expiration(jwt: &str) -> () { + let parts = jwt.split('.').collect_vec(); + let p = parts[1]; + println!("p = {}", p); + let decoded = + base64::Engine::decode(&base64::engine::general_purpose::URL_SAFE_NO_PAD, p).unwrap(); + let jj: JWT = serde_json::from_slice(&decoded).unwrap(); + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + if jj.exp < now { + panic!("TOKEN is expired"); + } +} diff --git a/native/src/leboncoin.rs b/native/src/leboncoin.rs new file mode 100644 index 0000000..e91b912 --- /dev/null +++ b/native/src/leboncoin.rs @@ -0,0 +1,52 @@ +extern crate reqwest; +pub(crate) mod personal_info; +use crate::publisher::Publisher; +pub struct Leboncoin; +use crate::image_tools; + +mod parser; +mod request; + +use itertools::Itertools; +use std::path::Path; + +impl Publisher for Leboncoin { + fn publish(&self, ad: crate::common::Ad) -> bool { + crate::jwt_decoder::check_jwt_expiration(personal_info::LBC_TOKEN); + let img_lbc_refs = ad + .imgs_path + .clone() + .into_iter() + .map(|img_filepath| { + let input_path = Path::new(&img_filepath); + let compressed_img_filepath = Path::new("compressed/") + .join(input_path.file_name().unwrap().to_str().unwrap()); + image_tools::downsize_image(800, 800, &input_path, &compressed_img_filepath); + let imgs_upload_response = request::upload_file(&compressed_img_filepath); + let imgs_lbc_ref = parser::parse_file_upload(&imgs_upload_response); + Image { + name: imgs_lbc_ref.filename, + url: imgs_lbc_ref.url, + } + }) + .collect_vec(); + + let send_answer: String = request::send(ad, img_lbc_refs); + let ad_id = parser::parse_send(&send_answer); + let submit_answer = request::submit(ad_id).unwrap(); + let submit_ret = parser::parse_submit(&submit_answer); + println!("submit_ret = {:#?}", submit_ret); + match submit_ret { + parser::SubmitResult::Submitted => true, + parser::SubmitResult::Captcha(_) => false, + } + } +} + +use serde::{Deserialize, Serialize}; +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Image { + pub name: String, + pub url: String, +} diff --git a/native/src/leboncoin/parser.rs b/native/src/leboncoin/parser.rs new file mode 100644 index 0000000..b7fcb22 --- /dev/null +++ b/native/src/leboncoin/parser.rs @@ -0,0 +1,61 @@ +use serde::{Deserialize, Serialize}; + +//////////// + +pub fn parse_file_upload(imgs_upload_response: &str) -> ImageSubmitResponse { + let r: ImageSubmitResponse = serde_json::from_str(imgs_upload_response).unwrap(); + r +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ImageSubmitResponse { + pub filename: String, + pub url: String, +} + +//////////// + +pub fn parse_send(send_response: &str) -> i64 { + let s: structs::SendResponse = serde_json::from_str(send_response).unwrap(); + s.ad_id +} + +mod structs { + use serde::{Deserialize, Serialize}; + + #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct SendResponse { + pub status: String, + #[serde(rename = "ad_id")] + pub ad_id: i64, + #[serde(rename = "action_id")] + pub action_id: i64, + pub step: String, + #[serde(rename = "transaction_step")] + pub transaction_step: String, + } +} + +////////////// + +#[derive(Debug)] +pub enum SubmitResult { + Submitted, + Captcha(String), +} + +pub fn parse_submit(submit_response: &str) -> SubmitResult { + if submit_response == "{}" { + return SubmitResult::Submitted; + } + let s: SubmitResponse = serde_json::from_str(submit_response).unwrap(); + SubmitResult::Captcha(s.url) +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SubmitResponse { + pub url: String, +} diff --git a/native/src/leboncoin/request.rs b/native/src/leboncoin/request.rs new file mode 100644 index 0000000..7ecdbc7 --- /dev/null +++ b/native/src/leboncoin/request.rs @@ -0,0 +1,347 @@ +use std::path::Path; + +use reqwest; +use serde::{Deserialize, Serialize}; + +use crate::leboncoin::personal_info; + +use super::Image; + +pub fn send(ad: crate::common::Ad, images: Vec) -> String { + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("authority", "api.leboncoin.fr".parse().unwrap()); + headers.insert("accept", "*/*".parse().unwrap()); + headers.insert( + "accept-language", + "en-US,en;q=0.9,fr;q=0.8".parse().unwrap(), + ); + headers.insert( + "authorization", + ["Bearer ", personal_info::LBC_TOKEN] + .concat() + .parse() + .unwrap(), + ); + headers.insert("cache-control", "no-cache".parse().unwrap()); + headers.insert("content-type", "application/json".parse().unwrap()); + headers.insert("origin", "https://www.leboncoin.fr".parse().unwrap()); + headers.insert("pragma", "no-cache".parse().unwrap()); + headers.insert( + "referer", + "https://www.leboncoin.fr/deposer-une-annonce" + .parse() + .unwrap(), + ); + headers.insert( + "sec-ch-ua", + "\"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"108\"" + .parse() + .unwrap(), + ); + headers.insert("sec-ch-ua-mobile", "?0".parse().unwrap()); + headers.insert("sec-ch-ua-platform", "\"Linux\"".parse().unwrap()); + headers.insert("sec-fetch-dest", "empty".parse().unwrap()); + headers.insert("sec-fetch-mode", "cors".parse().unwrap()); + headers.insert( + reqwest::header::COOKIE, + personal_info::DATA_DOME_COOKIE.parse().unwrap(), + ); + headers.insert("sec-fetch-site", "same-site".parse().unwrap()); + headers.insert("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36".parse().unwrap()); + + let client = reqwest::blocking::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap(); + let body = SendStruct { + subject: ad.title, + body: ad.description, + category_id: "27".to_string(), + ad_type: "sell".to_string(), + images, + attributes: Attributes { + title_adparams_prediction_id: "2a242efc-e50f-4c3c-9486-9c6ee59225dd".to_string(), + item_condition: "3".to_string(), + donation: "0".to_string(), + price_reco: "2|13|10|90|64ca29e6-269d-4d89-b945-a0dcd5eaf992".to_string(), + shipping_cost: "".to_string(), + }, + extended_attributes: ExtendedAttributes { + shipping: Shipping { + version: 2, + shipping_types: [ + "mondial_relay", + "colissimo", + "face_to_face", + "courrier_suivi", + ] + .map(|s| s.to_string()) + .to_vec(), + estimated_parcel_weight: 600, + }, + }, + location: Location { + address: "".to_string(), + city: personal_info::CITY.to_string(), + country: personal_info::COUNTRY.to_string(), + district: "".to_string(), + geo_provider: "here".to_string(), + geo_source: "city".to_string(), + label: personal_info::LABEL.to_string(), + lat: personal_info::LAT, + lng: personal_info::LNG, + zipcode: personal_info::ZIPCODE.to_string(), + }, + email: personal_info::EMAIL.to_string(), + phone: personal_info::PHONE.to_string(), + escrow_firstname: personal_info::ESCROW_FIRSTNAME.to_string(), + escrow_lastname: personal_info::ESCROW_LASTNAME.to_string(), + price_cents: ad.price_cent.to_string(), + price: (ad.price_cent / 100).to_string(), + no_salesmen: true, + }; + + let res = client + .post("https://api.leboncoin.fr/api/adsubmit/v2/classifieds?with_variation=true") + .headers(headers) + .body(serde_json::to_string(&body).unwrap()) + .send() + .unwrap() + .text() + .unwrap(); + + println!("request send : {:#?}", res); + res +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SendStruct { + pub subject: String, + pub body: String, + #[serde(rename = "category_id")] + pub category_id: String, + #[serde(rename = "ad_type")] + pub ad_type: String, + pub images: Vec, + pub attributes: Attributes, + #[serde(rename = "extended_attributes")] + pub extended_attributes: ExtendedAttributes, + pub location: Location, + pub email: String, + pub phone: String, + pub escrow_firstname: String, + pub escrow_lastname: String, + #[serde(rename = "price_cents")] + pub price_cents: String, + pub price: String, + #[serde(rename = "no_salesmen")] + pub no_salesmen: bool, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Attributes { + #[serde(rename = "title_adparams_prediction_id")] + pub title_adparams_prediction_id: String, + #[serde(rename = "item_condition")] + pub item_condition: String, + pub donation: String, + #[serde(rename = "price_reco")] + pub price_reco: String, + #[serde(rename = "shipping_cost")] + pub shipping_cost: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExtendedAttributes { + pub shipping: Shipping, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Shipping { + pub version: i64, + #[serde(rename = "shipping_types")] + pub shipping_types: Vec, + #[serde(rename = "estimated_parcel_weight")] + pub estimated_parcel_weight: i64, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Location { + pub address: String, + pub city: String, + pub country: String, + pub district: String, + #[serde(rename = "geo_provider")] + pub geo_provider: String, + #[serde(rename = "geo_source")] + pub geo_source: String, + pub label: String, + pub lat: f64, + pub lng: f64, + pub zipcode: String, +} + +pub fn submit(ad_id: i64) -> Result> { + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert("authority", "api.leboncoin.fr".parse().unwrap()); + headers.insert("accept", "*/*".parse().unwrap()); + headers.insert( + "accept-language", + "en-US,en;q=0.9,fr;q=0.8".parse().unwrap(), + ); + headers.insert( + "authorization", + ["Bearer ", personal_info::LBC_TOKEN] + .concat() + .parse() + .unwrap(), + ); + headers.insert("cache-control", "no-cache".parse().unwrap()); + headers.insert("content-type", "application/json".parse().unwrap()); + headers.insert("origin", "https://www.leboncoin.fr".parse().unwrap()); + headers.insert("pragma", "no-cache".parse().unwrap()); + headers.insert( + "referer", + "https://www.leboncoin.fr/deposer-une-annonce/options" + .parse() + .unwrap(), + ); + headers.insert( + "sec-ch-ua", + "\"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"108\"" + .parse() + .unwrap(), + ); + headers.insert("sec-ch-ua-mobile", "?0".parse().unwrap()); + headers.insert("sec-ch-ua-platform", "\"Linux\"".parse().unwrap()); + headers.insert("sec-fetch-dest", "empty".parse().unwrap()); + headers.insert("sec-fetch-mode", "cors".parse().unwrap()); + headers.insert( + reqwest::header::COOKIE, + personal_info::DATA_DOME_COOKIE.parse().unwrap(), + ); + headers.insert("sec-fetch-site", "same-site".parse().unwrap()); + headers.insert("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36".parse().unwrap()); + + let client = reqwest::blocking::Client::builder() + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap(); + let body = SubmitBody { + ads: vec![SubmitAd { + ad_type: "sell".to_string(), + ad_id, + options: vec![], + action_id: 1, + transaction_type: "new_ad".to_string(), + }], + pricing_id: "87275b3e0eae7a906b6ef915156f8295".to_string(), + user_journey: "deposit".to_string(), + }; + let res = client + .post("https://api.leboncoin.fr/api/services/v1/submit") + .headers(headers) + .body(serde_json::to_string(&body).unwrap()) + .send()? + .text()?; + println!("request submit : {}", res); + + Ok(res) +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SubmitBody { + pub ads: Vec, + #[serde(rename = "pricing_id")] + pub pricing_id: String, + #[serde(rename = "user_journey")] + pub user_journey: String, +} + +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SubmitAd { + #[serde(rename = "ad_type")] + pub ad_type: String, + #[serde(rename = "ad_id")] + pub ad_id: i64, + pub options: Vec, + #[serde(rename = "action_id")] + pub action_id: i64, + #[serde(rename = "transaction_type")] + pub transaction_type: String, +} + +pub fn upload_file(img_path: &Path) -> String { + let mut headers = reqwest::header::HeaderMap::new(); + headers.insert( + "authorization", + ["Bearer ", personal_info::LBC_TOKEN] + .concat() + .parse() + .unwrap(), + ); + headers.insert( + "sec-ch-ua", + "\"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"108\"" + .parse() + .unwrap(), + ); + headers.insert("sec-ch-ua-mobile", "?0".parse().unwrap()); + headers.insert("sec-ch-ua-platform", "\"Linux\"".parse().unwrap()); + headers.insert("sec-fetch-dest", "empty".parse().unwrap()); + headers.insert("sec-fetch-mode", "cors".parse().unwrap()); + headers.insert( + reqwest::header::COOKIE, + personal_info::DATA_DOME_COOKIE.parse().unwrap(), + ); + headers.insert("sec-fetch-site", "same-site".parse().unwrap()); + headers.insert("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36".parse().unwrap()); + + let form = reqwest::blocking::multipart::Form::new() + .file("file", img_path) + .unwrap(); + + let client = reqwest::blocking::Client::new(); + let res = client + .post("https://api.leboncoin.fr/api/pintad/v1/public/upload/image") + .headers(headers) + .multipart(form) + .send() + .unwrap() + .text() + .unwrap(); + println!("upload_file response = {}", res); + res +} + +//curl 'https://api.leboncoin.fr/api/pintad/v1/public/upload/image' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H 'Accept: */*' -H 'Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3' -H 'Accept-Encoding: gzip, deflate, br' -H 'Referer: https://www.leboncoin.fr/annonce/2305203826/editer' -H 'api_key: ba0c2dad52b3ec' -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxNTYxMiwiaWF0IjoxNjc3NjA4NDExLCJpZCI6IjliYzg5OWM1LTMxN2UtNDE1Ny1iMzEyLTAyMWQ1ZTQ3YTlkYSIsImluc3RhbGxfaWQiOiI3MDQ1YjhmMy0xMzYyLTRiN2UtYjhmZC1lY2Y0OWU4ODRjOGQiLCJqdGkiOiIyY2FhMzU5OS1jZDk3LTQxYTEtYmIzMC1hNmI2YjlmMDA1MzciLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6ImVjMDZjZTM0LThhMzItNDkyNC05NDc1LTc4MzU4MmY3ZGI3YiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiIyNGUyNDA2Mi0xNmE2LTQ4NWQtYjg0Yi1iNWY4MGViNjkzYTUiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.GRqf3gDdFiq1ukFhN_2i8HhWycirauVUM7rDoZZHsSgD-wv5VOwuKDWc6axDoPK3Wsbg_oFXfrSHX-bcDkE2SRaOqNB734eqD-fbceCG1ntf-afgeLWf-MPnas0n_ylOB2ZSK1LAG2aCXZSDm3ZEkXs_-KZhwQtsmqLgIte0PJUUk_qP4tYYDqLe3FvUeGIkrPAFHKxfnAXmKXf-kh9RvbykGiek9lqFT-Hg95X21eS3Z8HH2li-OMP4B2I-PQysOLuaAZ47wkjkt8PKgC6qG3rlitr28MRbkBYrsuo5ic9JMEKTlmbYa5WsyzZJL5F5Y3CdKTXxiQ4ae2kY2hRLgubk2Dihy8vdqLhitX-Fm_sGQSFnP7vy7iHhQCK5m4jLnD-p-sD_DAehNkGYF8lQqG44myb7XdmTtY9uoR_1Tv2LXYSncKQzEpCn-G6Pf0DJg4xb02CCxXWqB7oysooBgFzgPGdixJeBnFSX_8H0zbmoszUUW7Wqw_aSKv7aAQ3p2Foha3U7B4B-3lHPetec6wEo1eLvq5XXRbDAZuvIqbG6cvQHS5HDNkiBQIHfED3VwVOnextu0BADL7hYl4bOM50yNquNIoecPbEOC0Tij3JdYdGjHJ_ywDhsGwD08awZLIpPsJ1ppcaxMv3thoMsiInKqX5wLHNmTec13Lyn978' -H 'Content-Type: multipart/form-data; boundary=---------------------------153785532732722146451504606153' -H 'Origin: https://www.leboncoin.fr' -H 'Connection: keep-alive' -H 'Cookie: datadome=Ysu9fziXa098YNozRd6iS4oq62CcYXd0SZUP9cw4Rl5NMfVROqQrM7eT96i618vhu3M4l~JZWv80IUWEBCzBJdRnqF2Z-W6M0oyZXXOyLvLbDiGsWPQcoQw_UU7qXS-; __Secure-Install=7045b8f3-1362-4b7e-b8fd-ecf49e884c8d; __Secure-InstanceId=7045b8f3-1362-4b7e-b8fd-ecf49e884c8d; utag_main=v_id:0186993fcaf6000e3c437fda305405046001900900bd0$_sn:1$_ss:0$_pn:5%3Bexp-session$_st:1677610707192$ses_id:1677608340214%3Bexp-session; didomi_token=eyJ1c2VyX2lkIjoiMTg2OTkzZmMtYjVmNi02ZTFiLWI2YWQtZDFmMjhmOTJiODk4IiwiY3JlYXRlZCI6IjIwMjMtMDItMjhUMTg6MTk6MDIuMzQyWiIsInVwZGF0ZWQiOiIyMDIzLTAyLTI4VDE4OjE5OjAyLjM0MloiLCJ2ZW5kb3JzIjp7ImVuYWJsZWQiOlsiZ29vZ2xlIiwiYzpsYmNmcmFuY2UiLCJjOnJldmxpZnRlci1jUnBNbnA1eCIsImM6ZGlkb21pIl19LCJwdXJwb3NlcyI6eyJlbmFibGVkIjpbImV4cGVyaWVuY2V1dGlsaXNhdGV1ciIsIm1lc3VyZWF1ZGllbmNlIiwicGVyc29ubmFsaXNhdGlvbm1hcmtldGluZyIsInByaXgiXX0sInZlbmRvcnNfbGkiOnsiZW5hYmxlZCI6WyJnb29nbGUiXX0sInZlcnNpb24iOjIsImFjIjoiRExXQkFBRUlBSXdBV1FCLWdHRkFQeUFra0JKWUVBd0lrZ1NrQXR5QnhBRHB3SFZnUU1BaW9CSE9DU2NFdFlLREFVSWdvdEJYT0N3VUZ0NExqQVhMQXdHQmhFREUwR1dvLkRMV0JBQUVJQUl3QVdRQi1nR0ZBUHlBa2tCSllFQXdJa2dTa0F0eUJ4QURwd0hWZ1FNQWlvQkhPQ1NjRXRZS0RBVUlnb3RCWE9Dd1VGdDRMakFYTEF3R0JoRURFMEdXbyJ9; euconsent-v2=CPn5KgAPn5KgAAHABBENC5CgAPLAAH7AAAAAIsNB_G_dTyPi-f59YvtwYQ1P4VQnoyACjgaNgwwJiRLBMI0EhmAIKAHqAAACIBAkICJAAQBlCAHAAAAA4IEAASMMAAAAIRAIIgCAAEAAAmJICABZCxAAAQAQgkwAABQAgAICABMgSDAAAAAAFAAAAAgAAAAAAAAAAAAAQAAAAAAAAgAAAAAAAAAAAAAEEAQATDVuIAGxLHAmkDCIAACMIAgCgBABRQBCwQAEBIgAEEYACjAAAAAFAAAAAAAAEAMAAAAAgAQgAAAAcEAgAIAEAAAAEAgEAAAAACAAADAAAAAAAMAAAAAAgAIAAAKAQAABAAgAJAgACAAAAgAAAAAAAAAgEAAAAAAAAAAAAAAAAQAxQAGAAIJQjAAMAAQShIAAYAAglCAA.flgAD9gAAAAA; include_in_experiment=true; _hjSessionUser_2783207=eyJpZCI6IjBlOTAwNGIzLWVmNmUtNWM5ZC1iNzQ4LTU4NjFlMWVmMmUyMSIsImNyZWF0ZWQiOjE2Nzc2MDgzNDM2NzksImV4aXN0aW5nIjp0cnVlfQ==; _hjFirstSeen=1; _hjIncludedInSessionSample_2783207=1; _hjSession_2783207=eyJpZCI6ImE1MjQxMjgxLWY5YzQtNDU5YS05NzNkLWFiODc4YjM4MzU2MyIsImNyZWF0ZWQiOjE2Nzc2MDgzNDM2ODEsImluU2FtcGxlIjp0cnVlfQ==; _hjAbsoluteSessionInProgress=0; ry_ry-l3b0nco_realytics=eyJpZCI6InJ5XzI5RjFCQTE5LUQ3ODYtNEUxQy05NUNDLTYwMjE4QUVEOUI3NyIsImNpZCI6bnVsbCwiZXhwIjoxNzA5MTQ0MzQ0Nzc3LCJjcyI6bnVsbH0%3D; ry_ry-l3b0nco_so_realytics=eyJpZCI6InJ5XzI5RjFCQTE5LUQ3ODYtNEUxQy05NUNDLTYwMjE4QUVEOUI3NyIsImNpZCI6bnVsbCwib3JpZ2luIjp0cnVlLCJyZWYiOm51bGwsImNvbnQiOm51bGwsIm5zIjpmYWxzZX0%3D; _gcl_au=1.1.701707994.1677608345; cto_bundle=CNZ-SV9JaWtsRlAyVjVqODhGWWhVYldZN3BzOFl6eG5hJTJCMTZibnQ2R2RNakRCS3N5WDdWWFVleHpzRCUyQmNtQUQwUmt0bW5CdldxZUFLem02b1clMkZTMDdTdXQ3emtxYUtISDJqUzZEazclMkZZMFlFTzQzS2dRaU5NZnVMc3JtR2NMTFBBcGNBZ2hINGczcm44TFpQSSUyRmF1YiUyQnJ3OXclM0QlM0Q; __gads=ID=f3fe3612fb4470d8:T=1677608347:S=ALNI_MbMwkuo3rfJtYKI9L6tyyLC8sIwbg; __gpi=UID=00000be0047e7d68:T=1677608347:RT=1677608347:S=ALNI_Mav2FKNNTDv7opgvCIg2Kld9HMpbw; luat=eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxNTYxMiwiaWF0IjoxNjc3NjA4NDExLCJpZCI6IjliYzg5OWM1LTMxN2UtNDE1Ny1iMzEyLTAyMWQ1ZTQ3YTlkYSIsImluc3RhbGxfaWQiOiI3MDQ1YjhmMy0xMzYyLTRiN2UtYjhmZC1lY2Y0OWU4ODRjOGQiLCJqdGkiOiIyY2FhMzU5OS1jZDk3LTQxYTEtYmIzMC1hNmI2YjlmMDA1MzciLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6ImVjMDZjZTM0LThhMzItNDkyNC05NDc1LTc4MzU4MmY3ZGI3YiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiIyNGUyNDA2Mi0xNmE2LTQ4NWQtYjg0Yi1iNWY4MGViNjkzYTUiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.GRqf3gDdFiq1ukFhN_2i8HhWycirauVUM7rDoZZHsSgD-wv5VOwuKDWc6axDoPK3Wsbg_oFXfrSHX-bcDkE2SRaOqNB734eqD-fbceCG1ntf-afgeLWf-MPnas0n_ylOB2ZSK1LAG2aCXZSDm3ZEkXs_-KZhwQtsmqLgIte0PJUUk_qP4tYYDqLe3FvUeGIkrPAFHKxfnAXmKXf-kh9RvbykGiek9lqFT-Hg95X21eS3Z8HH2li-OMP4B2I-PQysOLuaAZ47wkjkt8PKgC6qG3rlitr28MRbkBYrsuo5ic9JMEKTlmbYa5WsyzZJL5F5Y3CdKTXxiQ4ae2kY2hRLgubk2Dihy8vdqLhitX-Fm_sGQSFnP7vy7iHhQCK5m4jLnD-p-sD_DAehNkGYF8lQqG44myb7XdmTtY9uoR_1Tv2LXYSncKQzEpCn-G6Pf0DJg4xb02CCxXWqB7oysooBgFzgPGdixJeBnFSX_8H0zbmoszUUW7Wqw_aSKv7aAQ3p2Foha3U7B4B-3lHPetec6wEo1eLvq5XXRbDAZuvIqbG6cvQHS5HDNkiBQIHfED3VwVOnextu0BADL7hYl4bOM50yNquNIoecPbEOC0Tij3JdYdGjHJ_ywDhsGwD08awZLIpPsJ1ppcaxMv3thoMsiInKqX5wLHNmTec13Lyn978; _schn=_27faqp; _scid=d6571009-7472-41bb-9cb9-8510b51a6a95; _fbp=fb.1.1677608908547.2034999500' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-site' -H 'TE: trailers' --data-binary $'-----------------------------153785532732722146451504606153\r\nContent-Disposition: form-data; name="file"; filename="20230228_192237.jpg"\r\nContent-Type: image/jpeg\r\n\r\n-----------------------------153785532732722146451504606153--\r\n' + +// curl 'https://api.leboncoin.fr/api/pintad/v1/public/upload/image' \ +// -H 'authority: api.leboncoin.fr' \ + +// -H 'accept: */*' \ +/* +-H 'accept-language: en-US,en;q=0.9,fr;q=0.8' \ +-H 'api_key: ba0c2dad52b3ec' \ +-H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxMTY1NywiaWF0IjoxNjc3NjA0NDU2LCJpZCI6IjQ1MTM1OTUxLTY0ZjQtNDFjZS05NGVjLWJkMzkzZDRjY2U2ZiIsImluc3RhbGxfaWQiOiIwNTA1NzA2YS05NDJhLTQzNjktYTdlYy02MGYxZDYxYWZiNjUiLCJqdGkiOiJlYmQzZDI2My1mMTMzLTQ3MjktYjVjOS1kNTA3ZmYwZjUxNDEiLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6IjcwN2M3M2ZiLTk2NzQtNDZlNC05N2NmLTRkZTk2MzQ3NTYwZiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiI3NGM1ZGY4Yy05MzQ3LTQ0MWQtYWViYi0zZWIzYjYyZTk1MjMiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.dZMGwYAei7ovgsB6REy1XjtTjqbgVj4oXQomz7teIj-z0KEajW1pyLyBp4EweGpyb1McgSu8BOS74da9GEeNZ60x9pOAn9KiS8VNfqMBYiwknURnwI8NJdf9KiB6k__SzXb05uTyDeazLK76MoUIAImT8LwzMrFvdZewmmkqyYqT4o4Bcn0tynDkRLv5dSZ87n4ca0AsOsHt6zgWipwpqsGBom0ysYnOzq-hCkyM1-3SsjR4ohVT--qqR2EIijO2-SGk90kmwDzR9aYwCZzzRAlUTFhpE6-zHO7TquAV9oIQAU2Wmq5HgzhEREjUhJOI0fqXy9xk1dPRzb1A__rDbAm8Nkfxq-mF1JcaRM-nB2Pb1VgDV8j6P2MtPC8TlKyr9dMQFzuTWpvFa8sMYg92f3i1oLwvbgsHu5nweMqrWItDAwja7v35T3IReejBwKGOXXEmsTlJEcq589b2AdtZwH82mcFfwn6QkTPbJVGv7YiSLbyGNCQLbUJ-FhLptq2fLZwJUTEye72u-WzY5yeCxs8ZaIfaQHTduJrVviMlEfam9rnUUU-cUdA7NJx8bg63FqOYhEH-hFHYeo5gSF5EqA97jBwC2KoApADf4t1q5EhUPw7gGR9U7qQuhoRiTLVFV4kEjIWeX1QRntOHkVXfuoWTaTE-X1A6XsBvkcINJho' \ +-H 'content-type: multipart/form-data; boundary=----WebKitFormBoundaryZ9zBRAquVv1qb78o' \ +-H 'cookie: s=red1xa04ffeea4ed8b07c235277adc932a3e4d092859d; log_from=http%3A%2F%2Fwww2.leboncoin.fr%2Fdc%2Frules%3Fca%3D12_s; xtvrn=$266818$; ry_ry-l3b0nco_realytics=eyJpZCI6InJ5Xzg1M0VCQzVGLURCNjgtNDk1NC1COEE0LTM1OTY0RjhDN0U1RSIsImNpZCI6bnVsbCwiZXhwIjoxNjg0MzI0MDMzMDQ1LCJjcyI6bnVsbH0%3D; _pin_unauth=dWlkPU1HVXhPREF4TnpZdE1qTXhZUzAwTXpBd0xXSmpZVFF0TXpoallqRTNNamczTldVeg; _hjSessionUser_2783207=eyJpZCI6ImZjZTk5MmM1LTY0NmItNTViOS05OWY4LWRkYzc2YmE3ODc1YyIsImNyZWF0ZWQiOjE2NTI3ODgwMzQ1MjcsImV4aXN0aW5nIjp0cnVlfQ==; _scid=e7942114-373a-4d77-b41c-1717c1b77529; __Secure-Install=0505706a-942a-4369-a7ec-60f1d61afb65; __Secure-InstanceId=0505706a-942a-4369-a7ec-60f1d61afb65; didomi_token=eyJ1c2VyX2lkIjoiMTcyNmY1NWMtMjgwYS02Njc3LWFmMDAtZjk2YzM1MDM1NDQ3IiwiY3JlYXRlZCI6IjIwMjItMTItMDFUMTk6NTA6MDAuNzMwWiIsInVwZGF0ZWQiOiIyMDIyLTEyLTAxVDE5OjUwOjAwLjczMFoiLCJ2ZW5kb3JzIjp7ImVuYWJsZWQiOlsiZ29vZ2xlIiwiYzpsYmNmcmFuY2UiLCJjOnJldmxpZnRlci1jUnBNbnA1eCIsImM6ZGlkb21pIl19LCJwdXJwb3NlcyI6eyJlbmFibGVkIjpbInBlcnNvbm5hbGlzYXRpb25tYXJrZXRpbmciLCJwcml4IiwibWVzdXJlYXVkaWVuY2UiLCJleHBlcmllbmNldXRpbGlzYXRldXIiXX0sInZlbmRvcnNfbGkiOnsiZW5hYmxlZCI6WyJnb29nbGUiXX0sInZlcnNpb24iOjIsImFjIjoiRExXQkFBRUlBSXdBV1FCLWdHRkFQeUFra0JKWUVBd0lrZ1NrQXR5QnhBRHB3SFZnUU1BaW9CSE9DU2NFdFlLREFVSWdvdEJYT0N3VUZ0NExqQVhMQXdHQmhFREUwR1dvLkRMV0EtQUVJQUl3QV9RRENnSDVBU1NBa3NDQVlFU1FKU0FXNUE0Z0IwNERxd0lHQVJVQWpuQkpPQ1dzRkJnS0VRVVdncm5CWUtDMjhGeGdMbGdZREF3aUJpYURMVUFBQSJ9; euconsent-v2=CPjT1EAPjT1EAAHABBENCsCgAPLAAHLAAAAAIAtB_G_dTyPi-f59YvtwYQ1P4VQnoyACjgaNgwwJiRLBMI0EhmAIKAHqAAACIBAkICJAAQBlCAHAAAAA4IEAASMMAAAAIRAIIgCAAEAAAiJICABZCxAAAQAQgkwAABQAgAICABMgSDAAAAAAFAAAAAgAAAAAAAAAAAAAQAAAAAAAAggCACYatxAA2JY4E0gYRAAARhAEAUAIAKKAIWCAAgJEAAgjAAUYAAAAAoAAAAAAAAgBgAAAAEACEAAAADggEABAAgAAAAgEAgAAAAAQAAAYAAAAAABgAAAAAEABAAABQCAAAIAEABIEAAQAAAEAAAAAAAAAEAgAAAAAAAAAAAAAAACAGKAAwABBJYYABgACCSxAADAAEElg.flgADlgAAAAA; include_in_experiment=true; _gcl_au=1.1.396577628.1672180056; _fbp=fb.1.1674497618789.664585017; __gads=ID=3756ef2df471787f:T=1674498902:S=ALNI_MZYJfxMcpbJdw6KbbvraZ_khGHZnA; __gpi=UID=00000bc9e3cb6789:T=1674498902:RT=1674566983:S=ALNI_MbBn6iqQ8F-8Zpmb1WCKLM9NSFmxg; __gsas=ID=8ce0a4b086087df5:T=1674567077:S=ALNI_Mby9K0wXjz1NzkAZsVzbLFcccNtNw; adview_clickmeter=search__listing__4__8c78a2b2-4b20-497b-ae05-cf1e963b741d; luat=eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxMTY1NywiaWF0IjoxNjc3NjA0NDU2LCJpZCI6IjQ1MTM1OTUxLTY0ZjQtNDFjZS05NGVjLWJkMzkzZDRjY2U2ZiIsImluc3RhbGxfaWQiOiIwNTA1NzA2YS05NDJhLTQzNjktYTdlYy02MGYxZDYxYWZiNjUiLCJqdGkiOiJlYmQzZDI2My1mMTMzLTQ3MjktYjVjOS1kNTA3ZmYwZjUxNDEiLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6IjcwN2M3M2ZiLTk2NzQtNDZlNC05N2NmLTRkZTk2MzQ3NTYwZiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiI3NGM1ZGY4Yy05MzQ3LTQ0MWQtYWViYi0zZWIzYjYyZTk1MjMiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.dZMGwYAei7ovgsB6REy1XjtTjqbgVj4oXQomz7teIj-z0KEajW1pyLyBp4EweGpyb1McgSu8BOS74da9GEeNZ60x9pOAn9KiS8VNfqMBYiwknURnwI8NJdf9KiB6k__SzXb05uTyDeazLK76MoUIAImT8LwzMrFvdZewmmkqyYqT4o4Bcn0tynDkRLv5dSZ87n4ca0AsOsHt6zgWipwpqsGBom0ysYnOzq-hCkyM1-3SsjR4ohVT--qqR2EIijO2-SGk90kmwDzR9aYwCZzzRAlUTFhpE6-zHO7TquAV9oIQAU2Wmq5HgzhEREjUhJOI0fqXy9xk1dPRzb1A__rDbAm8Nkfxq-mF1JcaRM-nB2Pb1VgDV8j6P2MtPC8TlKyr9dMQFzuTWpvFa8sMYg92f3i1oLwvbgsHu5nweMqrWItDAwja7v35T3IReejBwKGOXXEmsTlJEcq589b2AdtZwH82mcFfwn6QkTPbJVGv7YiSLbyGNCQLbUJ-FhLptq2fLZwJUTEye72u-WzY5yeCxs8ZaIfaQHTduJrVviMlEfam9rnUUU-cUdA7NJx8bg63FqOYhEH-hFHYeo5gSF5EqA97jBwC2KoApADf4t1q5EhUPw7gGR9U7qQuhoRiTLVFV4kEjIWeX1QRntOHkVXfuoWTaTE-X1A6XsBvkcINJho; _hjSession_2783207=eyJpZCI6IjlmOWFiNjllLTk0MTctNDIwOS1iMTU0LWQwODM1OGU2MDVlZSIsImNyZWF0ZWQiOjE2Nzc2MDQ0NTc4NTgsImluU2FtcGxlIjp0cnVlfQ==; _hjAbsoluteSessionInProgress=0; ry_ry-l3b0nco_so_realytics=eyJpZCI6InJ5Xzg1M0VCQzVGLURCNjgtNDk1NC1COEE0LTM1OTY0RjhDN0U1RSIsImNpZCI6bnVsbCwib3JpZ2luIjpmYWxzZSwicmVmIjpudWxsLCJjb250IjpudWxsLCJucyI6ZmFsc2V9; _hjIncludedInSessionSample_2783207=1; cto_bundle=XggTMV9lYzVPa0lSRm5hMGZrMzB1ciUyRkhyamtZUmRmUHFhYkVVVDlMRWZMSUxRQW1PMGdOYyUyQkNwOE5QbFp3Y0U5NmV1aWtyZjg3b2xMZ09tOFVTdWFMenBPR2Y1anNhb0tteFZqUng5NDVBVmdzakRjdVh1cyUyRkdiYWxGVnZXTyUyRkl4dk9Q; datadome=4cwibCI1RYexaCD1wjZqJZ-6hUi16_fPyqPGEtsMSG-r3~8EoWMghXY6ZUZ3L~1GpA3vzRzDw__LqSlDp~FlYdAu_jP3M0N9vV8ZWBSbzQ0~ijJp6tNET4wW0fjfuKGn; utag_main=v_id:01792c6c39a9001726449a99708002069002106100bd0$_sn:149$_ss:0$_st:1677607622614$_pn:3%3Bexp-session$ses_id:1677604455561%3Bexp-session' \ +-H 'origin: https://www.leboncoin.fr' \ +-H 'referer: https://www.leboncoin.fr/deposer-une-annonce' \ +-H 'sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110"' \ +-H 'sec-ch-ua-mobile: ?0' \ +-H 'sec-ch-ua-platform: "Linux"' \ +-H 'sec-fetch-dest: empty' \ +-H 'sec-fetch-mode: cors' \ +-H 'sec-fetch-site: same-site' \ +-H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' \ +--data-raw $'------WebKitFormBoundaryZ9zBRAquVv1qb78o\r\nContent-Disposition: form-data; name="file"; filename="20230204_194811.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryZ9zBRAquVv1qb78o--\r\n' \ +--compressed +*/ diff --git a/native/src/lib.rs b/native/src/lib.rs index dfdd872..66e43fd 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -1,2 +1,10 @@ mod api; mod bridge_generated; +mod babelio; +mod cached_client; +mod common; +mod google_books; +mod image_tools; +mod publisher; +mod jwt_decoder; +mod leboncoin; diff --git a/native/src/publisher.rs b/native/src/publisher.rs new file mode 100644 index 0000000..329cbf3 --- /dev/null +++ b/native/src/publisher.rs @@ -0,0 +1,5 @@ +use crate::common::Ad; + +pub trait Publisher { + fn publish(&self, ad: Ad) -> bool; +} From d57e930de3aab2d84d018c530629393d35e4a001 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 9 Mar 2023 19:20:58 +0100 Subject: [PATCH 003/150] Rework cache so it works with flutter run --- native/.gitignore | 1 + native/src/babelio/request.rs | 6 +++--- native/src/cached_client.rs | 11 ++++++++--- native/src/lib.rs | 1 + 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/native/.gitignore b/native/.gitignore index 107eafc..8d19f7f 100644 --- a/native/.gitignore +++ b/native/.gitignore @@ -1 +1,2 @@ personal_info.rs +config.rs \ No newline at end of file diff --git a/native/src/babelio/request.rs b/native/src/babelio/request.rs index 79735a8..7895a40 100644 --- a/native/src/babelio/request.rs +++ b/native/src/babelio/request.rs @@ -19,7 +19,7 @@ struct BabelioISBNResponse { pub fn get_book_url(client: &CachedClient, isbn: &str) -> Option { let raw_search_results = client.get_from_cache( - format!("cache/babelio/get_book_url_{}.html", isbn).as_str(), + format!("babelio/get_book_url_{}.html", isbn).as_str(), |http_client| { http_client .post("https://www.babelio.com/aj_recherche.php") @@ -38,7 +38,7 @@ pub fn get_book_url(client: &CachedClient, isbn: &str) -> Option { pub fn get_book_page(client: &CachedClient, url: String) -> String { client.get_from_cache( format!( - "cache/babelio/get_book_page_{}.html", + "babelio/get_book_page_{}.html", url.replace("/", "_slash_") ) .as_str(), @@ -54,7 +54,7 @@ pub fn get_book_page(client: &CachedClient, url: String) -> String { pub fn get_book_blurb_see_more(client: &CachedClient, id_obj: &str) -> String { client.get_from_cache( - format!("cache/babelio/get_book_blurb_see_more_{}.html", id_obj).as_str(), + format!("babelio/get_book_blurb_see_more_{}.html", id_obj).as_str(), |http_client| { let params = std::collections::HashMap::from([("type", "1"), ("id_obj", id_obj)]); diff --git a/native/src/cached_client.rs b/native/src/cached_client.rs index 55783ea..366cb18 100644 --- a/native/src/cached_client.rs +++ b/native/src/cached_client.rs @@ -8,12 +8,17 @@ impl CachedClient { cache_file_path: &str, make_request: F, ) -> String { - let html = std::fs::read_to_string(cache_file_path); + let cache_file_path = format!("{}/{}", crate::config::CACHE_PATH , cache_file_path); + let html = std::fs::read_to_string(&cache_file_path); match html { - Ok(f) => f, + Ok(f) => { + println!("Read request from cache {}", &cache_file_path); + f + }, Err(_) => { + println!("No file name {} in the cache", &cache_file_path); let resp = make_request(&self.http_client); - let write_res = std::fs::write(cache_file_path, &resp); + let write_res = std::fs::write(&cache_file_path, &resp); write_res.expect(format!("Can't write to file {}", cache_file_path).as_str()); resp } diff --git a/native/src/lib.rs b/native/src/lib.rs index 66e43fd..26a09fa 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -8,3 +8,4 @@ mod image_tools; mod publisher; mod jwt_decoder; mod leboncoin; +mod config; From 50c362d6b4cb1c9649ec4d5553d3156ed8464f64 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 9 Mar 2023 19:46:40 +0100 Subject: [PATCH 004/150] Better UI, display images --- lib/main.dart | 132 ++++++++++++++++++++++++++------------------------ 1 file changed, 69 insertions(+), 63 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 29b4a08..d013a62 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; @@ -13,37 +15,15 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // Try running your application with "flutter run". You'll see the - // application has a blue toolbar. Then, without quitting the app, try - // changing the primarySwatch below to Colors.green and then invoke - // "hot reload" (press "r" in the console where you ran "flutter run", - // or simply save your changes to "hot reload" in a Flutter IDE). - // Notice that the counter didn't reset back to zero; the application - // is not restarted. - primarySwatch: Colors.blue, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), + title: 'BookAdPublisher', + theme: ThemeData(primarySwatch: Colors.blue), + home: const MyHomePage(), ); } } class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; + const MyHomePage(); @override State createState() => _MyHomePageState(); @@ -54,20 +34,18 @@ class _MyHomePageState extends State { @override void initState() { super.initState(); - ad = api.getMetadataFromImages(imgsPath: [ - '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg' - ]); //.then((ad) => print('ad = $ad')); + ad = api.getMetadataFromImages( + imgsPath: ['/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg']); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(widget.title), + title: const Text('Create an automatic online book ad from picture'), ), body: Center( child: Column( - mainAxisAlignment: MainAxisAlignment.center, children: [ // To render the results of a Future, a FutureBuilder is used which // turns a Future into an AsyncSnapshot, which can be used to @@ -113,7 +91,7 @@ extension DoubleExt on double { } class AdPage extends StatefulWidget { - AdPage({super.key, required Ad ad}) : initialAd = ad; + const AdPage({required Ad ad}) : initialAd = ad; final Ad initialAd; @@ -132,37 +110,65 @@ class _AdPageState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - TextFormField( - initialValue: ad.title, - onChanged: (newText) { - setState(() { - ad.title = newText; - }); - }, - ), - TextFormField( - initialValue: ad.description, - maxLines: 20, - onChanged: (newText) { - setState(() { - ad.description = newText; - }); - }, - ), - TextFormField( - initialValue: ad.priceCent /*?*/ .divide(100).toString(), - onChanged: (newText) => setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), - ), - ElevatedButton( - onPressed: ad.priceCent == null - ? null - : () { - print('Try to publish'); - }, - child: const Text("Publish")) - ], + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + TextFormField( + initialValue: ad.title, + onChanged: (newText) => setState(() => ad.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Ad title', + ), + style: const TextStyle(fontSize: 30), + ), + TextFormField( + initialValue: ad.description, + maxLines: 20, + onChanged: (newText) => setState(() => ad.description = newText), + decoration: const InputDecoration( + icon: Icon(Icons.text_snippet), + labelText: 'Ad description', + ), + ), + TextFormField( + initialValue: ad.priceCent /*?*/ .divide(100).toString(), + onChanged: (newText) => + setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', + ), + style: const TextStyle(fontSize: 20), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row(children: [ + const Icon( + Icons.image, + color: Colors.grey, + ), + const SizedBox(width: 16), + ...ad.imgsPath + .map((imgPath) => Image.file( + File(imgPath), + height: 200, + isAntiAlias: true, + filterQuality: FilterQuality.medium, + )) + .toList(), + ]), + ), + ElevatedButton( + onPressed: ad.priceCent == null + ? null + : () { + print('Try to publish'); + }, + child: const Text("Publish")) + ], + ), ); } } From 87973a07e231b8290b5af440a68a46f76daac547 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 9 Mar 2023 23:29:01 +0100 Subject: [PATCH 005/150] Publish ad with images, simplify GoogleBooks, show better error for convert --- lib/bridge_definitions.dart | 4 ++ lib/bridge_generated.dart | 83 ++++++++++++++++++++++++++++++ lib/main.dart | 9 ++-- native/src/api.rs | 9 ++-- native/src/bridge_generated.io.rs | 52 +++++++++++++++++++ native/src/bridge_generated.rs | 18 +++++++ native/src/google_books.rs | 4 +- native/src/google_books/parser.rs | 15 +++--- native/src/google_books/request.rs | 6 +-- native/src/image_tools.rs | 11 +++- native/src/leboncoin.rs | 1 + native/src/leboncoin/request.rs | 2 +- 12 files changed, 188 insertions(+), 26 deletions(-) diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 29fd663..8ce158f 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -12,6 +12,10 @@ abstract class Native { Future getMetadataFromImages({required List imgsPath, dynamic hint}); FlutterRustBridgeTaskConstMeta get kGetMetadataFromImagesConstMeta; + + Future publishAd({required Ad ad, dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta; } class Ad { diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 7ae6c5e..6239de2 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -43,6 +43,23 @@ class NativeImpl implements Native { argNames: ["imgsPath"], ); + Future publishAd({required Ad ad, dynamic hint}) { + var arg0 = _platform.api2wire_box_autoadd_ad(ad); + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_publish_ad(port_, arg0), + parseSuccessData: _wire2api_unit, + constMeta: kPublishAdConstMeta, + argValues: [ad], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "publish_ad", + argNames: ["ad"], + ); + void dispose() { _platform.dispose(); } @@ -79,10 +96,19 @@ class NativeImpl implements Native { Uint8List _wire2api_uint_8_list(dynamic raw) { return raw as Uint8List; } + + void _wire2api_unit(dynamic raw) { + return; + } } // Section: api2wire +@protected +int api2wire_i32(int raw) { + return raw; +} + @protected int api2wire_u8(int raw) { return raw; @@ -109,6 +135,13 @@ class NativePlatform extends FlutterRustBridgeBase { return ans; } + @protected + ffi.Pointer api2wire_box_autoadd_ad(Ad raw) { + final ptr = inner.new_box_autoadd_ad_0(); + _api_fill_to_wire_ad(raw, ptr.ref); + return ptr; + } + @protected ffi.Pointer api2wire_uint_8_list(Uint8List raw) { final ans = inner.new_uint_8_list_0(raw.length); @@ -118,6 +151,18 @@ class NativePlatform extends FlutterRustBridgeBase { // Section: finalizer // Section: api_fill_to_wire + + void _api_fill_to_wire_ad(Ad apiObj, wire_Ad wireObj) { + wireObj.title = api2wire_String(apiObj.title); + wireObj.description = api2wire_String(apiObj.description); + wireObj.price_cent = api2wire_i32(apiObj.priceCent); + wireObj.imgs_path = api2wire_StringList(apiObj.imgsPath); + } + + void _api_fill_to_wire_box_autoadd_ad( + Ad apiObj, ffi.Pointer wireObj) { + _api_fill_to_wire_ad(apiObj, wireObj.ref); + } } // ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names @@ -232,6 +277,23 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _wire_get_metadata_from_images = _wire_get_metadata_from_imagesPtr .asFunction)>(); + void wire_publish_ad( + int port_, + ffi.Pointer ad, + ) { + return _wire_publish_ad( + port_, + ad, + ); + } + + late final _wire_publish_adPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, ffi.Pointer)>>('wire_publish_ad'); + late final _wire_publish_ad = _wire_publish_adPtr + .asFunction)>(); + ffi.Pointer new_StringList_0( int len, ) { @@ -246,6 +308,16 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _new_StringList_0 = _new_StringList_0Ptr .asFunction Function(int)>(); + ffi.Pointer new_box_autoadd_ad_0() { + return _new_box_autoadd_ad_0(); + } + + late final _new_box_autoadd_ad_0Ptr = + _lookup Function()>>( + 'new_box_autoadd_ad_0'); + late final _new_box_autoadd_ad_0 = + _new_box_autoadd_ad_0Ptr.asFunction Function()>(); + ffi.Pointer new_uint_8_list_0( int len, ) { @@ -292,6 +364,17 @@ class wire_StringList extends ffi.Struct { external int len; } +class wire_Ad extends ffi.Struct { + external ffi.Pointer title; + + external ffi.Pointer description; + + @ffi.Int32() + external int price_cent; + + external ffi.Pointer imgs_path; +} + typedef DartPostCObjectFnType = ffi.Pointer< ffi.NativeFunction)>>; typedef DartPort = ffi.Int64; diff --git a/lib/main.dart b/lib/main.dart index d013a62..4d50e82 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,8 +34,10 @@ class _MyHomePageState extends State { @override void initState() { super.initState(); - ad = api.getMetadataFromImages( - imgsPath: ['/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg']); + ad = api.getMetadataFromImages(imgsPath: [ + '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/Camera/20230220_182059.jpg', + '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/Camera/20230220_182113.jpg' + ]); } @override @@ -164,7 +166,8 @@ class _AdPageState extends State { onPressed: ad.priceCent == null ? null : () { - print('Try to publish'); + print('Try to publish...'); + api.publishAd(ad: ad); }, child: const Text("Publish")) ], diff --git a/native/src/api.rs b/native/src/api.rs index 65e1ea0..5d94ca0 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -2,6 +2,7 @@ use std::process::Command; use itertools::Itertools; use crate::{babelio, common, google_books, leboncoin}; use crate::common::{Ad, BookMetaData}; +use crate::publisher::Publisher; pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { let isbns: Vec = imgs_path @@ -81,11 +82,11 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { price_cent: 1000, imgs_path, } +} - /*let publisher = leboncoin::Leboncoin {}; - - - publisher::Publisher::publish(&publisher, ad);*/ +pub fn publish_ad(ad: Ad) -> () { + let lbc_publisher = leboncoin::Leboncoin {}; + Publisher::publish(&lbc_publisher, ad); } fn book_format_title_and_author(book: &BookMetaData) -> String { diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index f335968..447aa2a 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -6,6 +6,11 @@ pub extern "C" fn wire_get_metadata_from_images(port_: i64, imgs_path: *mut wire wire_get_metadata_from_images_impl(port_, imgs_path) } +#[no_mangle] +pub extern "C" fn wire_publish_ad(port_: i64, ad: *mut wire_Ad) { + wire_publish_ad_impl(port_, ad) +} + // Section: allocate functions #[no_mangle] @@ -17,6 +22,11 @@ pub extern "C" fn new_StringList_0(len: i32) -> *mut wire_StringList { support::new_leak_box_ptr(wrap) } +#[no_mangle] +pub extern "C" fn new_box_autoadd_ad_0() -> *mut wire_Ad { + support::new_leak_box_ptr(wire_Ad::new_with_null_ptr()) +} + #[no_mangle] pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { let ans = wire_uint_8_list { @@ -45,6 +55,22 @@ impl Wire2Api> for *mut wire_StringList { vec.into_iter().map(Wire2Api::wire2api).collect() } } +impl Wire2Api for wire_Ad { + fn wire2api(self) -> Ad { + Ad { + title: self.title.wire2api(), + description: self.description.wire2api(), + price_cent: self.price_cent.wire2api(), + imgs_path: self.imgs_path.wire2api(), + } + } +} +impl Wire2Api for *mut wire_Ad { + fn wire2api(self) -> Ad { + let wrap = unsafe { support::box_from_leak_ptr(self) }; + Wire2Api::::wire2api(*wrap).into() + } +} impl Wire2Api> for *mut wire_uint_8_list { fn wire2api(self) -> Vec { @@ -63,6 +89,15 @@ pub struct wire_StringList { len: i32, } +#[repr(C)] +#[derive(Clone)] +pub struct wire_Ad { + title: *mut wire_uint_8_list, + description: *mut wire_uint_8_list, + price_cent: i32, + imgs_path: *mut wire_StringList, +} + #[repr(C)] #[derive(Clone)] pub struct wire_uint_8_list { @@ -82,6 +117,23 @@ impl NewWithNullPtr for *mut T { } } +impl NewWithNullPtr for wire_Ad { + fn new_with_null_ptr() -> Self { + Self { + title: core::ptr::null_mut(), + description: core::ptr::null_mut(), + price_cent: Default::default(), + imgs_path: core::ptr::null_mut(), + } + } +} + +impl Default for wire_Ad { + fn default() -> Self { + Self::new_with_null_ptr() + } +} + // Section: sync execution mode utility #[no_mangle] diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 991de3c..56be752 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -39,6 +39,19 @@ fn wire_get_metadata_from_images_impl( }, ) } +fn wire_publish_ad_impl(port_: MessagePort, ad: impl Wire2Api + UnwindSafe) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap( + WrapInfo { + debug_name: "publish_ad", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || { + let api_ad = ad.wire2api(); + move |task_callback| Ok(publish_ad(api_ad)) + }, + ) +} // Section: wrapper structs // Section: static checks @@ -62,6 +75,11 @@ where } } +impl Wire2Api for i32 { + fn wire2api(self) -> i32 { + self + } +} impl Wire2Api for u8 { fn wire2api(self) -> u8 { self diff --git a/native/src/google_books.rs b/native/src/google_books.rs index a2dd9aa..4b04879 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -8,9 +8,7 @@ impl common::Provider for GoogleBooks { fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { let client = reqwest::blocking::Client::builder().build().unwrap(); let isbn_search_response = request::search_by_isbn(&client, isbn); - let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response); - let book_page = request::get_volume(&client, &self_link); - Some(parser::extract_metadata_from_self_link_response(&book_page)) + Some(parser::extract_metadata_from_isbn_response(&isbn_search_response)) } } diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index c563d33..519137c 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -2,14 +2,11 @@ use itertools::Itertools; use crate::common; -pub fn extract_self_link_from_isbn_response(html: &str) -> String { - let s: structs::Root = serde_json::from_str(html).unwrap(); - s.items[0].self_link.to_string() -} +pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaData { + let owned_string = html.to_string(); + let s: structs::Root = serde_json::from_str(&owned_string).unwrap(); + let first_book = &s.items[0].volume_info; -pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaData { - let s: structs::Item = serde_json::from_str(html).unwrap(); - let first_book = &s.volume_info; common::BookMetaData { title: first_book.title.to_string(), authors: first_book @@ -21,7 +18,7 @@ pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaD }) .collect_vec(), - blurb: first_book.description.map(|d| d.to_string()), + blurb: first_book.description.to_owned(), ..Default::default() } } @@ -88,7 +85,7 @@ mod structs { pub authors: Vec<&'a str>, pub publisher: Option<&'a str>, pub published_date: &'a str, - pub description: Option<&'a str>, + pub description: Option, pub industry_identifiers: Vec>, pub reading_modes: ReadingModes, pub page_count: i64, diff --git a/native/src/google_books/request.rs b/native/src/google_books/request.rs index f0df992..8da9c9b 100644 --- a/native/src/google_books/request.rs +++ b/native/src/google_books/request.rs @@ -6,8 +6,4 @@ pub fn search_by_isbn(client: &reqwest::blocking::Client, isbn: &str) -> String .send() .unwrap(); resp.text().unwrap() -} -pub fn get_volume(client: &reqwest::blocking::Client, url: &str) -> String { - let resp = client.get(url).send().unwrap(); - resp.text().unwrap() -} +} \ No newline at end of file diff --git a/native/src/image_tools.rs b/native/src/image_tools.rs index c904ecd..27685c7 100644 --- a/native/src/image_tools.rs +++ b/native/src/image_tools.rs @@ -1,11 +1,20 @@ use std::{path::Path, process::Command}; +use std::process::ExitStatus; pub fn downsize_image(widht: u32, height: u32, input_filepath: &Path, output_filepath: &Path) { - Command::new("convert") + let output = Command::new("convert") .arg(input_filepath.to_str().unwrap()) .arg("-resize") .arg(format!("{}x{}^>", widht, height)) .arg(output_filepath.to_str().unwrap()) .output() .unwrap(); + if output.status.success() { + return; + } + println!("status: {}", output.status); + println!("stdout: {:?}", &std::str::from_utf8(&output.stdout)); + println!("stderr: {:?}", &std::str::from_utf8(&output.stderr)); + + assert!(output.status.success()); } diff --git a/native/src/leboncoin.rs b/native/src/leboncoin.rs index e91b912..8235aa8 100644 --- a/native/src/leboncoin.rs +++ b/native/src/leboncoin.rs @@ -30,6 +30,7 @@ impl Publisher for Leboncoin { } }) .collect_vec(); + // let img_lbc_refs = vec![]; let send_answer: String = request::send(ad, img_lbc_refs); let ad_id = parser::parse_send(&send_answer); diff --git a/native/src/leboncoin/request.rs b/native/src/leboncoin/request.rs index 7ecdbc7..d49ad99 100644 --- a/native/src/leboncoin/request.rs +++ b/native/src/leboncoin/request.rs @@ -306,7 +306,7 @@ pub fn upload_file(img_path: &Path) -> String { let form = reqwest::blocking::multipart::Form::new() .file("file", img_path) - .unwrap(); + .expect(&format!("Could not find file at path: {:?}", img_path)); let client = reqwest::blocking::Client::new(); let res = client From abd20ee4d53f81a4e2921507e076248151799b49 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 9 Mar 2023 23:47:49 +0100 Subject: [PATCH 006/150] clean --- native/src/google_books/parser.rs | 2 ++ native/src/image_tools.rs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index 519137c..ba577a6 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -85,6 +85,8 @@ mod structs { pub authors: Vec<&'a str>, pub publisher: Option<&'a str>, pub published_date: &'a str, + // Should be an owned String in case the description contain escape characters like (\") + // TODO: change all &str to String pub description: Option, pub industry_identifiers: Vec>, pub reading_modes: ReadingModes, diff --git a/native/src/image_tools.rs b/native/src/image_tools.rs index 27685c7..0040c24 100644 --- a/native/src/image_tools.rs +++ b/native/src/image_tools.rs @@ -1,5 +1,4 @@ use std::{path::Path, process::Command}; -use std::process::ExitStatus; pub fn downsize_image(widht: u32, height: u32, input_filepath: &Path, output_filepath: &Path) { let output = Command::new("convert") From cbc25516a1dc25c8c5ff22f3fc76baa06f4326ad Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 10 Mar 2023 21:28:25 +0100 Subject: [PATCH 007/150] WIP drag and drop --- lib/drag_and_drop.dart | 535 ++++++++++++++++++ lib/main.dart | 3 +- linux/flutter/generated_plugin_registrant.cc | 8 + linux/flutter/generated_plugins.cmake | 2 + macos/Flutter/GeneratedPluginRegistrant.swift | 4 + pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 6 + windows/flutter/generated_plugins.cmake | 2 + 8 files changed, 560 insertions(+), 1 deletion(-) create mode 100644 lib/drag_and_drop.dart diff --git a/lib/drag_and_drop.dart b/lib/drag_and_drop.dart new file mode 100644 index 0000000..23d4b05 --- /dev/null +++ b/lib/drag_and_drop.dart @@ -0,0 +1,535 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:ui' as ui; + +import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:super_clipboard/super_clipboard.dart'; +import 'package:super_drag_and_drop/super_drag_and_drop.dart'; + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class DragableWidget extends StatefulWidget { + const DragableWidget({ + super.key, + required this.name, + required this.color, + required this.dragItemProvider, + }); + + final String name; + final Color color; + final DragItemProvider dragItemProvider; + + @override + State createState() => _DragableWidgetState(); +} + +class _DragableWidgetState extends State { + bool _dragging = false; + + Future provideDragItem(AsyncValueGetter snapshot, DragSession session) async { + final item = await widget.dragItemProvider(snapshot, session); + if (item != null) { + setState(() { + _dragging = session.dragging; + }); + session.dragStarted.addListener(() { + setState(() { + _dragging = true; + }); + }); + session.dragCompleted.addListener(() { + if (mounted) { + setState(() { + _dragging = false; + }); + } + }); + } + return item; + } + + @override + Widget build(BuildContext context) { + return DragItemWidget( + allowedOperations: () => [DropOperation.copy], + canAddItemToExistingSession: true, + dragItemProvider: provideDragItem, + child: DraggableWidget( + child: AnimatedOpacity( + opacity: _dragging ? 0.5 : 1, + duration: const Duration(milliseconds: 200), + child: Container( + decoration: BoxDecoration( + color: widget.color, + borderRadius: BorderRadius.circular(14), + ), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), + child: Text( + widget.name, + style: const TextStyle(fontSize: 20, color: Colors.white), + textAlign: TextAlign.center, + ), + ), + ), + ), + ); + } +} + +Future createImageData(Color color) async { + final recorder = ui.PictureRecorder(); + final canvas = Canvas(recorder); + final paint = Paint()..color = color; + canvas.drawOval(const Rect.fromLTWH(0, 0, 200, 200), paint); + final picture = recorder.endRecording(); + final image = await picture.toImage(200, 200); + final data = await image.toByteData(format: ui.ImageByteFormat.png); + return data!.buffer.asUint8List(); +} + +class HomeLayout extends StatelessWidget { + const HomeLayout({ + super.key, + required this.draggable, + required this.dropZone, + }); + + final List draggable; + final Widget dropZone; + + @override + Widget build(BuildContext context) { + return SafeArea( + child: LayoutBuilder(builder: (context, constraints) { + if (constraints.maxWidth < 500) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Container( + padding: const EdgeInsets.all(16), + child: Wrap( + direction: Axis.horizontal, + runSpacing: 8, + spacing: 10, + children: draggable, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0).copyWith(top: 0), + child: dropZone, + ), + ), + ], + ); + } else { + return Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + textDirection: TextDirection.rtl, + children: [ + SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: IntrinsicWidth( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: draggable + .intersperse( + const SizedBox(height: 10), + ) + .toList(growable: false), + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0).copyWith(right: 0), + child: dropZone, + ), + ), + ], + ); + } + }), + ); + } +} + +extension IntersperseExtensions on Iterable { + Iterable intersperse(T element) sync* { + final iterator = this.iterator; + if (iterator.moveNext()) { + yield iterator.current; + while (iterator.moveNext()) { + yield element; + yield iterator.current; + } + } + } +} + +extension on DragSession { + Future hasLocalData(Object data) async { + final localData = await getLocalData() ?? []; + return localData.contains(data); + } +} + +class _MyHomePageState extends State { + void showMessage(String message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + duration: const Duration(milliseconds: 1500), + ), + ); + } + + Future textDragItem( + AsyncValueGetter dragImage, + DragSession session, + ) async { + // For multi drag on iOS check if this item is already in the session + if (await session.hasLocalData('text-item')) { + return null; + } + final item = DragItem(image: await dragImage(), localData: 'text-item', suggestedName: 'PlainText.txt'); + item.add(Formats.plainText('Plain Text Value')); + return item; + } + + Future imageDragItem( + AsyncValueGetter dragImage, + DragSession session, + ) async { + // For multi drag on iOS check if this item is already in the session + if (await session.hasLocalData('image-item')) { + return null; + } + final item = DragItem( + image: await dragImage(), + localData: 'image-item', + suggestedName: 'Green.png', + ); + item.add(Formats.png(await createImageData(Colors.green))); + return item; + } + + Future lazyImageDragItem( + AsyncValueGetter dragImage, + DragSession session, + ) async { + // For multi drag on iOS check if this item is already in the session + if (await session.hasLocalData('lazy-image-item')) { + return null; + } + final item = DragItem( + image: await dragImage(), + localData: 'lazy-image-item', + suggestedName: 'LazyBlue.png', + ); + item.add(Formats.png.lazy(() async { + showMessage('Requested lazy image.'); + return await createImageData(Colors.blue); + })); + return item; + } + + Future virtualFileDragItem( + AsyncValueGetter dragImage, + DragSession session, + ) async { + // For multi drag on iOS check if this item is already in the session + if (await session.hasLocalData('virtual-file-item')) { + return null; + } + final item = DragItem( + image: await dragImage(), + localData: 'virtual-file-item', + suggestedName: 'VirtualFile.txt', + ); + if (!item.virtualFileSupported) { + return null; + } + item.addVirtualFile( + format: Formats.plainTextFile, + provider: (sinkProvider, progress) { + showMessage('Requesting virtual file content.'); + final line = utf8.encode('Line in virtual file\n'); + const lines = 10; + final sink = sinkProvider(fileSize: line.length * lines); + for (var i = 0; i < lines; ++i) { + sink.add(line); + } + sink.close(); + }, + ); + return item; + } + + Future multipleRepresentationsDragItem( + AsyncValueGetter dragImage, + DragSession session, + ) async { + // For multi drag on iOS check if this item is already in the session + if (await session.hasLocalData('multiple-representations-item')) { + return null; + } + final item = DragItem( + image: await dragImage(), + localData: 'multiple-representations-item', + ); + item.add(Formats.png(await createImageData(Colors.pink))); + item.add(Formats.plainText("Hello World")); + item.add(Formats.uri(NamedUri(Uri.parse('https://flutter.dev'), name: 'Flutter'))); + return item; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: HomeLayout( + draggable: [ + DragableWidget( + name: 'Text', + color: Colors.red, + dragItemProvider: textDragItem, + ), + DragableWidget( + name: 'Image', + color: Colors.green, + dragItemProvider: imageDragItem, + ), + DragableWidget( + name: 'Image 2', + color: Colors.blue, + dragItemProvider: lazyImageDragItem, + ), + DragableWidget( + name: 'Virtual', + color: Colors.amber.shade700, + dragItemProvider: virtualFileDragItem, + ), + DragableWidget( + name: 'Multiple', + color: Colors.pink, + dragItemProvider: multipleRepresentationsDragItem, + ), + ], + dropZone: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.blueGrey.shade200), + borderRadius: BorderRadius.circular(14), + ), + child: _DropZone(), + ), + ), + ); + } +} + +extension _ReadValue on DataReader { + Future readValue(ValueFormat format) { + final c = Completer(); + final progress = getValue(format, (value) { + c.complete(value); + }, onError: (e) { + c.completeError(e); + }); + if (progress == null) { + c.complete(null); + } + return c.future; + } +} + +class _DropZone extends StatefulWidget { + @override + State createState() => _DropZoneState(); +} + +class _DropZoneState extends State<_DropZone> { + @override + Widget build(BuildContext context) { + return DropRegion( + formats: const [ + ...Formats.standardFormats, + ], + hitTestBehavior: HitTestBehavior.opaque, + onDropOver: _onDropOver, + onPerformDrop: _onPerformDrop, + onDropLeave: _onDropLeave, + child: Stack( + children: [ + Positioned.fill(child: _content), + Positioned.fill( + child: IgnorePointer( + child: AnimatedOpacity( + opacity: _isDragOver ? 1.0 : 0.0, + duration: const Duration(milliseconds: 200), + child: _preview, + ), + ), + ), + ], + ), + ); + } + + DropOperation _onDropOver(DropOverEvent event) { + setState(() { + _isDragOver = true; + _preview = Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(13), + color: Colors.black.withOpacity(0.2), + ), + child: Padding( + padding: const EdgeInsets.all(50), + child: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 400), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: ListView( + shrinkWrap: true, + children: event.session.items + .map((e) => _DropItemInfo(dropItem: e)) + .intersperse(Container( + height: 2, + color: Colors.white.withOpacity(0.7), + )) + .toList(growable: false), + ), + ), + ), + ), + ), + ); + }); + return event.session.allowedOperations.firstOrNull ?? DropOperation.none; + } + + Future _onPerformDrop(PerformDropEvent event) async { + final dataReader = event.session.items.first.dataReader!; + final sugg = await dataReader.getSuggestedName(); + print('sugg = $sugg'); + + final formats = dataReader.getFormats(Formats.standardFormats); + print("PerformDropEvent = ${formats}"); + formats.forEach((format) async { + switch (format) { + case Formats.plainText: + final text = (await dataReader.readValue(Formats.plainText))!; + print('text is $text'); + const mtpPrefix = 'mtp://'; + var path = text; + if (text.startsWith(mtpPrefix)) { + path = '/run/user/1000/gvfs/mtp:host=' + text.substring(mtpPrefix.length).replaceAll('%20', ' '); + } + print('path = $path'); + break; + default: + print('format not handled'); + } + }); + + /* + // Obtain additional reader information first + final readers = await Future.wait( + event.session.items.map( + (e) => ReaderInfo.fromReader( + e.dataReader!, + localData: e.localData, + ), + ), + ); + + if (!mounted) { + return; + } + + buildWidgetsForReaders(context, readers, (value) { + setState(() { + _content = ListView( + padding: const EdgeInsets.all(10), + children: value.intersperse(const SizedBox(height: 10)).toList(growable: false), + ); + }); + }); + */ + } + + void _onDropLeave(DropEvent event) { + setState(() { + _isDragOver = false; + }); + } + + bool _isDragOver = false; + + Widget _preview = const SizedBox(); + Widget _content = const Center( + child: Text( + 'Drop here', + style: TextStyle( + color: Colors.grey, + fontSize: 16, + ), + ), + ); +} + +class _DropItemInfo extends StatelessWidget { + const _DropItemInfo({ + required this.dropItem, + }); + + final DropItem dropItem; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + child: DefaultTextStyle.merge( + style: const TextStyle(fontSize: 11.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (dropItem.localData != null) + Text.rich(TextSpan(children: [ + const TextSpan( + text: 'Local data: ', + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(text: '${dropItem.localData}'), + ])), + const SizedBox( + height: 4, + ), + Text.rich(TextSpan(children: [ + const TextSpan( + text: 'Native formats: ', + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(text: dropItem.platformFormats.join(', ')), + ])), + ], + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 4d50e82..327702c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'drag_and_drop.dart' as drag_and_drop; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; void main() { @@ -17,7 +18,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'BookAdPublisher', theme: ThemeData(primarySwatch: Colors.blue), - home: const MyHomePage(), + home: const drag_and_drop.MyHomePage(title: 'title'), ); } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index e71a16d..819251b 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,14 @@ #include "generated_plugin_registrant.h" +#include +#include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) irondash_engine_context_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "IrondashEngineContextPlugin"); + irondash_engine_context_plugin_register_with_registrar(irondash_engine_context_registrar); + g_autoptr(FlPluginRegistrar) super_native_extensions_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SuperNativeExtensionsPlugin"); + super_native_extensions_plugin_register_with_registrar(super_native_extensions_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2e1de87..cd11e59 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + irondash_engine_context + super_native_extensions ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..35154be 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,10 @@ import FlutterMacOS import Foundation +import irondash_engine_context +import super_native_extensions func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) + SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) } diff --git a/pubspec.yaml b/pubspec.yaml index 298476d..b2dfd2e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: ffi: ^2.0.1 flutter_rust_bridge: ^1.45.0 meta: ^1.8.0 + super_drag_and_drop: ^0.2.3 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 8b6d468..94564b6 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,12 @@ #include "generated_plugin_registrant.h" +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { + IrondashEngineContextPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi")); + SuperNativeExtensionsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index b93c4c3..607cf1f 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + irondash_engine_context + super_native_extensions ) list(APPEND FLUTTER_FFI_PLUGIN_LIST From 2103eceaecdeac71bade0d09012ddb8b041e399f Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 10 Mar 2023 21:54:53 +0100 Subject: [PATCH 008/150] drop clean --- lib/drag_and_drop.dart | 418 ++--------------------------------------- lib/main.dart | 2 +- 2 files changed, 15 insertions(+), 405 deletions(-) diff --git a/lib/drag_and_drop.dart b/lib/drag_and_drop.dart index 23d4b05..0393312 100644 --- a/lib/drag_and_drop.dart +++ b/lib/drag_and_drop.dart @@ -1,343 +1,26 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:ui' as ui; import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:super_clipboard/super_clipboard.dart'; import 'package:super_drag_and_drop/super_drag_and_drop.dart'; -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class DragableWidget extends StatefulWidget { - const DragableWidget({ - super.key, - required this.name, - required this.color, - required this.dragItemProvider, - }); - - final String name; - final Color color; - final DragItemProvider dragItemProvider; - - @override - State createState() => _DragableWidgetState(); -} - -class _DragableWidgetState extends State { - bool _dragging = false; - - Future provideDragItem(AsyncValueGetter snapshot, DragSession session) async { - final item = await widget.dragItemProvider(snapshot, session); - if (item != null) { - setState(() { - _dragging = session.dragging; - }); - session.dragStarted.addListener(() { - setState(() { - _dragging = true; - }); - }); - session.dragCompleted.addListener(() { - if (mounted) { - setState(() { - _dragging = false; - }); - } - }); - } - return item; - } +class SelectImages extends StatelessWidget { + const SelectImages(); @override - Widget build(BuildContext context) { - return DragItemWidget( - allowedOperations: () => [DropOperation.copy], - canAddItemToExistingSession: true, - dragItemProvider: provideDragItem, - child: DraggableWidget( - child: AnimatedOpacity( - opacity: _dragging ? 0.5 : 1, - duration: const Duration(milliseconds: 200), - child: Container( - decoration: BoxDecoration( - color: widget.color, - borderRadius: BorderRadius.circular(14), - ), - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), - child: Text( - widget.name, - style: const TextStyle(fontSize: 20, color: Colors.white), - textAlign: TextAlign.center, - ), - ), + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: const Text('Drop the images to create a new ad'), ), - ), - ); - } -} - -Future createImageData(Color color) async { - final recorder = ui.PictureRecorder(); - final canvas = Canvas(recorder); - final paint = Paint()..color = color; - canvas.drawOval(const Rect.fromLTWH(0, 0, 200, 200), paint); - final picture = recorder.endRecording(); - final image = await picture.toImage(200, 200); - final data = await image.toByteData(format: ui.ImageByteFormat.png); - return data!.buffer.asUint8List(); -} - -class HomeLayout extends StatelessWidget { - const HomeLayout({ - super.key, - required this.draggable, - required this.dropZone, - }); - - final List draggable; - final Widget dropZone; - - @override - Widget build(BuildContext context) { - return SafeArea( - child: LayoutBuilder(builder: (context, constraints) { - if (constraints.maxWidth < 500) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Container( - padding: const EdgeInsets.all(16), - child: Wrap( - direction: Axis.horizontal, - runSpacing: 8, - spacing: 10, - children: draggable, - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(16.0).copyWith(top: 0), - child: dropZone, - ), - ), - ], - ); - } else { - return Row( - crossAxisAlignment: CrossAxisAlignment.stretch, - textDirection: TextDirection.rtl, - children: [ - SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: IntrinsicWidth( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: draggable - .intersperse( - const SizedBox(height: 10), - ) - .toList(growable: false), - ), - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(16.0).copyWith(right: 0), - child: dropZone, - ), - ), - ], - ); - } - }), - ); - } -} - -extension IntersperseExtensions on Iterable { - Iterable intersperse(T element) sync* { - final iterator = this.iterator; - if (iterator.moveNext()) { - yield iterator.current; - while (iterator.moveNext()) { - yield element; - yield iterator.current; - } - } - } -} - -extension on DragSession { - Future hasLocalData(Object data) async { - final localData = await getLocalData() ?? []; - return localData.contains(data); - } -} - -class _MyHomePageState extends State { - void showMessage(String message) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - duration: const Duration(milliseconds: 1500), - ), - ); - } - - Future textDragItem( - AsyncValueGetter dragImage, - DragSession session, - ) async { - // For multi drag on iOS check if this item is already in the session - if (await session.hasLocalData('text-item')) { - return null; - } - final item = DragItem(image: await dragImage(), localData: 'text-item', suggestedName: 'PlainText.txt'); - item.add(Formats.plainText('Plain Text Value')); - return item; - } - - Future imageDragItem( - AsyncValueGetter dragImage, - DragSession session, - ) async { - // For multi drag on iOS check if this item is already in the session - if (await session.hasLocalData('image-item')) { - return null; - } - final item = DragItem( - image: await dragImage(), - localData: 'image-item', - suggestedName: 'Green.png', - ); - item.add(Formats.png(await createImageData(Colors.green))); - return item; - } - - Future lazyImageDragItem( - AsyncValueGetter dragImage, - DragSession session, - ) async { - // For multi drag on iOS check if this item is already in the session - if (await session.hasLocalData('lazy-image-item')) { - return null; - } - final item = DragItem( - image: await dragImage(), - localData: 'lazy-image-item', - suggestedName: 'LazyBlue.png', - ); - item.add(Formats.png.lazy(() async { - showMessage('Requested lazy image.'); - return await createImageData(Colors.blue); - })); - return item; - } - - Future virtualFileDragItem( - AsyncValueGetter dragImage, - DragSession session, - ) async { - // For multi drag on iOS check if this item is already in the session - if (await session.hasLocalData('virtual-file-item')) { - return null; - } - final item = DragItem( - image: await dragImage(), - localData: 'virtual-file-item', - suggestedName: 'VirtualFile.txt', - ); - if (!item.virtualFileSupported) { - return null; - } - item.addVirtualFile( - format: Formats.plainTextFile, - provider: (sinkProvider, progress) { - showMessage('Requesting virtual file content.'); - final line = utf8.encode('Line in virtual file\n'); - const lines = 10; - final sink = sinkProvider(fileSize: line.length * lines); - for (var i = 0; i < lines; ++i) { - sink.add(line); - } - sink.close(); - }, - ); - return item; - } - - Future multipleRepresentationsDragItem( - AsyncValueGetter dragImage, - DragSession session, - ) async { - // For multi drag on iOS check if this item is already in the session - if (await session.hasLocalData('multiple-representations-item')) { - return null; - } - final item = DragItem( - image: await dragImage(), - localData: 'multiple-representations-item', - ); - item.add(Formats.png(await createImageData(Colors.pink))); - item.add(Formats.plainText("Hello World")); - item.add(Formats.uri(NamedUri(Uri.parse('https://flutter.dev'), name: 'Flutter'))); - return item; - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(widget.title), - ), - body: HomeLayout( - draggable: [ - DragableWidget( - name: 'Text', - color: Colors.red, - dragItemProvider: textDragItem, - ), - DragableWidget( - name: 'Image', - color: Colors.green, - dragItemProvider: imageDragItem, - ), - DragableWidget( - name: 'Image 2', - color: Colors.blue, - dragItemProvider: lazyImageDragItem, - ), - DragableWidget( - name: 'Virtual', - color: Colors.amber.shade700, - dragItemProvider: virtualFileDragItem, - ), - DragableWidget( - name: 'Multiple', - color: Colors.pink, - dragItemProvider: multipleRepresentationsDragItem, - ), - ], - dropZone: Container( + body: Container( decoration: BoxDecoration( border: Border.all(color: Colors.blueGrey.shade200), borderRadius: BorderRadius.circular(14), ), child: _DropZone(), ), - ), - ); - } + ); } extension _ReadValue on DataReader { @@ -403,16 +86,10 @@ class _DropZoneState extends State<_DropZone> { constraints: const BoxConstraints(maxWidth: 400), child: ClipRRect( borderRadius: BorderRadius.circular(10), - child: ListView( - shrinkWrap: true, - children: event.session.items - .map((e) => _DropItemInfo(dropItem: e)) - .intersperse(Container( - height: 2, - color: Colors.white.withOpacity(0.7), - )) - .toList(growable: false), - ), + child: Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), + child: Text('${event.session.items.length} images selected')), ), ), ), @@ -445,31 +122,6 @@ class _DropZoneState extends State<_DropZone> { print('format not handled'); } }); - - /* - // Obtain additional reader information first - final readers = await Future.wait( - event.session.items.map( - (e) => ReaderInfo.fromReader( - e.dataReader!, - localData: e.localData, - ), - ), - ); - - if (!mounted) { - return; - } - - buildWidgetsForReaders(context, readers, (value) { - setState(() { - _content = ListView( - padding: const EdgeInsets.all(10), - children: value.intersperse(const SizedBox(height: 10)).toList(growable: false), - ); - }); - }); - */ } void _onDropLeave(DropEvent event) { @@ -481,55 +133,13 @@ class _DropZoneState extends State<_DropZone> { bool _isDragOver = false; Widget _preview = const SizedBox(); - Widget _content = const Center( + final Widget _content = const Center( child: Text( - 'Drop here', + 'Drop images here', style: TextStyle( color: Colors.grey, - fontSize: 16, + fontSize: 32, ), ), ); } - -class _DropItemInfo extends StatelessWidget { - const _DropItemInfo({ - required this.dropItem, - }); - - final DropItem dropItem; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 10), - child: DefaultTextStyle.merge( - style: const TextStyle(fontSize: 11.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (dropItem.localData != null) - Text.rich(TextSpan(children: [ - const TextSpan( - text: 'Local data: ', - style: TextStyle(fontWeight: FontWeight.bold), - ), - TextSpan(text: '${dropItem.localData}'), - ])), - const SizedBox( - height: 4, - ), - Text.rich(TextSpan(children: [ - const TextSpan( - text: 'Native formats: ', - style: TextStyle(fontWeight: FontWeight.bold), - ), - TextSpan(text: dropItem.platformFormats.join(', ')), - ])), - ], - ), - ), - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index 327702c..e986901 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,7 +18,7 @@ class MyApp extends StatelessWidget { return MaterialApp( title: 'BookAdPublisher', theme: ThemeData(primarySwatch: Colors.blue), - home: const drag_and_drop.MyHomePage(title: 'title'), + home: const drag_and_drop.SelectImages(), ); } } From c1a6ddba437d68f5906a5220cfa98378da348ab8 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 10 Mar 2023 23:07:42 +0100 Subject: [PATCH 009/150] Drag and Drop works --- lib/drag_and_drop.dart | 66 +++++++++++++++++++++++++++++++++++++++--- lib/main.dart | 26 ++++++++++++----- native/src/api.rs | 5 ++++ 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/lib/drag_and_drop.dart b/lib/drag_and_drop.dart index 0393312..098c7fe 100644 --- a/lib/drag_and_drop.dart +++ b/lib/drag_and_drop.dart @@ -6,7 +6,8 @@ import 'package:super_clipboard/super_clipboard.dart'; import 'package:super_drag_and_drop/super_drag_and_drop.dart'; class SelectImages extends StatelessWidget { - const SelectImages(); + const SelectImages({required this.onSelect}); + final void Function(List paths) onSelect; @override Widget build(BuildContext context) => Scaffold( @@ -18,7 +19,7 @@ class SelectImages extends StatelessWidget { border: Border.all(color: Colors.blueGrey.shade200), borderRadius: BorderRadius.circular(14), ), - child: _DropZone(), + child: _DropZone(onSelect: onSelect), ), ); } @@ -39,6 +40,9 @@ extension _ReadValue on DataReader { } class _DropZone extends StatefulWidget { + const _DropZone({required this.onSelect}); + final void Function(List paths) onSelect; + @override State createState() => _DropZoneState(); } @@ -100,13 +104,67 @@ class _DropZoneState extends State<_DropZone> { } Future _onPerformDrop(PerformDropEvent event) async { + final pathsRaw = await event.session.items.first.dataReader!.readValue(Formats.plainText); + final paths = pathsRaw!.split('\n'); + final imgsPath = paths.where((e) => e.isNotEmpty).map((rawPath) { + print('A${rawPath}B'); + var path = rawPath; + const mtpPrefix = 'mtp://'; + if (rawPath.startsWith(mtpPrefix)) { + final p = rawPath.substring(mtpPrefix.length).trim(); + // final deviceName = p.substring(0, p.indexOf('/')); + // final pathInDevice = p.substring(p.indexOf('/')); + path = '/run/user/1000/gvfs/mtp:host=' + p.replaceAll('%20', ' '); + } + print('path = $path'); + return path; + }).toList(); + // The good way would be to read the Format.uri + // But it convert the device name to lowercase + // Because the device name can be mixed case it is thus not possible to retrieve the original device name + /*print('len = ${event.session.items}'); + final futureImgPaths = event.session.items.map((item) async { + final dataReader = item.dataReader!; + + for (final f in Formats.standardFormats.whereType()) { + print('f($f) = ${await dataReader.readValue(f)}'); + } + + /*final format = dataReader!.getFormats([Formats.plainText]).first; + switch (format) { + case Formats.plainText: + final text = (await dataReader.readValue(Formats.plainText))!; + break; + }*/ + print('plainText = ${await dataReader.readValue(Formats.plainText)}'); + final uri = await dataReader.readValue(Formats.uri); + // print('uri = ${uri?.uri.toFilePath(windows: false)}'); + + // final text = (await dataReader.readValue(Formats.plainText))!; + final text = uri!.uri.toString(); + print('text is $text'); + const mtpPrefix = 'mtp://'; + var path = text; + if (text.startsWith(mtpPrefix)) { + final p = text.substring(mtpPrefix.length); + final deviceName = p.substring(0, p.indexOf('/')); + final pathInDevice = p.substring(p.indexOf('/')); + path = '/run/user/1000/gvfs/mtp:host=' + deviceName.toUpperCase() + pathInDevice.replaceAll('%20', ' '); + } + print('path = $path'); + return path; + }).toList();*/ + + // final imgPaths = await Future.wait(futureImgPaths); + widget.onSelect(imgsPath); + /* final dataReader = event.session.items.first.dataReader!; final sugg = await dataReader.getSuggestedName(); print('sugg = $sugg'); final formats = dataReader.getFormats(Formats.standardFormats); print("PerformDropEvent = ${formats}"); - formats.forEach((format) async { + final imgsPaths = formats.map((format) async { switch (format) { case Formats.plainText: final text = (await dataReader.readValue(Formats.plainText))!; @@ -121,7 +179,7 @@ class _DropZoneState extends State<_DropZone> { default: print('format not handled'); } - }); + });*/ } void _onDropLeave(DropEvent event) { diff --git a/lib/main.dart b/lib/main.dart index e986901..4b5a1fe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,22 +9,35 @@ void main() { runApp(const MyApp()); } -class MyApp extends StatelessWidget { +class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); - // This widget is the root of your application. + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + List imgsPaths = []; + @override Widget build(BuildContext context) { return MaterialApp( title: 'BookAdPublisher', theme: ThemeData(primarySwatch: Colors.blue), - home: const drag_and_drop.SelectImages(), + home: imgsPaths.isEmpty + ? drag_and_drop.SelectImages(onSelect: (List paths) { + setState(() { + imgsPaths = paths; + }); + }) + : MyHomePage(imgsPaths), ); } } class MyHomePage extends StatefulWidget { - const MyHomePage(); + const MyHomePage(this.imgsPaths); + final List imgsPaths; @override State createState() => _MyHomePageState(); @@ -35,10 +48,7 @@ class _MyHomePageState extends State { @override void initState() { super.initState(); - ad = api.getMetadataFromImages(imgsPath: [ - '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/Camera/20230220_182059.jpg', - '/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/Camera/20230220_182113.jpg' - ]); + ad = api.getMetadataFromImages(imgsPath: widget.imgsPaths); } @override diff --git a/native/src/api.rs b/native/src/api.rs index 5d94ca0..2d6579c 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -16,6 +16,11 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { .arg("-in=".to_string() + &picture_path) .output() .expect("failed to execute process"); + if !output.status.success() { + println!("stdout is {:?}", std::str::from_utf8(&output.stdout).unwrap()); + println!("stderr is {:?}", std::str::from_utf8(&output.stderr).unwrap()); + panic!("output.status is {}", output.status) + } let output = std::str::from_utf8(&output.stdout).unwrap(); println!("output is {:?}", output); output From 785d4865656d23e6b5e8efa4b1e5aa86c8e60747 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 11 Mar 2023 09:47:45 +0100 Subject: [PATCH 010/150] Make babelio more resistant to no blurb --- native/src/api.rs | 10 +++++++--- native/src/babelio.rs | 19 +++++++++++-------- native/src/babelio/parser.rs | 31 +++++++++++-------------------- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/native/src/api.rs b/native/src/api.rs index 2d6579c..536aeae 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -56,7 +56,7 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { .collect(); let books_titles = books.iter().map(book_format_title_and_author).join("\n"); let blurbs = books - .iter() + .iter().filter(|b| b.blurb.is_some()) .map(|b| { format!( "{}:\n{}\n", @@ -69,7 +69,11 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { let custom_message = leboncoin::personal_info::CUSTOM_MESSAGE; - let mut ad_description = books_titles + "\n\nRésumé:\n" + &blurbs + "\n" + &custom_message; + let mut ad_description = books_titles; + if !blurbs.is_empty() { + ad_description += &("\n\nRésumé:\n".to_owned() + &blurbs); + } + ad_description += &("\n\n".to_owned() + &custom_message); if !keywords.is_empty() { ad_description = ad_description + "\n\nMots-clés:\n" + &keywords; } @@ -81,7 +85,7 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { title: if books.len() == 1 { books.first().unwrap().title.clone() } else { - todo!() + "".to_string() }, description: ad_description, price_cent: 1000, diff --git a/native/src/babelio.rs b/native/src/babelio.rs index 9878b25..3ff3255 100644 --- a/native/src/babelio.rs +++ b/native/src/babelio.rs @@ -14,15 +14,18 @@ impl common::Provider for Babelio { let book_page = request::get_book_page(&cached_client, book_url); let blurb_res = parser::extract_blurb(&book_page); - let raw_blurb = match blurb_res { - parser::BlurbRes::SmallBlurb(blurb) => blurb, - parser::BlurbRes::BigBlurb(id_obj) => { - request::get_book_blurb_see_more(&cached_client, &id_obj) - } - }; - let mut res = parser::extract_title_author_keywords(&book_page); - res.blurb = parser::parse_blurb(&raw_blurb); + + if let Some(blurb_res) = blurb_res { + let raw_blurb = match blurb_res { + parser::BlurbRes::SmallBlurb(blurb) => blurb, + parser::BlurbRes::BigBlurb(id_obj) => { + request::get_book_blurb_see_more(&cached_client, &id_obj) + } + }; + res.blurb = parser::parse_blurb(&raw_blurb); + } + Some(res) } } diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index d12517e..5d63474 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -7,27 +7,18 @@ pub enum BlurbRes { BigBlurb(String), } -pub fn extract_blurb(html: &str) -> BlurbRes { +pub fn extract_blurb(html: &str) -> Option { let doc = scraper::Html::parse_document(html); - let selector = scraper::Selector::parse("#d_bio").expect( - format!( - "Response should contain a element whose id is 'd_bio', html is {:?}", - html - ) - .as_str(), - ); + let selector = scraper::Selector::parse("#d_bio").expect("#d_bio should be a valid CSS selector"); let mut res = doc.select(&selector); - let d_bio = res.next().expect( - format!( - "There should be exactly one element with id 'd_bio', html {:?}", - html - ) - .as_str(), - ); + let d_bio = match res.next() { + None => return None, + Some(e) => e, + }; - // Some books do not folow the general strucuture: https://www.babelio.com/livres/Pullman--la-croisee-des-mondes-tome-2--La-tour-des-anges/59278 + // Some books do not follow the general structure: https://www.babelio.com/livres/Pullman--la-croisee-des-mondes-tome-2--La-tour-des-anges/59278 // It looks like a bug from Babelio because the style span do not close // So I must use a css-style selector instead of going down the DOM tree let s = scraper::Selector::parse("a[onclick^=\"javascript\"]").unwrap(); @@ -43,13 +34,13 @@ pub fn extract_blurb(html: &str) -> BlurbRes { .rev() .nth(1) .expect("d_bio should have a second to last children (the style span)"); - BlurbRes::SmallBlurb( + Some(BlurbRes::SmallBlurb( dbio_second_to_last_child .value() .as_text() .unwrap() .to_string(), - ) + )) } Some(on_click_element) => { let on_click = on_click_element @@ -63,7 +54,7 @@ pub fn extract_blurb(html: &str) -> BlurbRes { .next() .expect("The onclick should match with the regex"); let id_obj = &single_capture[1]; - BlurbRes::BigBlurb(String::from(id_obj)) + Some(BlurbRes::BigBlurb(String::from(id_obj))) } } } @@ -167,7 +158,7 @@ mod tests { fn extract_id_obj_from_file() { let html = std::fs::read_to_string("src/babelio/test/get_book.html").unwrap(); let id_obj = extract_blurb(&html); - assert_eq!(id_obj, BlurbRes::BigBlurb("827593".to_string())); + assert_eq!(id_obj, Some(BlurbRes::BigBlurb("827593".to_string()))); } #[test] pub fn extract_title_author_keywords_from_file() { From 5d483dd7229cf677cdd01fd4d7878501f91c9fc0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 11 Mar 2023 09:55:03 +0100 Subject: [PATCH 011/150] Undo part of commit 50c362d, which oversimplified GoogleBooks --- native/src/google_books.rs | 4 +++- native/src/google_books/parser.rs | 13 ++++++++----- native/src/google_books/request.rs | 6 +++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/native/src/google_books.rs b/native/src/google_books.rs index 4b04879..a2dd9aa 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -8,7 +8,9 @@ impl common::Provider for GoogleBooks { fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { let client = reqwest::blocking::Client::builder().build().unwrap(); let isbn_search_response = request::search_by_isbn(&client, isbn); - Some(parser::extract_metadata_from_isbn_response(&isbn_search_response)) + let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response); + let book_page = request::get_volume(&client, &self_link); + Some(parser::extract_metadata_from_self_link_response(&book_page)) } } diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index ba577a6..23202e4 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -2,11 +2,14 @@ use itertools::Itertools; use crate::common; -pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaData { - let owned_string = html.to_string(); - let s: structs::Root = serde_json::from_str(&owned_string).unwrap(); - let first_book = &s.items[0].volume_info; +pub fn extract_self_link_from_isbn_response(html: &str) -> String { + let s: structs::Root = serde_json::from_str(html).unwrap(); + s.items[0].self_link.to_string() +} +pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaData { + let s: structs::Item = serde_json::from_str(html).unwrap(); + let first_book = &s.volume_info; common::BookMetaData { title: first_book.title.to_string(), authors: first_book @@ -18,7 +21,7 @@ pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaData { }) .collect_vec(), - blurb: first_book.description.to_owned(), + blurb: first_book.description.clone(), ..Default::default() } } diff --git a/native/src/google_books/request.rs b/native/src/google_books/request.rs index 8da9c9b..f0df992 100644 --- a/native/src/google_books/request.rs +++ b/native/src/google_books/request.rs @@ -6,4 +6,8 @@ pub fn search_by_isbn(client: &reqwest::blocking::Client, isbn: &str) -> String .send() .unwrap(); resp.text().unwrap() -} \ No newline at end of file +} +pub fn get_volume(client: &reqwest::blocking::Client, url: &str) -> String { + let resp = client.get(url).send().unwrap(); + resp.text().unwrap() +} From c59fdc77537439084def2841d4813224b4b21db2 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 11 Mar 2023 10:24:40 +0100 Subject: [PATCH 012/150] Add GoogleBook comment --- native/src/google_books.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/src/google_books.rs b/native/src/google_books.rs index a2dd9aa..4d9c5de 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -6,6 +6,8 @@ pub struct GoogleBooks; impl common::Provider for GoogleBooks { fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + // TODO: For some books (eg 9782703305033), the description is better on the first page than in the second + // The number of authors can be different too ! let client = reqwest::blocking::Client::builder().build().unwrap(); let isbn_search_response = request::search_by_isbn(&client, isbn); let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response); @@ -13,5 +15,3 @@ impl common::Provider for GoogleBooks { Some(parser::extract_metadata_from_self_link_response(&book_page)) } } - - From 1105a53727a534b5e1909607dd64de0012447631 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 12 Mar 2023 12:30:48 +0100 Subject: [PATCH 013/150] Babelio return None instead of panic if there is no book itemscope --- native/src/babelio.rs | 6 ++---- native/src/babelio/parser.rs | 27 +++++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/native/src/babelio.rs b/native/src/babelio.rs index 3ff3255..3e20eb1 100644 --- a/native/src/babelio.rs +++ b/native/src/babelio.rs @@ -12,11 +12,9 @@ impl common::Provider for Babelio { }; let book_url = request::get_book_url(&cached_client, isbn)?; let book_page = request::get_book_page(&cached_client, book_url); - let blurb_res = parser::extract_blurb(&book_page); + let mut res = parser::extract_title_author_keywords(&book_page)?; - let mut res = parser::extract_title_author_keywords(&book_page); - - if let Some(blurb_res) = blurb_res { + if let Some(blurb_res) = parser::extract_blurb(&book_page) { let raw_blurb = match blurb_res { parser::BlurbRes::SmallBlurb(blurb) => blurb, parser::BlurbRes::BigBlurb(id_obj) => { diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index 5d63474..d366eac 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -1,5 +1,5 @@ use crate::common::{html_select, BookMetaData}; -use itertools::Itertools; +use itertools::{Itertools}; #[derive(PartialEq, Debug)] pub enum BlurbRes { @@ -10,7 +10,8 @@ pub enum BlurbRes { pub fn extract_blurb(html: &str) -> Option { let doc = scraper::Html::parse_document(html); - let selector = scraper::Selector::parse("#d_bio").expect("#d_bio should be a valid CSS selector"); + let selector = + scraper::Selector::parse("#d_bio").expect("#d_bio should be a valid CSS selector"); let mut res = doc.select(&selector); let d_bio = match res.next() { @@ -59,16 +60,18 @@ pub fn extract_blurb(html: &str) -> Option { } } -pub fn extract_title_author_keywords(html: &str) -> BookMetaData { +pub fn extract_title_author_keywords(html: &str) -> Option { let doc = scraper::Html::parse_document(html); let book_select = html_select("div[itemscope][itemtype=\"https://schema.org/Book\"]"); let res = doc.select(&book_select); - let book_scope = res.exactly_one().expect(format!( - "Response should contain a element whose with id is itemscope and itemtype=\"https://schema.org/Book\", html is {:?}", - html - ) - .as_str()); + let book_scope = match res.exactly_one() { + Ok(book_scope) => book_scope, + Err(_) => { + eprintln!("Response should contain a element whose with id is itemscope and itemtype=\"https://schema.org/Book\""); + return None; + } + }; let title_select = html_select("[itemprop=\"name\"]"); let mut res2 = book_scope.select(&title_select).into_iter(); let title = res2 @@ -138,12 +141,12 @@ pub fn extract_title_author_keywords(html: &str) -> BookMetaData { ) }) .collect(); - BookMetaData { + Some(BookMetaData { title, authors, keywords, ..Default::default() - } + }) } pub fn parse_blurb(raw_blurb: &str) -> Option { @@ -166,7 +169,7 @@ mod tests { let title_author_keywords = extract_title_author_keywords(&html); assert_eq!( title_author_keywords, - BookMetaData { + Some(BookMetaData { title: "Le nom de la bête".to_string(), authors: vec![crate::common::Author { first_name: "Daniel".to_string(), @@ -197,7 +200,7 @@ mod tests { ] .map(|s| s.to_string()) .to_vec(), - } + }) ); } } From 3360a4d83a54fc059a84a77ec5a8c6b8c560ee69 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 12 Mar 2023 12:52:24 +0100 Subject: [PATCH 014/150] GoogleBooks better handle no matche for isbn --- native/src/google_books.rs | 2 +- native/src/google_books/parser.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/native/src/google_books.rs b/native/src/google_books.rs index 4d9c5de..fddea62 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -10,7 +10,7 @@ impl common::Provider for GoogleBooks { // The number of authors can be different too ! let client = reqwest::blocking::Client::builder().build().unwrap(); let isbn_search_response = request::search_by_isbn(&client, isbn); - let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response); + let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response)?; let book_page = request::get_volume(&client, &self_link); Some(parser::extract_metadata_from_self_link_response(&book_page)) } diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index 23202e4..ea041b8 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -2,9 +2,9 @@ use itertools::Itertools; use crate::common; -pub fn extract_self_link_from_isbn_response(html: &str) -> String { +pub fn extract_self_link_from_isbn_response(html: &str) -> Option { let s: structs::Root = serde_json::from_str(html).unwrap(); - s.items[0].self_link.to_string() + s.items.map(|items| items[0].self_link.to_string()) } pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaData { @@ -38,7 +38,7 @@ mod tests { let self_link = extract_self_link_from_isbn_response(&html); assert_eq!( self_link, - "https://www.googleapis.com/books/v1/volumes/DQUFSQAACAAJ" + Some("https://www.googleapis.com/books/v1/volumes/DQUFSQAACAAJ".to_string()) ) } @@ -64,7 +64,7 @@ mod structs { pub struct Root<'a> { pub kind: &'a str, pub total_items: i64, - pub items: Vec>, + pub items: Option>>, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] From b1f0cf1aad889d08972f7bf6d8502bc6c9b5cc20 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 15 Mar 2023 19:27:46 +0100 Subject: [PATCH 015/150] Big refacto --- .gitignore | 2 + analysis_options.yaml | 34 ++------ lib/ad_editing.dart | 102 ++++++++++++++++++++++ lib/bridge_definitions.dart | 33 ++++++- lib/bridge_generated.dart | 86 +++++++++++++------ lib/isbn_decoding.dart | 31 +++++++ lib/main.dart | 137 ++++++++++++------------------ lib/metadata_collecting.dart | 86 +++++++++++++++++++ native/src/api.rs | 36 +++++--- native/src/bridge_generated.io.rs | 8 +- native/src/bridge_generated.rs | 40 ++++++--- native/src/lib.rs | 6 +- pubspec.yaml | 2 +- 13 files changed, 438 insertions(+), 165 deletions(-) create mode 100644 lib/ad_editing.dart create mode 100644 lib/isbn_decoding.dart create mode 100644 lib/metadata_collecting.dart diff --git a/.gitignore b/.gitignore index 09360f3..38d2398 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,8 @@ .pub/ /build/ +personal_info.* + # Web related # Symbolication related diff --git a/analysis_options.yaml b/analysis_options.yaml index 61b6c4d..da4804b 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,29 +1,13 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + use_key_in_widget_constructors: false + avoid_print: false + prefer_single_quotes: true + prefer_interpolation_to_compose_strings: false +analyzer: + enable-experiment: + - records + - patterns + - sealed-class \ No newline at end of file diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart new file mode 100644 index 0000000..a5b5a04 --- /dev/null +++ b/lib/ad_editing.dart @@ -0,0 +1,102 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/main.dart'; +import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; + +import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; + +class AdEditingWidget extends StatefulWidget { + const AdEditingWidget({required this.step}); + final AdEditingStep step; + + @override + State createState() => _AdEditingWidgetState(); +} + +String vecFmt(List vec) { + if (vec.length == 0) return ''; + if (vec.length == 1) return "de ${vec[0]}"; + if (vec.length == 2) return "de ${vec[0]} et ${vec[1]}"; + throw UnimplementedError('More than 2 authors'); +} + +String _bookFormatTitleAndAuthor(BookMetaData book) { + final authors = book.authors.map((a) => '${a.firstName} ${a.lastName}').toList(); + return '"${book.title}" ${vecFmt(authors)}'; +} + +class _AdEditingWidgetState extends State { + late Ad ad; + + @override + void initState() { + super.initState(); + ad.imgsPath = widget.step.imgsPaths; + final bookTitles = widget.step.metadata.entries.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); + final blurbs = widget.step.metadata.entries + .map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!) + .join('\n'); + ad.description = bookTitles + '\n\nRésumé:\n' + blurbs + '\n\n' + personal_info.customMessage; + final keywords = widget.step.metadata.entries.map((entry) => entry.value.keywords).join(', '); + if (keywords.isNotEmpty) { + ad.description += '\n\nMots-clés:\n' + keywords; + } + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + TextFormField( + initialValue: ad.title, + onChanged: (newText) => setState(() => ad.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Ad title', + ), + style: const TextStyle(fontSize: 30), + ), + TextFormField( + initialValue: ad.description, + maxLines: 20, + onChanged: (newText) => setState(() => ad.description = newText), + decoration: const InputDecoration( + icon: Icon(Icons.text_snippet), + labelText: 'Ad description', + ), + ), + TextFormField( + initialValue: ad.priceCent /*?*/ .divide(100).toString(), + onChanged: (newText) => + setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', + ), + style: const TextStyle(fontSize: 20), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row(children: [ + const Icon( + Icons.image, + color: Colors.grey, + ), + const SizedBox(width: 16), + ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), + ]), + ), + ElevatedButton( + onPressed: ad.priceCent == null + ? null + : () { + print('Try to publish...'); + api.publishAd(ad: ad); + }, + child: const Text("Publish")) + ], + ), + ); + } +} diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 8ce158f..6287545 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -9,9 +9,9 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; import 'package:meta/meta.dart'; abstract class Native { - Future getMetadataFromImages({required List imgsPath, dynamic hint}); + Future getMetadataFromProvider({required ProviderEnum provider, required String isbn, dynamic hint}); - FlutterRustBridgeTaskConstMeta get kGetMetadataFromImagesConstMeta; + FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; Future publishAd({required Ad ad, dynamic hint}); @@ -31,3 +31,32 @@ class Ad { required this.imgsPath, }); } + +class Author { + final String firstName; + final String lastName; + + const Author({ + required this.firstName, + required this.lastName, + }); +} + +class BookMetaData { + String title; + List authors; + String? blurb; + List keywords; + + BookMetaData({ + required this.title, + required this.authors, + this.blurb, + required this.keywords, + }); +} + +enum ProviderEnum { + Babelio, + GoogleBooks, +} diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 6239de2..2e6a4cb 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -24,23 +24,24 @@ class NativeImpl implements Native { factory NativeImpl.wasm(FutureOr module) => NativeImpl(module as ExternalLibrary); NativeImpl.raw(this._platform); - Future getMetadataFromImages( - {required List imgsPath, dynamic hint}) { - var arg0 = _platform.api2wire_StringList(imgsPath); + Future getMetadataFromProvider( + {required ProviderEnum provider, required String isbn, dynamic hint}) { + var arg0 = api2wire_provider_enum(provider); + var arg1 = _platform.api2wire_String(isbn); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => - _platform.inner.wire_get_metadata_from_images(port_, arg0), - parseSuccessData: _wire2api_ad, - constMeta: kGetMetadataFromImagesConstMeta, - argValues: [imgsPath], + _platform.inner.wire_get_metadata_from_provider(port_, arg0, arg1), + parseSuccessData: _wire2api_opt_box_autoadd_book_meta_data, + constMeta: kGetMetadataFromProviderConstMeta, + argValues: [provider, isbn], hint: hint, )); } - FlutterRustBridgeTaskConstMeta get kGetMetadataFromImagesConstMeta => + FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta => const FlutterRustBridgeTaskConstMeta( - debugName: "get_metadata_from_images", - argNames: ["imgsPath"], + debugName: "get_metadata_from_provider", + argNames: ["provider", "isbn"], ); Future publishAd({required Ad ad, dynamic hint}) { @@ -73,20 +74,42 @@ class NativeImpl implements Native { return (raw as List).cast(); } - Ad _wire2api_ad(dynamic raw) { + Author _wire2api_author(dynamic raw) { + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return Author( + firstName: _wire2api_String(arr[0]), + lastName: _wire2api_String(arr[1]), + ); + } + + BookMetaData _wire2api_book_meta_data(dynamic raw) { final arr = raw as List; if (arr.length != 4) throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); - return Ad( + return BookMetaData( title: _wire2api_String(arr[0]), - description: _wire2api_String(arr[1]), - priceCent: _wire2api_i32(arr[2]), - imgsPath: _wire2api_StringList(arr[3]), + authors: _wire2api_list_author(arr[1]), + blurb: _wire2api_opt_String(arr[2]), + keywords: _wire2api_StringList(arr[3]), ); } - int _wire2api_i32(dynamic raw) { - return raw as int; + BookMetaData _wire2api_box_autoadd_book_meta_data(dynamic raw) { + return _wire2api_book_meta_data(raw); + } + + List _wire2api_list_author(dynamic raw) { + return (raw as List).map(_wire2api_author).toList(); + } + + String? _wire2api_opt_String(dynamic raw) { + return raw == null ? null : _wire2api_String(raw); + } + + BookMetaData? _wire2api_opt_box_autoadd_book_meta_data(dynamic raw) { + return raw == null ? null : _wire2api_box_autoadd_book_meta_data(raw); } int _wire2api_u8(dynamic raw) { @@ -109,6 +132,11 @@ int api2wire_i32(int raw) { return raw; } +@protected +int api2wire_provider_enum(ProviderEnum raw) { + return api2wire_i32(raw.index); +} + @protected int api2wire_u8(int raw) { return raw; @@ -260,22 +288,26 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr .asFunction)>(); - void wire_get_metadata_from_images( + void wire_get_metadata_from_provider( int port_, - ffi.Pointer imgs_path, + int provider, + ffi.Pointer isbn, ) { - return _wire_get_metadata_from_images( + return _wire_get_metadata_from_provider( port_, - imgs_path, + provider, + isbn, ); } - late final _wire_get_metadata_from_imagesPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function(ffi.Int64, - ffi.Pointer)>>('wire_get_metadata_from_images'); - late final _wire_get_metadata_from_images = _wire_get_metadata_from_imagesPtr - .asFunction)>(); + late final _wire_get_metadata_from_providerPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function( + ffi.Int64, ffi.Int32, ffi.Pointer)>>( + 'wire_get_metadata_from_provider'); + late final _wire_get_metadata_from_provider = + _wire_get_metadata_from_providerPtr + .asFunction)>(); void wire_publish_ad( int port_, diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart new file mode 100644 index 0000000..9d25bc6 --- /dev/null +++ b/lib/isbn_decoding.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/main.dart'; + +class ISBNDecodingWidget extends StatefulWidget { + const ISBNDecodingWidget({required this.step, required this.onSubmit}); + final ISBNDecodingStep step; + final void Function(MetadataCollectingStep newStep) onSubmit; + + @override + State createState() => _ISBNDecodingWidgetState(); +} + +class _ISBNDecodingWidgetState extends State { + Map> isbns = {}; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row( + children: widget.step.imgsPaths + .map((imgPath) => Column( + children: [ + ImageWidget(imgPath), + ...isbns[imgPath]?.map((isbn) => Text(isbn)).toList() ?? [Text('no ISBN')], + ], + )) + .toList(), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 4b5a1fe..31988ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,8 +2,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'ad_editing.dart'; import 'drag_and_drop.dart' as drag_and_drop; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; +import 'isbn_decoding.dart'; +import 'metadata_collecting.dart'; void main() { runApp(const MyApp()); @@ -16,22 +19,57 @@ class MyApp extends StatefulWidget { State createState() => _MyAppState(); } -class _MyAppState extends State { +sealed class BookyStep {} + +class ImageSelectionStep implements BookyStep {} + +class ISBNDecodingStep implements BookyStep { + List imgsPaths = []; + ISBNDecodingStep({required this.imgsPaths}); +} + +class MetadataCollectingStep implements BookyStep { List imgsPaths = []; + Set isbns = {}; +} + +class AdEditingStep implements BookyStep { + List imgsPaths = []; + Set isbns = {}; + Map metadata = {}; +} +class _MyAppState extends State { + BookyStep step = ImageSelectionStep(); @override Widget build(BuildContext context) { return MaterialApp( - title: 'BookAdPublisher', - theme: ThemeData(primarySwatch: Colors.blue), - home: imgsPaths.isEmpty + title: 'BookAdPublisher', + theme: ThemeData(primarySwatch: Colors.blue), + home: switch (step) { + ImageSelectionStep() => drag_and_drop.SelectImages(onSelect: (List paths) { + setState(() { + step = ISBNDecodingStep(imgsPaths: paths); + }); + }), + ISBNDecodingStep() => ISBNDecodingWidget( + step: step as ISBNDecodingStep, + onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep)), + MetadataCollectingStep() => MetadataCollectingWidget( + step: step as MetadataCollectingStep, + onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), + AdEditingStep() => AdEditingWidget( + step: step as AdEditingStep, onSubmit: (bool publishSuccess) => setState(() => step = newStep)), + BookyStep() => throw UnimplementedError('Not possible') + } + /* imgsPaths.isEmpty ? drag_and_drop.SelectImages(onSelect: (List paths) { setState(() { imgsPaths = paths; }); }) - : MyHomePage(imgsPaths), - ); + : MyHomePage(imgsPaths),*/ + ); } } @@ -48,7 +86,7 @@ class _MyHomePageState extends State { @override void initState() { super.initState(); - ad = api.getMetadataFromImages(imgsPath: widget.imgsPaths); + // ad = api.getMetadataFromImages(imgsPath: widget.imgsPaths); } @override @@ -103,86 +141,17 @@ extension DoubleExt on double { double multiply(double other) => this * other; } -class AdPage extends StatefulWidget { - const AdPage({required Ad ad}) : initialAd = ad; - - final Ad initialAd; - - @override - State createState() => _AdPageState(); -} - -class _AdPageState extends State { - late Ad ad; - - @override - void initState() { - super.initState(); - ad = widget.initialAd; - } +class ImageWidget extends StatelessWidget { + const ImageWidget(this.imgPath); + final String imgPath; @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextFormField( - initialValue: ad.title, - onChanged: (newText) => setState(() => ad.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Ad title', - ), - style: const TextStyle(fontSize: 30), - ), - TextFormField( - initialValue: ad.description, - maxLines: 20, - onChanged: (newText) => setState(() => ad.description = newText), - decoration: const InputDecoration( - icon: Icon(Icons.text_snippet), - labelText: 'Ad description', - ), - ), - TextFormField( - initialValue: ad.priceCent /*?*/ .divide(100).toString(), - onChanged: (newText) => - setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), - decoration: const InputDecoration( - icon: Icon(Icons.euro), - labelText: 'Price', - ), - style: const TextStyle(fontSize: 20), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row(children: [ - const Icon( - Icons.image, - color: Colors.grey, - ), - const SizedBox(width: 16), - ...ad.imgsPath - .map((imgPath) => Image.file( - File(imgPath), - height: 200, - isAntiAlias: true, - filterQuality: FilterQuality.medium, - )) - .toList(), - ]), - ), - ElevatedButton( - onPressed: ad.priceCent == null - ? null - : () { - print('Try to publish...'); - api.publishAd(ad: ad); - }, - child: const Text("Publish")) - ], - ), + return Image.file( + File(imgPath), + height: 200, + isAntiAlias: true, + filterQuality: FilterQuality.medium, ); } } diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart new file mode 100644 index 0000000..21b6ccd --- /dev/null +++ b/lib/metadata_collecting.dart @@ -0,0 +1,86 @@ +import 'package:flutter/material.dart'; + +import 'bridge_definitions.dart'; +import 'main.dart'; +import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; + + +class MetadataCollectingWidget extends StatefulWidget { + const MetadataCollectingWidget({required this.step, required this.onSubmit}); + final MetadataCollectingStep step; + final void Function(AdEditingStep newStep) onSubmit; + + @override + State createState() => _MetadataCollectingWidgetState(); +} + +class Metadatas { + + final Map> mdFromProviders; + final BookMetaData manual; // = BookMetaData(title: '', authors: [], keywords: []); + Metadatas({required this.mdFromProviders, required this.manual}); +} + +class _MetadataCollectingWidgetState extends State { + Map metadata = {}; + + @override + void initState() { + super.initState(); + widget.step.isbns.forEach((isbn) { + metadata.putIfAbsent(isbn, () => Metadatas(manual: BookMetaData(title: '', authors: [], keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) => MapEntry(provider, api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!))))) ); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Row( + children: widget.step.isbns.map((isbn) { + // metadata[isbn]. + // if (metadata[isbn] == null) { + // metadata.a + // } + // if + return Column( + children: [ + // ImageWidget(imgPath), + Text('ISBN: $isbn'), + FutureBuilder( + future: metadata[isbn], + builder: (context, snap) { + if (snap.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + final book = snap.data!; + + return Column(children: [ + TextFormField( + initialValue: book.title, + onChanged: (newText) => setState(() => metadata.update(key, (value) => null)[isbn].title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + style: const TextStyle(fontSize: 30), + ), + TextFormField( + initialValue: book?.title, + onChanged: (newText) => setState(() => metadata[isbn].title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Book author', + ), + style: const TextStyle(fontSize: 30), + ), + ]), + }), + + // ...isbns[imgPath]?.map((isbn) => Text(isbn)).toList() ?? [Text('no ISBN')], + ], + ); + }).toList(), + ), + ); + } +} diff --git a/native/src/api.rs b/native/src/api.rs index 536aeae..1c65bfe 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,9 +1,25 @@ -use std::process::Command; -use itertools::Itertools; -use crate::{babelio, common, google_books, leboncoin}; +use crate::babelio::Babelio; +use crate::common::Provider; use crate::common::{Ad, BookMetaData}; +use crate::google_books::GoogleBooks; use crate::publisher::Publisher; +use crate::{babelio, common, google_books, leboncoin}; +use itertools::Itertools; +use std::process::Command; +enum ProviderEnum { + Babelio, + GoogleBooks, +} + +pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Option { + match provider { + ProviderEnum::Babelio => babelio::Babelio {}.get_book_metadata_from_isbn(&isbn), + ProviderEnum::GoogleBooks => google_books::GoogleBooks {}.get_book_metadata_from_isbn(&isbn), + } +} + +/* pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { let isbns: Vec = imgs_path .clone() @@ -39,19 +55,16 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { Box::new(google_books::GoogleBooks {}), ]; - let books: Vec = isbns + let books: Vec> = isbns .iter() .map(|isbn| { for provider in &book_metadata_providers { let res = provider.get_book_metadata_from_isbn(&isbn); if let Some(r) = res { - return r; + return Some(r); } } - panic!("No provider find any information on book {}", isbn) - /* book_metadata_providers[0] - .get_book_metadata_from_isbn(&isbn) - .unwrap() */ + None }) .collect(); let books_titles = books.iter().map(book_format_title_and_author).join("\n"); @@ -91,13 +104,13 @@ pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { price_cent: 1000, imgs_path, } -} +}*/ pub fn publish_ad(ad: Ad) -> () { let lbc_publisher = leboncoin::Leboncoin {}; Publisher::publish(&lbc_publisher, ad); } - +/* fn book_format_title_and_author(book: &BookMetaData) -> String { format!( "\"{}\" {}", @@ -119,3 +132,4 @@ fn vec_fmt(vec: Vec) -> String { _ => panic!("More than 2 authors"), } } +*/ diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index 447aa2a..e24e918 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -2,8 +2,12 @@ use super::*; // Section: wire functions #[no_mangle] -pub extern "C" fn wire_get_metadata_from_images(port_: i64, imgs_path: *mut wire_StringList) { - wire_get_metadata_from_images_impl(port_, imgs_path) +pub extern "C" fn wire_get_metadata_from_provider( + port_: i64, + provider: i32, + isbn: *mut wire_uint_8_list, +) { + wire_get_metadata_from_provider_impl(port_, provider, isbn) } #[no_mangle] diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 56be752..cec2d9f 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -20,22 +20,26 @@ use std::sync::Arc; // Section: imports use crate::common::Ad; +use crate::common::Author; +use crate::common::BookMetaData; // Section: wire functions -fn wire_get_metadata_from_images_impl( +fn wire_get_metadata_from_provider_impl( port_: MessagePort, - imgs_path: impl Wire2Api> + UnwindSafe, + provider: impl Wire2Api + UnwindSafe, + isbn: impl Wire2Api + UnwindSafe, ) { FLUTTER_RUST_BRIDGE_HANDLER.wrap( WrapInfo { - debug_name: "get_metadata_from_images", + debug_name: "get_metadata_from_provider", port: Some(port_), mode: FfiCallMode::Normal, }, move || { - let api_imgs_path = imgs_path.wire2api(); - move |task_callback| Ok(get_metadata_from_images(api_imgs_path)) + let api_provider = provider.wire2api(); + let api_isbn = isbn.wire2api(); + move |task_callback| Ok(get_metadata_from_provider(api_provider, api_isbn)) }, ) } @@ -80,6 +84,15 @@ impl Wire2Api for i32 { self } } +impl Wire2Api for i32 { + fn wire2api(self) -> ProviderEnum { + match self { + 0 => ProviderEnum::Babelio, + 1 => ProviderEnum::GoogleBooks, + _ => unreachable!("Invalid variant for ProviderEnum: {}", self), + } + } +} impl Wire2Api for u8 { fn wire2api(self) -> u8 { self @@ -88,18 +101,25 @@ impl Wire2Api for u8 { // Section: impl IntoDart -impl support::IntoDart for Ad { +impl support::IntoDart for Author { + fn into_dart(self) -> support::DartAbi { + vec![self.first_name.into_dart(), self.last_name.into_dart()].into_dart() + } +} +impl support::IntoDartExceptPrimitive for Author {} + +impl support::IntoDart for BookMetaData { fn into_dart(self) -> support::DartAbi { vec![ self.title.into_dart(), - self.description.into_dart(), - self.price_cent.into_dart(), - self.imgs_path.into_dart(), + self.authors.into_dart(), + self.blurb.into_dart(), + self.keywords.into_dart(), ] .into_dart() } } -impl support::IntoDartExceptPrimitive for Ad {} +impl support::IntoDartExceptPrimitive for BookMetaData {} // Section: executor diff --git a/native/src/lib.rs b/native/src/lib.rs index 26a09fa..c8c5bf4 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -1,11 +1,11 @@ mod api; -mod bridge_generated; mod babelio; +mod bridge_generated; mod cached_client; mod common; +mod config; mod google_books; mod image_tools; -mod publisher; mod jwt_decoder; mod leboncoin; -mod config; +mod publisher; diff --git a/pubspec.yaml b/pubspec.yaml index b2dfd2e..aa1fe86 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.17.5 <3.0.0" + sdk: '>=3.0.0-305.0.dev <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions From 6999e83220f110cd87c243692f421e7a1cf90fa2 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 15 Mar 2023 19:46:10 +0100 Subject: [PATCH 016/150] compile --- analysis_options.yaml | 2 + lib/ad_editing.dart | 10 +++-- lib/common.dart | 18 ++++++++ lib/isbn_decoding.dart | 2 + lib/main.dart | 34 +++------------ lib/metadata_collecting.dart | 81 ++++++++++++++++++++---------------- pubspec.yaml | 2 + 7 files changed, 81 insertions(+), 68 deletions(-) create mode 100644 lib/common.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index da4804b..8529660 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -6,6 +6,8 @@ linter: avoid_print: false prefer_single_quotes: true prefer_interpolation_to_compose_strings: false + prefer_is_empty: false + avoid_function_literals_in_foreach_calls: false analyzer: enable-experiment: - records diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index a5b5a04..e356508 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; +import 'common.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; class AdEditingWidget extends StatefulWidget { - const AdEditingWidget({required this.step}); + const AdEditingWidget({required this.step, required this.onSubmit}); final AdEditingStep step; + final void Function(bool newStep) onSubmit; @override State createState() => _AdEditingWidgetState(); @@ -14,8 +16,8 @@ class AdEditingWidget extends StatefulWidget { String vecFmt(List vec) { if (vec.length == 0) return ''; - if (vec.length == 1) return "de ${vec[0]}"; - if (vec.length == 2) return "de ${vec[0]} et ${vec[1]}"; + if (vec.length == 1) return 'de ${vec[0]}'; + if (vec.length == 2) return 'de ${vec[0]} et ${vec[1]}'; throw UnimplementedError('More than 2 authors'); } @@ -94,7 +96,7 @@ class _AdEditingWidgetState extends State { print('Try to publish...'); api.publishAd(ad: ad); }, - child: const Text("Publish")) + child: const Text('Publish')) ], ), ); diff --git a/lib/common.dart b/lib/common.dart new file mode 100644 index 0000000..0c253be --- /dev/null +++ b/lib/common.dart @@ -0,0 +1,18 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +class ImageWidget extends StatelessWidget { + const ImageWidget(this.imgPath); + final String imgPath; + + @override + Widget build(BuildContext context) { + return Image.file( + File(imgPath), + height: 200, + isAntiAlias: true, + filterQuality: FilterQuality.medium, + ); + } +} diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 9d25bc6..05bc0a4 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; +import 'common.dart'; + class ISBNDecodingWidget extends StatefulWidget { const ISBNDecodingWidget({required this.step, required this.onSubmit}); final ISBNDecodingStep step; diff --git a/lib/main.dart b/lib/main.dart index 31988ae..e5b3545 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'ad_editing.dart'; @@ -59,17 +57,10 @@ class _MyAppState extends State { step: step as MetadataCollectingStep, onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), AdEditingStep() => AdEditingWidget( - step: step as AdEditingStep, onSubmit: (bool publishSuccess) => setState(() => step = newStep)), + step: step as AdEditingStep, + onSubmit: (bool publishSuccess) => print('onSubmit with bool = $publishSuccess')), BookyStep() => throw UnimplementedError('Not possible') - } - /* imgsPaths.isEmpty - ? drag_and_drop.SelectImages(onSelect: (List paths) { - setState(() { - imgsPaths = paths; - }); - }) - : MyHomePage(imgsPaths),*/ - ); + }); } } @@ -121,9 +112,9 @@ class _MyHomePageState extends State { } final ad = snap.data; - if (ad == null) return const Text("Extracting info from images"); + if (ad == null) return const Text('Extracting info from images'); - return AdPage(ad: ad); + return const Text('extract finish'); }, ) ], @@ -140,18 +131,3 @@ extension IntExt on int { extension DoubleExt on double { double multiply(double other) => this * other; } - -class ImageWidget extends StatelessWidget { - const ImageWidget(this.imgPath); - final String imgPath; - - @override - Widget build(BuildContext context) { - return Image.file( - File(imgPath), - height: 200, - isAntiAlias: true, - filterQuality: FilterQuality.medium, - ); - } -} diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 21b6ccd..b156144 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; -import 'bridge_definitions.dart'; -import 'main.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; - +import 'main.dart'; class MetadataCollectingWidget extends StatefulWidget { const MetadataCollectingWidget({required this.step, required this.onSubmit}); @@ -15,7 +13,6 @@ class MetadataCollectingWidget extends StatefulWidget { } class Metadatas { - final Map> mdFromProviders; final BookMetaData manual; // = BookMetaData(title: '', authors: [], keywords: []); Metadatas({required this.mdFromProviders, required this.manual}); @@ -28,53 +25,67 @@ class _MetadataCollectingWidgetState extends State { void initState() { super.initState(); widget.step.isbns.forEach((isbn) { - metadata.putIfAbsent(isbn, () => Metadatas(manual: BookMetaData(title: '', authors: [], keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) => MapEntry(provider, api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!))))) ); + metadata.putIfAbsent( + isbn, + () => Metadatas( + manual: BookMetaData(title: '', authors: [], keywords: []), + mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) => MapEntry( + provider, api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!)))))); }); } @override Widget build(BuildContext context) { return Scaffold( - body: Row( + body: Column( children: widget.step.isbns.map((isbn) { // metadata[isbn]. // if (metadata[isbn] == null) { // metadata.a // } // if - return Column( + final manual = metadata[isbn]!.manual; + + return Row( children: [ // ImageWidget(imgPath), Text('ISBN: $isbn'), - FutureBuilder( - future: metadata[isbn], - builder: (context, snap) { - if (snap.connectionState != ConnectionState.done) { - return const CircularProgressIndicator(); - } - final book = snap.data!; + Column( + children: [ + TextFormField( + initialValue: manual.title, + onChanged: (newText) => setState(() => manual.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + style: const TextStyle(fontSize: 30), + ), + TextFormField( + initialValue: manual.blurb, + onChanged: (newText) => setState(() => manual.blurb = newText), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Book blurb', + ), + style: const TextStyle(fontSize: 30), + ), + ], + ), + ...metadata[isbn]!.mdFromProviders.entries.map((entry) => Text(entry.key.name) + /*FutureBuilder( + future: metadata[isbn], + builder: (context, snap) { + if (snap.connectionState != ConnectionState.done) { + return const CircularProgressIndicator(); + } + final book = snap.data!; + + return Column(children: [ - return Column(children: [ - TextFormField( - initialValue: book.title, - onChanged: (newText) => setState(() => metadata.update(key, (value) => null)[isbn].title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', - ), - style: const TextStyle(fontSize: 30), - ), - TextFormField( - initialValue: book?.title, - onChanged: (newText) => setState(() => metadata[isbn].title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Book author', - ), - style: const TextStyle(fontSize: 30), - ), - ]), - }), + ]), + })*/ + ), // ...isbns[imgPath]?.map((isbn) => Text(isbn)).toList() ?? [Text('no ISBN')], ], diff --git a/pubspec.yaml b/pubspec.yaml index aa1fe86..10b8a9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,8 @@ dependencies: flutter_rust_bridge: ^1.45.0 meta: ^1.8.0 super_drag_and_drop: ^0.2.3 + collection: ^1.17.1 + super_clipboard: ^0.2.3+1 dev_dependencies: flutter_test: From eb099d056aac6158bbd6edec1959dbbadee0f269 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 15 Mar 2023 21:22:26 +0100 Subject: [PATCH 017/150] Add isbn decoder --- lib/drag_and_drop.dart | 4 ++++ lib/isbn_decoding.dart | 47 +++++++++++++++++++++++++++++++++++++++++- native/src/api.rs | 6 ++++-- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/lib/drag_and_drop.dart b/lib/drag_and_drop.dart index 098c7fe..be36a4d 100644 --- a/lib/drag_and_drop.dart +++ b/lib/drag_and_drop.dart @@ -116,6 +116,10 @@ class _DropZoneState extends State<_DropZone> { // final pathInDevice = p.substring(p.indexOf('/')); path = '/run/user/1000/gvfs/mtp:host=' + p.replaceAll('%20', ' '); } + const filePrefix = 'file://'; + if (rawPath.startsWith(filePrefix)) { + path = rawPath.substring(filePrefix.length).trim(); + } print('path = $path'); return path; }).toList(); diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 05bc0a4..3937cf1 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; @@ -15,6 +17,49 @@ class ISBNDecodingWidget extends StatefulWidget { class _ISBNDecodingWidgetState extends State { Map> isbns = {}; + @override + void initState() { + // TODO: implement initState + super.initState(); + print('initState'); + widget.step.imgsPaths.forEach((imgPath) { + Future.microtask(() async { + final decoder_process = await Process.run( + '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', + ['-in=' + imgPath]); + if (decoder_process.exitCode != 0) { + print('stdout is ${decoder_process.stdout}'); + print('stderr is ${decoder_process.stderr}'); + throw Exception('decoder status is ${decoder_process.exitCode}'); + } + // final s = String.fromCharCodes((decoder_process.stdout as List)); + final s = decoder_process.stdout as String; + print('s = $s'); + setState(() { + isbns[imgPath] = s.split(' '); + }); + }); + }); +/* + let output = Command::new( + "/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode", + ) + .arg("-in=".to_string() + &picture_path) + .output() + .expect("failed to execute process"); + if !output.status.success() { + println!("stdout is {:?}", std::str::from_utf8(&output.stdout).unwrap()); + println!("stderr is {:?}", std::str::from_utf8(&output.stderr).unwrap()); + panic!("output.status is {}", output.status) + } + let output = std::str::from_utf8(&output.stdout).unwrap(); + println!("output is {:?}", output); + output + .split_ascii_whitespace() + .map(|x| x.to_string()) + .collect_vec()*/ + } + @override Widget build(BuildContext context) { return Scaffold( @@ -23,7 +68,7 @@ class _ISBNDecodingWidgetState extends State { .map((imgPath) => Column( children: [ ImageWidget(imgPath), - ...isbns[imgPath]?.map((isbn) => Text(isbn)).toList() ?? [Text('no ISBN')], + ...isbns[imgPath]!.map((isbn) => Text(isbn)).toList(), ], )) .toList(), diff --git a/native/src/api.rs b/native/src/api.rs index 1c65bfe..6945840 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -7,7 +7,7 @@ use crate::{babelio, common, google_books, leboncoin}; use itertools::Itertools; use std::process::Command; -enum ProviderEnum { +pub enum ProviderEnum { Babelio, GoogleBooks, } @@ -15,7 +15,9 @@ enum ProviderEnum { pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Option { match provider { ProviderEnum::Babelio => babelio::Babelio {}.get_book_metadata_from_isbn(&isbn), - ProviderEnum::GoogleBooks => google_books::GoogleBooks {}.get_book_metadata_from_isbn(&isbn), + ProviderEnum::GoogleBooks => { + google_books::GoogleBooks {}.get_book_metadata_from_isbn(&isbn) + } } } From dcd3aaa86041332ccfebd8a4b33cec00e3bced8b Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 15 Mar 2023 21:57:20 +0100 Subject: [PATCH 018/150] show basic metadatacollecting --- lib/isbn_decoding.dart | 65 ++++++++++++++++++------------------ lib/main.dart | 1 + lib/metadata_collecting.dart | 40 +++++++++++----------- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 3937cf1..e22dc5d 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -15,7 +15,7 @@ class ISBNDecodingWidget extends StatefulWidget { } class _ISBNDecodingWidgetState extends State { - Map> isbns = {}; + Map>> isbns = {}; @override void initState() { @@ -23,7 +23,7 @@ class _ISBNDecodingWidgetState extends State { super.initState(); print('initState'); widget.step.imgsPaths.forEach((imgPath) { - Future.microtask(() async { + isbns[imgPath] = Future(() async { final decoder_process = await Process.run( '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', ['-in=' + imgPath]); @@ -32,46 +32,45 @@ class _ISBNDecodingWidgetState extends State { print('stderr is ${decoder_process.stderr}'); throw Exception('decoder status is ${decoder_process.exitCode}'); } - // final s = String.fromCharCodes((decoder_process.stdout as List)); final s = decoder_process.stdout as String; - print('s = $s'); - setState(() { - isbns[imgPath] = s.split(' '); - }); + return s.split(' ').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); }); }); -/* - let output = Command::new( - "/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode", - ) - .arg("-in=".to_string() + &picture_path) - .output() - .expect("failed to execute process"); - if !output.status.success() { - println!("stdout is {:?}", std::str::from_utf8(&output.stdout).unwrap()); - println!("stderr is {:?}", std::str::from_utf8(&output.stderr).unwrap()); - panic!("output.status is {}", output.status) - } - let output = std::str::from_utf8(&output.stdout).unwrap(); - println!("output is {:?}", output); - output - .split_ascii_whitespace() - .map(|x| x.to_string()) - .collect_vec()*/ } @override Widget build(BuildContext context) { return Scaffold( body: Row( - children: widget.step.imgsPaths - .map((imgPath) => Column( - children: [ - ImageWidget(imgPath), - ...isbns[imgPath]!.map((isbn) => Text(isbn)).toList(), - ], - )) - .toList(), + children: [ + ...widget.step.imgsPaths + .map((imgPath) => Column( + children: [ + ImageWidget(imgPath), + FutureBuilder( + future: isbns[imgPath]!, + builder: (context, snap) { + if (snap.hasData == false) { + return const CircularProgressIndicator(); + } + return Column(children: snap.data!.map((isbn) => Text(isbn)).toList()); + }) + ], + )) + .toList(), + Spacer(), + FutureBuilder( + future: Future.wait(isbns.values), + builder: (context, snap) { + return ElevatedButton( + onPressed: () { + final isbnSet = snap.data!.expand((e) => e).toSet(); + print('isbnSet = $isbnSet'); + widget.onSubmit(MetadataCollectingStep(imgsPaths: widget.step.imgsPaths, isbns: isbnSet)); + }, + child: const Text('Validate ISBNs')); + }) + ], ), ); } diff --git a/lib/main.dart b/lib/main.dart index e5b3545..9cab584 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -29,6 +29,7 @@ class ISBNDecodingStep implements BookyStep { class MetadataCollectingStep implements BookyStep { List imgsPaths = []; Set isbns = {}; + MetadataCollectingStep({required this.imgsPaths, required this.isbns}); } class AdEditingStep implements BookyStep { diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index b156144..ef1769e 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -50,27 +50,29 @@ class _MetadataCollectingWidgetState extends State { children: [ // ImageWidget(imgPath), Text('ISBN: $isbn'), - Column( - children: [ - TextFormField( - initialValue: manual.title, - onChanged: (newText) => setState(() => manual.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', + Expanded( + child: Column( + children: [ + TextFormField( + initialValue: manual.title, + onChanged: (newText) => setState(() => manual.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + style: const TextStyle(fontSize: 30), ), - style: const TextStyle(fontSize: 30), - ), - TextFormField( - initialValue: manual.blurb, - onChanged: (newText) => setState(() => manual.blurb = newText), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Book blurb', + TextFormField( + initialValue: manual.blurb, + onChanged: (newText) => setState(() => manual.blurb = newText), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Book blurb', + ), + style: const TextStyle(fontSize: 30), ), - style: const TextStyle(fontSize: 30), - ), - ], + ], + ), ), ...metadata[isbn]!.mdFromProviders.entries.map((entry) => Text(entry.key.name) /*FutureBuilder( From c53ef46a96287e9f10562db3d34abf81fd3a60a9 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 15 Mar 2023 23:54:34 +0100 Subject: [PATCH 019/150] Use gridview for MetadataCollectingStep --- lib/metadata_collecting.dart | 99 ++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 54 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index ef1769e..ef6ff0e 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -37,62 +37,53 @@ class _MetadataCollectingWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Column( - children: widget.step.isbns.map((isbn) { - // metadata[isbn]. - // if (metadata[isbn] == null) { - // metadata.a - // } - // if - final manual = metadata[isbn]!.manual; - - return Row( - children: [ - // ImageWidget(imgPath), - Text('ISBN: $isbn'), - Expanded( - child: Column( - children: [ - TextFormField( - initialValue: manual.title, - onChanged: (newText) => setState(() => manual.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', - ), - style: const TextStyle(fontSize: 30), - ), - TextFormField( - initialValue: manual.blurb, - onChanged: (newText) => setState(() => manual.blurb = newText), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Book blurb', - ), - style: const TextStyle(fontSize: 30), + body: SingleChildScrollView( + child: Column( + children: widget.step.isbns.map((isbn) { + final manual = metadata[isbn]!.manual; + return Card( + margin: EdgeInsets.all(10), + child: Row( + children: [ + Text('ISBN: $isbn'), + Expanded( + child: GridView.count( + crossAxisCount: 3, + shrinkWrap: true, + children: [ + Text('Manual'), + Text('Babelio'), + Text('GoogleBooks'), + TextFormField( + initialValue: manual.title, + onChanged: (newText) => setState(() => manual.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + style: const TextStyle(fontSize: 30), + ), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => + FutureBuilder(future: e.value, builder: (context, snapMD) => Text(snapMD.data!.title))), + TextFormField( + initialValue: manual.blurb, + onChanged: (newText) => setState(() => manual.blurb = newText), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Book blurb', + ), + style: const TextStyle(fontSize: 30), + ), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( + future: e.value, builder: (context, snapMD) => Text(snapMD.data!.blurb ?? 'None'))) + ], ), - ], - ), - ), - ...metadata[isbn]!.mdFromProviders.entries.map((entry) => Text(entry.key.name) - /*FutureBuilder( - future: metadata[isbn], - builder: (context, snap) { - if (snap.connectionState != ConnectionState.done) { - return const CircularProgressIndicator(); - } - final book = snap.data!; - - return Column(children: [ - - ]), - })*/ ), - - // ...isbns[imgPath]?.map((isbn) => Text(isbn)).toList() ?? [Text('no ISBN')], - ], - ); - }).toList(), + ], + ), + ); + }).toList(), + ), ), ); } From 3ed60c5a12f79ebf06bc9140439dd1727f3db5f7 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 16 Mar 2023 00:16:57 +0100 Subject: [PATCH 020/150] SelectableText non scroll grid view --- lib/metadata_collecting.dart | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index ef6ff0e..4d2f6a9 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -14,7 +14,7 @@ class MetadataCollectingWidget extends StatefulWidget { class Metadatas { final Map> mdFromProviders; - final BookMetaData manual; // = BookMetaData(title: '', authors: [], keywords: []); + final BookMetaData manual; Metadatas({required this.mdFromProviders, required this.manual}); } @@ -45,10 +45,11 @@ class _MetadataCollectingWidgetState extends State { margin: EdgeInsets.all(10), child: Row( children: [ - Text('ISBN: $isbn'), + SelectableText('ISBN: $isbn'), Expanded( child: GridView.count( crossAxisCount: 3, + physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, children: [ Text('Manual'), @@ -63,8 +64,8 @@ class _MetadataCollectingWidgetState extends State { ), style: const TextStyle(fontSize: 30), ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => - FutureBuilder(future: e.value, builder: (context, snapMD) => Text(snapMD.data!.title))), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( + future: e.value, builder: (context, snapMD) => SelectableText(snapMD.data!.title))), TextFormField( initialValue: manual.blurb, onChanged: (newText) => setState(() => manual.blurb = newText), @@ -75,7 +76,12 @@ class _MetadataCollectingWidgetState extends State { style: const TextStyle(fontSize: 30), ), ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( - future: e.value, builder: (context, snapMD) => Text(snapMD.data!.blurb ?? 'None'))) + future: e.value, + builder: (context, snapMD) { + final blurb = snapMD.data!.blurb; + if (blurb == null) return Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + return SelectableText(blurb); + })) ], ), ), From a99a42ca85c19ec0f28746dcc625c51866e8bd73 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 16 Mar 2023 20:42:37 +0100 Subject: [PATCH 021/150] Use Table instead of GridView --- lib/metadata_collecting.dart | 69 +++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 4d2f6a9..15983b5 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -42,46 +42,49 @@ class _MetadataCollectingWidgetState extends State { children: widget.step.isbns.map((isbn) { final manual = metadata[isbn]!.manual; return Card( - margin: EdgeInsets.all(10), + margin: const EdgeInsets.all(10), child: Row( children: [ SelectableText('ISBN: $isbn'), Expanded( - child: GridView.count( - crossAxisCount: 3, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, + child: Table( children: [ - Text('Manual'), - Text('Babelio'), - Text('GoogleBooks'), - TextFormField( - initialValue: manual.title, - onChanged: (newText) => setState(() => manual.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', + const TableRow(children: [ + Text('Manual'), + Text('Babelio'), + Text('GoogleBooks'), + ]), + TableRow(children: [ + TextFormField( + initialValue: manual.title, + onChanged: (newText) => setState(() => manual.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), ), - style: const TextStyle(fontSize: 30), - ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( - future: e.value, builder: (context, snapMD) => SelectableText(snapMD.data!.title))), - TextFormField( - initialValue: manual.blurb, - onChanged: (newText) => setState(() => manual.blurb = newText), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Book blurb', + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( + future: e.value, builder: (context, snapMD) => SelectableText(snapMD.data!.title))), + ]), + TableRow(children: [ + TextFormField( + initialValue: manual.blurb, + onChanged: (newText) => setState(() => manual.blurb = newText), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Book blurb', + ), ), - style: const TextStyle(fontSize: 30), - ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( - future: e.value, - builder: (context, snapMD) { - final blurb = snapMD.data!.blurb; - if (blurb == null) return Text('None', style: TextStyle(fontStyle: FontStyle.italic)); - return SelectableText(blurb); - })) + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( + future: e.value, + builder: (context, snapMD) { + final blurb = snapMD.data!.blurb; + if (blurb == null) { + return const Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + } + return SelectableText(blurb); + })), + ]), ], ), ), From e8390e435c33240d3e4dcd8e6aad808e001d9cb0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 16 Mar 2023 20:56:29 +0100 Subject: [PATCH 022/150] Mock Step2 --- lib/main.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index 9cab584..140a3b5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -39,7 +39,16 @@ class AdEditingStep implements BookyStep { } class _MyAppState extends State { - BookyStep step = ImageSelectionStep(); + BookyStep step = //ImageSelectionStep(); + MetadataCollectingStep(imgsPaths: [ + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194758.jpg' + ], isbns: { + '9782253029854', + '9782277223634', + }); @override Widget build(BuildContext context) { return MaterialApp( From d02b9128c78e26b5d54a7a1537c7316fd3200f32 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 16 Mar 2023 22:33:43 +0100 Subject: [PATCH 023/150] Add FutureWidget --- lib/ad_editing.dart | 6 +- lib/common.dart | 35 ++++++++ lib/main.dart | 3 +- lib/metadata_collecting.dart | 160 +++++++++++++++++++++++------------ 4 files changed, 145 insertions(+), 59 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index e356508..3a64ca2 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -32,16 +32,16 @@ class _AdEditingWidgetState extends State { @override void initState() { super.initState(); - ad.imgsPath = widget.step.imgsPaths; final bookTitles = widget.step.metadata.entries.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); final blurbs = widget.step.metadata.entries .map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!) .join('\n'); - ad.description = bookTitles + '\n\nRésumé:\n' + blurbs + '\n\n' + personal_info.customMessage; + var description = bookTitles + '\n\nRésumé:\n' + blurbs + '\n\n' + personal_info.customMessage; final keywords = widget.step.metadata.entries.map((entry) => entry.value.keywords).join(', '); if (keywords.isNotEmpty) { - ad.description += '\n\nMots-clés:\n' + keywords; + description += '\n\nMots-clés:\n' + keywords; } + ad = Ad(title: '', description: description, priceCent: 1000, imgsPath: widget.step.imgsPaths); } @override diff --git a/lib/common.dart b/lib/common.dart index 0c253be..8d07977 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -2,6 +2,8 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'bridge_definitions.dart'; + class ImageWidget extends StatelessWidget { const ImageWidget(this.imgPath); final String imgPath; @@ -16,3 +18,36 @@ class ImageWidget extends StatelessWidget { ); } } + +class FutureWidget extends StatelessWidget { + const FutureWidget({required this.future, required this.builder}); + final Future future; + final Widget Function(T) builder; + + @override + Widget build(BuildContext context) { + return FutureBuilder(future: future, builder: (context, snap) => AsyncSnapshotWidget(snap: snap, builder: builder)); + } +} + +class AsyncSnapshotWidget extends StatelessWidget { + const AsyncSnapshotWidget({required this.snap, required this.builder}); + final AsyncSnapshot snap; + final Widget Function(T data) builder; + + @override + Widget build(BuildContext context) { + switch (snap.connectionState) { + case ConnectionState.waiting: + return const CircularProgressIndicator(); + case ConnectionState.done: + return builder(snap.data!); + default: + return const Text('???'); + } + } +} + +extension AuthorsExt on List { + String toText() => map((a) => '${a.firstName} ${a.lastName}').join('\n'); +} diff --git a/lib/main.dart b/lib/main.dart index 140a3b5..a31beec 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,8 +34,9 @@ class MetadataCollectingStep implements BookyStep { class AdEditingStep implements BookyStep { List imgsPaths = []; - Set isbns = {}; Map metadata = {}; + + AdEditingStep({required this.imgsPaths, required this.metadata}); } class _MyAppState extends State { diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 15983b5..783deab 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/common.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'main.dart'; @@ -14,7 +15,7 @@ class MetadataCollectingWidget extends StatefulWidget { class Metadatas { final Map> mdFromProviders; - final BookMetaData manual; + BookMetaData manual; Metadatas({required this.mdFromProviders, required this.manual}); } @@ -29,8 +30,13 @@ class _MetadataCollectingWidgetState extends State { isbn, () => Metadatas( manual: BookMetaData(title: '', authors: [], keywords: []), - mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) => MapEntry( - provider, api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!)))))); + mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { + final md = api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!); + /*if (provider == ProviderEnum.Babelio) { + md.then((value) => metadata[isbn]!.manual = value); + }*/ + return MapEntry(provider, md); + })))); }); } @@ -39,59 +45,103 @@ class _MetadataCollectingWidgetState extends State { return Scaffold( body: SingleChildScrollView( child: Column( - children: widget.step.isbns.map((isbn) { - final manual = metadata[isbn]!.manual; - return Card( - margin: const EdgeInsets.all(10), - child: Row( - children: [ - SelectableText('ISBN: $isbn'), - Expanded( - child: Table( - children: [ - const TableRow(children: [ - Text('Manual'), - Text('Babelio'), - Text('GoogleBooks'), - ]), - TableRow(children: [ - TextFormField( - initialValue: manual.title, - onChanged: (newText) => setState(() => manual.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', - ), - ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( - future: e.value, builder: (context, snapMD) => SelectableText(snapMD.data!.title))), - ]), - TableRow(children: [ - TextFormField( - initialValue: manual.blurb, - onChanged: (newText) => setState(() => manual.blurb = newText), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Book blurb', - ), - ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureBuilder( - future: e.value, - builder: (context, snapMD) { - final blurb = snapMD.data!.blurb; - if (blurb == null) { - return const Text('None', style: TextStyle(fontStyle: FontStyle.italic)); - } - return SelectableText(blurb); - })), - ]), - ], - ), + children: [ + ...widget.step.isbns.map((isbn) { + final manual = metadata[isbn]!.manual; + return Card( + margin: const EdgeInsets.all(10), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + SelectableText('ISBN: $isbn'), + Expanded( + child: Table( + children: [ + const TableRow(children: [ + Text('Manual'), + Text('Babelio'), + Text('GoogleBooks'), + ]), + TableRow(children: [ + FutureWidget( + future: metadata[isbn]!.mdFromProviders.entries.first.value, + builder: (data) => TextFormField( + initialValue: data.title, + onChanged: (newText) => setState(() => manual.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + )), + ...metadata[isbn]!.mdFromProviders.entries.map( + (e) => FutureWidget(future: e.value, builder: (data) => SelectableText(data.title))), + ]), + TableRow(children: [ + FutureWidget( + future: metadata[isbn]!.mdFromProviders.entries.first.value, + builder: (data) => TextFormField( + initialValue: data.authors.toText(), + onChanged: (newText) => setState(() => manual.authors = newText + .split('\n') + .map((line) => Author(firstName: '', lastName: line)) + .toList()), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Authors', + ), + ), + ), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final authors = data.authors; + if (authors.isEmpty) { + return const Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + } + return SelectableText(authors.toText()); + })), + ]), + TableRow(children: [ + FutureWidget( + future: metadata[isbn]!.mdFromProviders.entries.first.value, + builder: (data) => TextFormField( + initialValue: data.blurb, + onChanged: (newText) => setState(() => manual.blurb = newText), + decoration: const InputDecoration( + icon: Icon(Icons.description), + labelText: 'Book blurb', + ), + )), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final blurb = data.blurb; + if (blurb == null) { + return const Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + } + return SelectableText(blurb); + })), + ]), + ], + ), + ), + ], ), - ], - ), - ); - }).toList(), + ), + ); + }).toList(), + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () { + widget.onSubmit(AdEditingStep( + imgsPaths: widget.step.imgsPaths, + metadata: metadata.map((key, value) => MapEntry(key, value.manual)))); + }, + child: const Text('Validate Metadatas')), + ) + ], ), ), ); From 70704a4feef4a8066935676434effa7f3ddc9fe4 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 16 Mar 2023 22:44:34 +0100 Subject: [PATCH 024/150] Going to AdEditing works --- lib/ad_editing.dart | 101 ++++++++++++++++++----------------- lib/isbn_decoding.dart | 2 +- lib/metadata_collecting.dart | 7 ++- 3 files changed, 58 insertions(+), 52 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 3a64ca2..e67ef54 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -46,58 +46,61 @@ class _AdEditingWidgetState extends State { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextFormField( - initialValue: ad.title, - onChanged: (newText) => setState(() => ad.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Ad title', - ), - style: const TextStyle(fontSize: 30), - ), - TextFormField( - initialValue: ad.description, - maxLines: 20, - onChanged: (newText) => setState(() => ad.description = newText), - decoration: const InputDecoration( - icon: Icon(Icons.text_snippet), - labelText: 'Ad description', + return Scaffold( + appBar: AppBar(title: const Text('Ad editing')), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + TextFormField( + initialValue: ad.title, + onChanged: (newText) => setState(() => ad.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Ad title', + ), + style: const TextStyle(fontSize: 30), ), - ), - TextFormField( - initialValue: ad.priceCent /*?*/ .divide(100).toString(), - onChanged: (newText) => - setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), - decoration: const InputDecoration( - icon: Icon(Icons.euro), - labelText: 'Price', + TextFormField( + initialValue: ad.description, + maxLines: 20, + onChanged: (newText) => setState(() => ad.description = newText), + decoration: const InputDecoration( + icon: Icon(Icons.text_snippet), + labelText: 'Ad description', + ), ), - style: const TextStyle(fontSize: 20), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row(children: [ - const Icon( - Icons.image, - color: Colors.grey, + TextFormField( + initialValue: ad.priceCent /*?*/ .divide(100).toString(), + onChanged: (newText) => + setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', ), - const SizedBox(width: 16), - ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), - ]), - ), - ElevatedButton( - onPressed: ad.priceCent == null - ? null - : () { - print('Try to publish...'); - api.publishAd(ad: ad); - }, - child: const Text('Publish')) - ], + style: const TextStyle(fontSize: 20), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row(children: [ + const Icon( + Icons.image, + color: Colors.grey, + ), + const SizedBox(width: 16), + ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), + ]), + ), + ElevatedButton( + onPressed: ad.priceCent == null + ? null + : () { + print('Try to publish...'); + api.publishAd(ad: ad); + }, + child: const Text('Publish')) + ], + ), ), ); } diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index e22dc5d..1883989 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -58,7 +58,7 @@ class _ISBNDecodingWidgetState extends State { ], )) .toList(), - Spacer(), + const Spacer(), FutureBuilder( future: Future.wait(isbns.values), builder: (context, snap) { diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 783deab..7c4af51 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -32,9 +32,9 @@ class _MetadataCollectingWidgetState extends State { manual: BookMetaData(title: '', authors: [], keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { final md = api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!); - /*if (provider == ProviderEnum.Babelio) { + if (provider == ProviderEnum.Babelio) { md.then((value) => metadata[isbn]!.manual = value); - }*/ + } return MapEntry(provider, md); })))); }); @@ -43,6 +43,9 @@ class _MetadataCollectingWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( + appBar: AppBar( + title: const Text('Metadata Collecting'), + ), body: SingleChildScrollView( child: Column( children: [ From a22da44700409d2a4cbabe16a96926fad505fa9a Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 16 Mar 2023 22:58:01 +0100 Subject: [PATCH 025/150] Remove Windows, macos and ios target --- ios/.gitignore | 34 - ios/Flutter/AppFrameworkInfo.plist | 26 - ios/Flutter/Debug.xcconfig | 1 - ios/Flutter/Release.xcconfig | 1 - ios/Runner.xcodeproj/project.pbxproj | 560 --------------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 87 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - ios/Runner/AppDelegate.swift | 14 - .../AppIcon.appiconset/Contents.json | 122 ---- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 295 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 450 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 282 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 462 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 704 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 586 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 1674 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 762 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 1226 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 1418 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 - ios/Runner/Base.lproj/Main.storyboard | 26 - ios/Runner/Info.plist | 51 -- ios/Runner/Runner-Bridging-Header.h | 2 - ios/Runner/bridge_generated.h | 40 -- macos/.gitignore | 7 - macos/Flutter/Flutter-Debug.xcconfig | 1 - macos/Flutter/Flutter-Release.xcconfig | 1 - macos/Flutter/GeneratedPluginRegistrant.swift | 14 - macos/Runner.xcodeproj/project.pbxproj | 639 ------------------ .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 87 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - macos/Runner/AppDelegate.swift | 10 - .../AppIcon.appiconset/Contents.json | 68 -- .../AppIcon.appiconset/app_icon_1024.png | Bin 102994 -> 0 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 5680 -> 0 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 520 -> 0 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 14142 -> 0 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1066 -> 0 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 36406 -> 0 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 2218 -> 0 bytes macos/Runner/Base.lproj/MainMenu.xib | 343 ---------- macos/Runner/Configs/AppInfo.xcconfig | 14 - macos/Runner/Configs/Debug.xcconfig | 2 - macos/Runner/Configs/Release.xcconfig | 2 - macos/Runner/Configs/Warnings.xcconfig | 13 - macos/Runner/DebugProfile.entitlements | 12 - macos/Runner/Info.plist | 32 - macos/Runner/MainFlutterWindow.swift | 15 - macos/Runner/Release.entitlements | 8 - macos/Runner/bridge_generated.h | 40 -- web/favicon.png | Bin 917 -> 0 bytes web/icons/Icon-192.png | Bin 5292 -> 0 bytes web/icons/Icon-512.png | Bin 8252 -> 0 bytes web/icons/Icon-maskable-192.png | Bin 5594 -> 0 bytes web/icons/Icon-maskable-512.png | Bin 20998 -> 0 bytes web/index.html | 59 -- web/manifest.json | 35 - windows/.gitignore | 17 - windows/CMakeLists.txt | 102 --- windows/flutter/CMakeLists.txt | 104 --- .../flutter/generated_plugin_registrant.cc | 17 - windows/flutter/generated_plugin_registrant.h | 15 - windows/flutter/generated_plugins.cmake | 25 - windows/runner/CMakeLists.txt | 40 -- windows/runner/Runner.rc | 121 ---- windows/runner/flutter_window.cpp | 66 -- windows/runner/flutter_window.h | 33 - windows/runner/main.cpp | 43 -- windows/runner/resource.h | 16 - windows/runner/resources/app_icon.ico | Bin 33772 -> 0 bytes windows/runner/runner.exe.manifest | 20 - windows/runner/utils.cpp | 64 -- windows/runner/utils.h | 19 - windows/runner/win32_window.cpp | 288 -------- windows/runner/win32_window.h | 102 --- windows/rust.cmake | 21 - 93 files changed, 3613 deletions(-) delete mode 100644 ios/.gitignore delete mode 100644 ios/Flutter/AppFrameworkInfo.plist delete mode 100644 ios/Flutter/Debug.xcconfig delete mode 100644 ios/Flutter/Release.xcconfig delete mode 100644 ios/Runner.xcodeproj/project.pbxproj delete mode 100644 ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 ios/Runner/AppDelegate.swift delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 ios/Runner/Base.lproj/Main.storyboard delete mode 100644 ios/Runner/Info.plist delete mode 100644 ios/Runner/Runner-Bridging-Header.h delete mode 100644 ios/Runner/bridge_generated.h delete mode 100644 macos/.gitignore delete mode 100644 macos/Flutter/Flutter-Debug.xcconfig delete mode 100644 macos/Flutter/Flutter-Release.xcconfig delete mode 100644 macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 macos/Runner.xcodeproj/project.pbxproj delete mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 macos/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 macos/Runner/AppDelegate.swift delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png delete mode 100644 macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png delete mode 100644 macos/Runner/Base.lproj/MainMenu.xib delete mode 100644 macos/Runner/Configs/AppInfo.xcconfig delete mode 100644 macos/Runner/Configs/Debug.xcconfig delete mode 100644 macos/Runner/Configs/Release.xcconfig delete mode 100644 macos/Runner/Configs/Warnings.xcconfig delete mode 100644 macos/Runner/DebugProfile.entitlements delete mode 100644 macos/Runner/Info.plist delete mode 100644 macos/Runner/MainFlutterWindow.swift delete mode 100644 macos/Runner/Release.entitlements delete mode 100644 macos/Runner/bridge_generated.h delete mode 100644 web/favicon.png delete mode 100644 web/icons/Icon-192.png delete mode 100644 web/icons/Icon-512.png delete mode 100644 web/icons/Icon-maskable-192.png delete mode 100644 web/icons/Icon-maskable-512.png delete mode 100644 web/index.html delete mode 100644 web/manifest.json delete mode 100644 windows/.gitignore delete mode 100644 windows/CMakeLists.txt delete mode 100644 windows/flutter/CMakeLists.txt delete mode 100644 windows/flutter/generated_plugin_registrant.cc delete mode 100644 windows/flutter/generated_plugin_registrant.h delete mode 100644 windows/flutter/generated_plugins.cmake delete mode 100644 windows/runner/CMakeLists.txt delete mode 100644 windows/runner/Runner.rc delete mode 100644 windows/runner/flutter_window.cpp delete mode 100644 windows/runner/flutter_window.h delete mode 100644 windows/runner/main.cpp delete mode 100644 windows/runner/resource.h delete mode 100644 windows/runner/resources/app_icon.ico delete mode 100644 windows/runner/runner.exe.manifest delete mode 100644 windows/runner/utils.cpp delete mode 100644 windows/runner/utils.h delete mode 100644 windows/runner/win32_window.cpp delete mode 100644 windows/runner/win32_window.h delete mode 100644 windows/rust.cmake diff --git a/ios/.gitignore b/ios/.gitignore deleted file mode 100644 index 7a7f987..0000000 --- a/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 9625e10..0000000 --- a/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 11.0 - - diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig deleted file mode 100644 index 592ceee..0000000 --- a/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index b7d7efb..0000000 --- a/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,560 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - A3671AD929957A9600604FF0 /* libnative_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A3671AD529957A8000604FF0 /* libnative_static.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - A3671AD229957A8000604FF0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A3671ACD29957A7F00604FF0 /* native.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CA603EE7FF4DEAC3B2E0A336; - remoteInfo = "native-cdylib"; - }; - A3671AD429957A8000604FF0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A3671ACD29957A7F00604FF0 /* native.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CA60F0FEBE702969E816930C; - remoteInfo = "native-staticlib"; - }; - A3671AD629957A8A00604FF0 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A3671ACD29957A7F00604FF0 /* native.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = CA60F0FEBE701C83950DBC35; - remoteInfo = "native-staticlib"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A3671ACD29957A7F00604FF0 /* native.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = native.xcodeproj; path = ../native/native.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A3671AD929957A9600604FF0 /* libnative_static.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - A3671ACD29957A7F00604FF0 /* native.xcodeproj */, - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - A3671AD829957A9600604FF0 /* Frameworks */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; - A3671ACE29957A7F00604FF0 /* Products */ = { - isa = PBXGroup; - children = ( - A3671AD329957A8000604FF0 /* native.dylib */, - A3671AD529957A8000604FF0 /* libnative_static.a */, - ); - name = Products; - sourceTree = ""; - }; - A3671AD829957A9600604FF0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - A3671AD729957A8A00604FF0 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = A3671ACE29957A7F00604FF0 /* Products */; - ProjectRef = A3671ACD29957A7F00604FF0 /* native.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - A3671AD329957A8000604FF0 /* native.dylib */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.dylib"; - path = native.dylib; - remoteRef = A3671AD229957A8000604FF0 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - A3671AD529957A8000604FF0 /* libnative_static.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libnative_static.a; - remoteRef = A3671AD429957A8000604FF0 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - A3671AD729957A8A00604FF0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "native-staticlib"; - targetProxy = A3671AD629957A8A00604FF0 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRustBridgeTemplate; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRustBridgeTemplate; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRustBridgeTemplate; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index c87d15a..0000000 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift deleted file mode 100644 index 1958642..0000000 --- a/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,14 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - print("dummy_value=\(dummy_method_to_enforce_bundling())"); - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fa..0000000 --- a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 797d452e458972bab9d994556c8305db4c827017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 6ed2d933e1120817fe9182483a228007b18ab6ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cd7b0099ca80c806f8fe495613e8d6c69460d76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index fe730945a01f64a61e2235dbe3f45b08f7729182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 502f463a9bc882b461c96aadf492d1729e49e725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 0ec303439225b78712f49115768196d8d76f6790..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index e9f5fea27c705180eb716271f41b582e76dcbd90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 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 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 0467bf12aa4d28f374bb26596605a46dcbb3e7c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2..0000000 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725..0000000 --- a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c..0000000 --- a/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c2851..0000000 --- a/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist deleted file mode 100644 index 182d522..0000000 --- a/ios/Runner/Info.plist +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Flutter Rust Bridge Template - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - flutter_rust_bridge_template - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/ios/Runner/Runner-Bridging-Header.h b/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index ffb33c6..0000000 --- a/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1,2 +0,0 @@ -#import "GeneratedPluginRegistrant.h" -#import "bridge_generated.h" diff --git a/ios/Runner/bridge_generated.h b/ios/Runner/bridge_generated.h deleted file mode 100644 index ae8c386..0000000 --- a/ios/Runner/bridge_generated.h +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -typedef struct _Dart_Handle* Dart_Handle; - -typedef struct DartCObject DartCObject; - -typedef int64_t DartPort; - -typedef bool (*DartPostCObjectFnType)(DartPort port_id, void *message); - -typedef struct DartCObject *WireSyncReturn; - -void store_dart_post_cobject(DartPostCObjectFnType ptr); - -Dart_Handle get_dart_object(uintptr_t ptr); - -void drop_dart_object(uintptr_t ptr); - -uintptr_t new_dart_opaque(Dart_Handle handle); - -intptr_t init_frb_dart_api_dl(void *obj); - -void wire_platform(int64_t port_); - -void wire_rust_release_mode(int64_t port_); - -void free_WireSyncReturn(WireSyncReturn ptr); - -static int64_t dummy_method_to_enforce_bundling(void) { - int64_t dummy_var = 0; - dummy_var ^= ((int64_t) (void*) wire_platform); - dummy_var ^= ((int64_t) (void*) wire_rust_release_mode); - dummy_var ^= ((int64_t) (void*) free_WireSyncReturn); - dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); - dummy_var ^= ((int64_t) (void*) get_dart_object); - dummy_var ^= ((int64_t) (void*) drop_dart_object); - dummy_var ^= ((int64_t) (void*) new_dart_opaque); - return dummy_var; -} \ No newline at end of file diff --git a/macos/.gitignore b/macos/.gitignore deleted file mode 100644 index 746adbb..0000000 --- a/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index c2efd0b..0000000 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index c2efd0b..0000000 --- a/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 35154be..0000000 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import irondash_engine_context -import super_native_extensions - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin")) - SuperNativeExtensionsPlugin.register(with: registry.registrar(forPlugin: "SuperNativeExtensionsPlugin")) -} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index d99ce60..0000000 --- a/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,639 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - A38F8BAA29957A110018A868 /* libnative_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A38F8BA0299579880018A868 /* libnative_static.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; - A38F8B9D299579880018A868 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A38F8B98299579880018A868 /* native.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CA603EE7FF4DEAC3B2E0A336; - remoteInfo = "native-cdylib"; - }; - A38F8B9F299579880018A868 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A38F8B98299579880018A868 /* native.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = CA60F0FEBE702969E816930C; - remoteInfo = "native-staticlib"; - }; - A38F8BA829957A0D0018A868 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A38F8B98299579880018A868 /* native.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = CA60F0FEBE701C83950DBC35; - remoteInfo = "native-staticlib"; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* flutter_rust_bridge_template.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = flutter_rust_bridge_template.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - A38F8B98299579880018A868 /* native.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = native.xcodeproj; path = ../native/native.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - A38F8BAA29957A110018A868 /* libnative_static.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - A38F8B98299579880018A868 /* native.xcodeproj */, - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* flutter_rust_bridge_template.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - A38F8B99299579880018A868 /* Products */ = { - isa = PBXGroup; - children = ( - A38F8B9E299579880018A868 /* native.dylib */, - A38F8BA0299579880018A868 /* libnative_static.a */, - ); - name = Products; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - A38F8BA929957A0D0018A868 /* PBXTargetDependency */, - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* flutter_rust_bridge_template.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = A38F8B99299579880018A868 /* Products */; - ProjectRef = A38F8B98299579880018A868 /* native.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - A38F8B9E299579880018A868 /* native.dylib */ = { - isa = PBXReferenceProxy; - fileType = "compiled.mach-o.dylib"; - path = native.dylib; - remoteRef = A38F8B9D299579880018A868 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - A38F8BA0299579880018A868 /* libnative_static.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libnative_static.a; - remoteRef = A38F8B9F299579880018A868 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; - A38F8BA929957A0D0018A868 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "native-staticlib"; - targetProxy = A38F8BA829957A0D0018A868 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = Runner/bridge_generated.h; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = Runner/bridge_generated.h; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = Runner/bridge_generated.h; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 6ec83e6..0000000 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a1..0000000 --- a/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift deleted file mode 100644 index 35578dd..0000000 --- a/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - dummy_method_to_enforce_bundling() - return true - } -} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f..0000000 --- a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9a33e198f5747104729e1fcef999772a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba55c6dabc3aac36f33d859266c18fa0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5680 zcmaiYXH?Tqu=Xz`p-L#B_gI#0we$cm_HcmYFP$?wjD#BaCN4mzC5#`>w9y6=ThxrYZc0WPXprg zYjB`UsV}0=eUtY$(P6YW}npdd;%9pi?zS3k-nqCob zSX_AQEf|=wYT3r?f!*Yt)ar^;l3Sro{z(7deUBPd2~(SzZ-s@0r&~Km2S?8r##9-< z)2UOSVaHqq6}%sA9Ww;V2LG=PnNAh6mA2iWOuV7T_lRDR z&N8-eN=U)-T|;wo^Wv=34wtV0g}sAAe}`Ph@~!|<;z7*K8(qkX0}o=!(+N*UWrkEja*$_H6mhK1u{P!AC39} z|3+Z(mAOq#XRYS)TLoHv<)d%$$I@+x+2)V{@o~~J-!YUI-Q9%!Ldi4Op&Lw&B>jj* zwAgC#Y>gbIqv!d|J5f!$dbCXoq(l3GR(S>(rtZ~Z*agXMMKN!@mWT_vmCbSd3dUUm z4M&+gz?@^#RRGal%G3dDvj7C5QTb@9+!MG+>0dcjtZEB45c+qx*c?)d<%htn1o!#1 zpIGonh>P1LHu3s)fGFF-qS}AXjW|M*2Xjkh7(~r(lN=o#mBD9?jt74=Rz85I4Nfx_ z7Z)q?!};>IUjMNM6ee2Thq7))a>My?iWFxQ&}WvsFP5LP+iGz+QiYek+K1`bZiTV- zHHYng?ct@Uw5!gquJ(tEv1wTrRR7cemI>aSzLI^$PxW`wL_zt@RSfZ1M3c2sbebM* ze0=;sy^!90gL~YKISz*x;*^~hcCoO&CRD)zjT(A2b_uRue=QXFe5|!cf0z1m!iwv5GUnLw9Dr*Ux z)3Lc!J@Ei;&&yxGpf2kn@2wJ2?t6~obUg;?tBiD#uo$SkFIasu+^~h33W~`r82rSa ztyE;ehFjC2hjpJ-e__EH&z?!~>UBb=&%DS>NT)1O3Isn-!SElBV2!~m6v0$vx^a<@ISutdTk1@?;i z<8w#b-%|a#?e5(n@7>M|v<<0Kpg?BiHYMRe!3Z{wYc2hN{2`6(;q`9BtXIhVq6t~KMH~J0~XtUuT06hL8c1BYZWhN zk4F2I;|za*R{ToHH2L?MfRAm5(i1Ijw;f+0&J}pZ=A0;A4M`|10ZskA!a4VibFKn^ zdVH4OlsFV{R}vFlD~aA4xxSCTTMW@Gws4bFWI@xume%smAnuJ0b91QIF?ZV!%VSRJ zO7FmG!swKO{xuH{DYZ^##gGrXsUwYfD0dxXX3>QmD&`mSi;k)YvEQX?UyfIjQeIm! z0ME3gmQ`qRZ;{qYOWt}$-mW*>D~SPZKOgP)T-Sg%d;cw^#$>3A9I(%#vsTRQe%moT zU`geRJ16l>FV^HKX1GG7fR9AT((jaVb~E|0(c-WYQscVl(z?W!rJp`etF$dBXP|EG z=WXbcZ8mI)WBN>3<@%4eD597FD5nlZajwh8(c$lum>yP)F}=(D5g1-WVZRc)(!E3} z-6jy(x$OZOwE=~{EQS(Tp`yV2&t;KBpG*XWX!yG+>tc4aoxbXi7u@O*8WWFOxUjcq z^uV_|*818$+@_{|d~VOP{NcNi+FpJ9)aA2So<7sB%j`$Prje&auIiTBb{oD7q~3g0 z>QNIwcz(V-y{Ona?L&=JaV5`o71nIsWUMA~HOdCs10H+Irew#Kr(2cn>orG2J!jvP zqcVX0OiF}c<)+5&p}a>_Uuv)L_j}nqnJ5a?RPBNi8k$R~zpZ33AA4=xJ@Z($s3pG9 zkURJY5ZI=cZGRt_;`hs$kE@B0FrRx(6K{`i1^*TY;Vn?|IAv9|NrN*KnJqO|8$e1& zb?OgMV&q5|w7PNlHLHF) zB+AK#?EtCgCvwvZ6*u|TDhJcCO+%I^@Td8CR}+nz;OZ*4Dn?mSi97m*CXXc=};!P`B?}X`F-B5v-%ACa8fo0W++j&ztmqK z;&A)cT4ob9&MxpQU41agyMU8jFq~RzXOAsy>}hBQdFVL%aTn~M>5t9go2j$i9=(rZ zADmVj;Qntcr3NIPPTggpUxL_z#5~C!Gk2Rk^3jSiDqsbpOXf^f&|h^jT4|l2ehPat zb$<*B+x^qO8Po2+DAmrQ$Zqc`1%?gp*mDk>ERf6I|42^tjR6>}4`F_Mo^N(~Spjcg z_uY$}zui*PuDJjrpP0Pd+x^5ds3TG#f?57dFL{auS_W8|G*o}gcnsKYjS6*t8VI<) zcjqTzW(Hk*t-Qhq`Xe+x%}sxXRerScbPGv8hlJ;CnU-!Nl=# zR=iTFf9`EItr9iAlAGi}i&~nJ-&+)Y| zMZigh{LXe)uR+4D_Yb+1?I93mHQ5{pId2Fq%DBr7`?ipi;CT!Q&|EO3gH~7g?8>~l zT@%*5BbetH)~%TrAF1!-!=)`FIS{^EVA4WlXYtEy^|@y@yr!C~gX+cp2;|O4x1_Ol z4fPOE^nj(}KPQasY#U{m)}TZt1C5O}vz`A|1J!-D)bR%^+=J-yJsQXDzFiqb+PT0! zIaDWWU(AfOKlSBMS};3xBN*1F2j1-_=%o($ETm8@oR_NvtMDVIv_k zlnNBiHU&h8425{MCa=`vb2YP5KM7**!{1O>5Khzu+5OVGY;V=Vl+24fOE;tMfujoF z0M``}MNnTg3f%Uy6hZi$#g%PUA_-W>uVCYpE*1j>U8cYP6m(>KAVCmbsDf39Lqv0^ zt}V6FWjOU@AbruB7MH2XqtnwiXS2scgjVMH&aF~AIduh#^aT1>*V>-st8%=Kk*{bL zzbQcK(l2~)*A8gvfX=RPsNnjfkRZ@3DZ*ff5rmx{@iYJV+a@&++}ZW+za2fU>&(4y`6wgMpQGG5Ah(9oGcJ^P(H< zvYn5JE$2B`Z7F6ihy>_49!6}(-)oZ(zryIXt=*a$bpIw^k?>RJ2 zQYr>-D#T`2ZWDU$pM89Cl+C<;J!EzHwn(NNnWpYFqDDZ_*FZ{9KQRcSrl5T>dj+eA zi|okW;6)6LR5zebZJtZ%6Gx8^=2d9>_670!8Qm$wd+?zc4RAfV!ZZ$jV0qrv(D`db zm_T*KGCh3CJGb(*X6nXzh!h9@BZ-NO8py|wG8Qv^N*g?kouH4%QkPU~Vizh-D3<@% zGomx%q42B7B}?MVdv1DFb!axQ73AUxqr!yTyFlp%Z1IAgG49usqaEbI_RnbweR;Xs zpJq7GKL_iqi8Md?f>cR?^0CA+Uk(#mTlGdZbuC*$PrdB$+EGiW**=$A3X&^lM^K2s zzwc3LtEs5|ho z2>U(-GL`}eNgL-nv3h7E<*<>C%O^=mmmX0`jQb6$mP7jUKaY4je&dCG{x$`0=_s$+ zSpgn!8f~ya&U@c%{HyrmiW2&Wzc#Sw@+14sCpTWReYpF9EQ|7vF*g|sqG3hx67g}9 zwUj5QP2Q-(KxovRtL|-62_QsHLD4Mu&qS|iDp%!rs(~ah8FcrGb?Uv^Qub5ZT_kn%I^U2rxo1DDpmN@8uejxik`DK2~IDi1d?%~pR7i#KTS zA78XRx<(RYO0_uKnw~vBKi9zX8VnjZEi?vD?YAw}y+)wIjIVg&5(=%rjx3xQ_vGCy z*&$A+bT#9%ZjI;0w(k$|*x{I1c!ECMus|TEA#QE%#&LxfGvijl7Ih!B2 z6((F_gwkV;+oSKrtr&pX&fKo3s3`TG@ye+k3Ov)<#J|p8?vKh@<$YE@YIU1~@7{f+ zydTna#zv?)6&s=1gqH<-piG>E6XW8ZI7&b@-+Yk0Oan_CW!~Q2R{QvMm8_W1IV8<+ zQTyy=(Wf*qcQubRK)$B;QF}Y>V6d_NM#=-ydM?%EPo$Q+jkf}*UrzR?Nsf?~pzIj$ z<$wN;7c!WDZ(G_7N@YgZ``l;_eAd3+;omNjlpfn;0(B7L)^;;1SsI6Le+c^ULe;O@ zl+Z@OOAr4$a;=I~R0w4jO`*PKBp?3K+uJ+Tu8^%i<_~bU!p%so z^sjol^slR`W@jiqn!M~eClIIl+`A5%lGT{z^mRbpv}~AyO%R*jmG_Wrng{B9TwIuS z0!@fsM~!57K1l0%{yy(#no}roy#r!?0wm~HT!vLDfEBs9x#`9yCKgufm0MjVRfZ=f z4*ZRc2Lgr(P+j2zQE_JzYmP0*;trl7{*N341Cq}%^M^VC3gKG-hY zmPT>ECyrhIoFhnMB^qpdbiuI}pk{qPbK^}0?Rf7^{98+95zNq6!RuV_zAe&nDk0;f zez~oXlE5%ve^TmBEt*x_X#fs(-En$jXr-R4sb$b~`nS=iOy|OVrph(U&cVS!IhmZ~ zKIRA9X%Wp1J=vTvHZ~SDe_JXOe9*fa zgEPf;gD^|qE=dl>Qkx3(80#SE7oxXQ(n4qQ#by{uppSKoDbaq`U+fRqk0BwI>IXV3 zD#K%ASkzd7u>@|pA=)Z>rQr@dLH}*r7r0ng zxa^eME+l*s7{5TNu!+bD{Pp@2)v%g6^>yj{XP&mShhg9GszNu4ITW=XCIUp2Xro&1 zg_D=J3r)6hp$8+94?D$Yn2@Kp-3LDsci)<-H!wCeQt$e9Jk)K86hvV^*Nj-Ea*o;G zsuhRw$H{$o>8qByz1V!(yV{p_0X?Kmy%g#1oSmlHsw;FQ%j9S#}ha zm0Nx09@jmOtP8Q+onN^BAgd8QI^(y!n;-APUpo5WVdmp8!`yKTlF>cqn>ag`4;o>i zl!M0G-(S*fm6VjYy}J}0nX7nJ$h`|b&KuW4d&W5IhbR;-)*9Y0(Jj|@j`$xoPQ=Cl diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa40fb3d1e0710331a48de5d256da3f275d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 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 diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cfddf3d9dade342351e627a0a75609fb46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index b7d4028..0000000 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = flutter_rust_bridge_template - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterRustBridgeTemplate - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9..0000000 --- a/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49..0000000 --- a/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4..0000000 --- a/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a3..0000000 --- a/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist deleted file mode 100644 index 4789daa..0000000 --- a/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837..0000000 --- a/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a..0000000 --- a/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/macos/Runner/bridge_generated.h b/macos/Runner/bridge_generated.h deleted file mode 100644 index ae8c386..0000000 --- a/macos/Runner/bridge_generated.h +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -typedef struct _Dart_Handle* Dart_Handle; - -typedef struct DartCObject DartCObject; - -typedef int64_t DartPort; - -typedef bool (*DartPostCObjectFnType)(DartPort port_id, void *message); - -typedef struct DartCObject *WireSyncReturn; - -void store_dart_post_cobject(DartPostCObjectFnType ptr); - -Dart_Handle get_dart_object(uintptr_t ptr); - -void drop_dart_object(uintptr_t ptr); - -uintptr_t new_dart_opaque(Dart_Handle handle); - -intptr_t init_frb_dart_api_dl(void *obj); - -void wire_platform(int64_t port_); - -void wire_rust_release_mode(int64_t port_); - -void free_WireSyncReturn(WireSyncReturn ptr); - -static int64_t dummy_method_to_enforce_bundling(void) { - int64_t dummy_var = 0; - dummy_var ^= ((int64_t) (void*) wire_platform); - dummy_var ^= ((int64_t) (void*) wire_rust_release_mode); - dummy_var ^= ((int64_t) (void*) free_WireSyncReturn); - dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); - dummy_var ^= ((int64_t) (void*) get_dart_object); - dummy_var ^= ((int64_t) (void*) drop_dart_object); - dummy_var ^= ((int64_t) (void*) new_dart_opaque); - return dummy_var; -} \ No newline at end of file diff --git a/web/favicon.png b/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 zcmZ`-2T+sGz6~)*FVZ`aW+(v>MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e525556d5d89141648c724331630325d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691fbdb0b7efa65097c7cc1edac12a6d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20998 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 MPgg&ebxsLQ025IeI{*Lx diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 4b7bd51..0000000 --- a/web/index.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - flutter_rust_bridge_template - - - - - - - - - - diff --git a/web/manifest.json b/web/manifest.json deleted file mode 100644 index a589f20..0000000 --- a/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "flutter_rust_bridge_template", - "short_name": "flutter_rust_bridge_template", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/windows/.gitignore b/windows/.gitignore deleted file mode 100644 index d492d0d..0000000 --- a/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt deleted file mode 100644 index 8394b51..0000000 --- a/windows/CMakeLists.txt +++ /dev/null @@ -1,102 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.14) -project(flutter_rust_bridge_template LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "flutter_rust_bridge_template") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Define build configuration option. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() -# Define settings for the Profile build mode. -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build; see runner/CMakeLists.txt. -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - -include(./rust.cmake) - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt deleted file mode 100644 index 930d207..0000000 --- a/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 94564b6..0000000 --- a/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,17 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - IrondashEngineContextPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi")); - SuperNativeExtensionsPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SuperNativeExtensionsPluginCApi")); -} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d8..0000000 --- a/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 607cf1f..0000000 --- a/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,25 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - irondash_engine_context - super_native_extensions -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt deleted file mode 100644 index 394917c..0000000 --- a/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -# Define the application target. To change its name, change BINARY_NAME in the -# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer -# work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add preprocessor definitions for the build version. -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") - -# Disable Windows macros that collide with C++ standard library functions. -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") - -# Add dependency libraries and include directories. Add any application-specific -# dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc deleted file mode 100644 index 45b3617..0000000 --- a/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) -#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD -#else -#define VERSION_AS_NUMBER 1,0,0,0 -#endif - -#if defined(FLUTTER_VERSION) -#define VERSION_AS_STRING FLUTTER_VERSION -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "flutter_rust_bridge_template" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "flutter_rust_bridge_template" "\0" - VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "flutter_rust_bridge_template.exe" "\0" - VALUE "ProductName", "flutter_rust_bridge_template" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp deleted file mode 100644 index b25e363..0000000 --- a/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - - flutter_controller_->engine()->SetNextFrameCallback([&]() { - this->Show(); - }); - - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652..0000000 --- a/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp deleted file mode 100644 index 6be2cdc..0000000 --- a/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.Create(L"flutter_rust_bridge_template", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/windows/runner/resource.h b/windows/runner/resource.h deleted file mode 100644 index 66a65d1..0000000 --- a/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6370ebb9253ad831cc31de4a9c965f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 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 diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest deleted file mode 100644 index a42ea76..0000000 --- a/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp deleted file mode 100644 index f5bf9fa..0000000 --- a/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - std::string utf8_string; - if (target_length == 0 || target_length > utf8_string.max_size()) { - return utf8_string; - } - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/windows/runner/utils.h b/windows/runner/utils.h deleted file mode 100644 index 3879d54..0000000 --- a/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp deleted file mode 100644 index 041a385..0000000 --- a/windows/runner/win32_window.cpp +++ /dev/null @@ -1,288 +0,0 @@ -#include "win32_window.h" - -#include -#include - -#include "resource.h" - -namespace { - -/// Window attribute that enables dark mode window decorations. -/// -/// Redefined in case the developer's machine has a Windows SDK older than -/// version 10.0.22000.0. -/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute -#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE -#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 -#endif - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -/// Registry key for app theme preference. -/// -/// A value of 0 indicates apps should use dark mode. A non-zero or missing -/// value indicates apps should use light mode. -constexpr const wchar_t kGetPreferredBrightnessRegKey[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; -constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - } - FreeLibrary(user32_module); -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::Create(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - UpdateTheme(window); - - return OnCreate(); -} - -bool Win32Window::Show() { - return ShowWindow(window_handle_, SW_SHOWNORMAL); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - - case WM_DWMCOLORIZATIONCOLORCHANGED: - UpdateTheme(hwnd); - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} - -void Win32Window::UpdateTheme(HWND const window) { - DWORD light_mode; - DWORD light_mode_size = sizeof(light_mode); - LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, - kGetPreferredBrightnessRegValue, - RRF_RT_REG_DWORD, nullptr, &light_mode, - &light_mode_size); - - if (result == ERROR_SUCCESS) { - BOOL enable_dark_mode = light_mode == 0; - DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, - &enable_dark_mode, sizeof(enable_dark_mode)); - } -} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h deleted file mode 100644 index c86632d..0000000 --- a/windows/runner/win32_window.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates a win32 window with |title| that is positioned and sized using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size this function will scale the inputted width and height as - // as appropriate for the default monitor. The window is invisible until - // |Show| is called. Returns true if the window was created successfully. - bool Create(const std::wstring& title, const Point& origin, const Size& size); - - // Show the current window. Returns true if the window was successfully shown. - bool Show(); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - // Update the window frame's theme to match the system theme. - static void UpdateTheme(HWND const window); - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/windows/rust.cmake b/windows/rust.cmake deleted file mode 100644 index a7c5321..0000000 --- a/windows/rust.cmake +++ /dev/null @@ -1,21 +0,0 @@ -# We include Corrosion inline here, but ideally in a project with -# many dependencies we would need to install Corrosion on the system. -# See instructions on https://github.com/AndrewGaspar/corrosion#cmake-install -# Once done, uncomment this line: -# find_package(Corrosion REQUIRED) - -include(FetchContent) - -FetchContent_Declare( - Corrosion - GIT_REPOSITORY https://github.com/AndrewGaspar/corrosion.git - GIT_TAG origin/master # Optionally specify a version tag or branch here -) - -FetchContent_MakeAvailable(Corrosion) - -corrosion_import_crate(MANIFEST_PATH ../native/Cargo.toml IMPORTED_CRATES imported_crates) -target_link_libraries(${BINARY_NAME} PRIVATE ${imported_crates}) -foreach(imported_crate ${imported_crates}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) -endforeach() From 40052c77e4e0e860c2ed16f4fbbf0c60384da2ff Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 00:08:54 +0100 Subject: [PATCH 026/150] Small adjustment in justfile --- justfile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/justfile b/justfile index 77f38c2..9faa856 100644 --- a/justfile +++ b/justfile @@ -1,16 +1,15 @@ -default: gen lint +default: gen fmt gen: flutter pub get flutter_rust_bridge_codegen \ --rust-input native/src/api.rs \ --dart-output lib/bridge_generated.dart \ - --c-output ios/Runner/bridge_generated.h \ --dart-decl-output lib/bridge_definitions.dart \ - --wasm - cp ios/Runner/bridge_generated.h macos/Runner/bridge_generated.h + # --wasm + # cp ios/Runner/bridge_generated.h macos/Runner/bridge_generated.h -lint: +fmt: cd native && cargo fmt dart format . From ba4f122c23bd19284576695853bec3769256f5e6 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 00:18:10 +0100 Subject: [PATCH 027/150] AdEditing: Fix scrolling, add check before enabling publishing --- lib/ad_editing.dart | 97 +++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 47 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index e67ef54..f1e250e 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -50,56 +50,59 @@ class _AdEditingWidgetState extends State { appBar: AppBar(title: const Text('Ad editing')), body: Padding( padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - TextFormField( - initialValue: ad.title, - onChanged: (newText) => setState(() => ad.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Ad title', - ), - style: const TextStyle(fontSize: 30), - ), - TextFormField( - initialValue: ad.description, - maxLines: 20, - onChanged: (newText) => setState(() => ad.description = newText), - decoration: const InputDecoration( - icon: Icon(Icons.text_snippet), - labelText: 'Ad description', + child: SingleChildScrollView( + child: Column( + children: [ + TextFormField( + initialValue: ad.title, + onChanged: (newText) => setState(() => ad.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Ad title', + ), + style: const TextStyle(fontSize: 30), ), - ), - TextFormField( - initialValue: ad.priceCent /*?*/ .divide(100).toString(), - onChanged: (newText) => - setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), - decoration: const InputDecoration( - icon: Icon(Icons.euro), - labelText: 'Price', + TextFormField( + initialValue: ad.description, + maxLines: null, + scrollPhysics: const NeverScrollableScrollPhysics(), + onChanged: (newText) => setState(() => ad.description = newText), + decoration: const InputDecoration( + icon: Icon(Icons.text_snippet), + labelText: 'Ad description', + ), ), - style: const TextStyle(fontSize: 20), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row(children: [ - const Icon( - Icons.image, - color: Colors.grey, + TextFormField( + initialValue: ad.priceCent /*?*/ .divide(100).toString(), + onChanged: (newText) => + setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', ), - const SizedBox(width: 16), - ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), - ]), - ), - ElevatedButton( - onPressed: ad.priceCent == null - ? null - : () { - print('Try to publish...'); - api.publishAd(ad: ad); - }, - child: const Text('Publish')) - ], + style: const TextStyle(fontSize: 20), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row(children: [ + const Icon( + Icons.image, + color: Colors.grey, + ), + const SizedBox(width: 16), + ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), + ]), + ), + ElevatedButton( + onPressed: (ad.title.length < 2 || ad.description.length < 15 || ad.priceCent == null) + ? null + : () { + print('Try to publish...'); + api.publishAd(ad: ad); + }, + child: const Text('Publish')) + ], + ), ), ), ); From cbba5a4619c48af68f4b839ddcd47550052b9ce0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 08:52:22 +0100 Subject: [PATCH 028/150] Fix lint --- lib/common.dart | 2 +- lib/isbn_decoding.dart | 12 ++++++------ lib/main.dart | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/common.dart b/lib/common.dart index 8d07977..89d7f29 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -41,7 +41,7 @@ class AsyncSnapshotWidget extends StatelessWidget { case ConnectionState.waiting: return const CircularProgressIndicator(); case ConnectionState.done: - return builder(snap.data!); + return builder(snap.data as T); default: return const Text('???'); } diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 1883989..78ef669 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -24,15 +24,15 @@ class _ISBNDecodingWidgetState extends State { print('initState'); widget.step.imgsPaths.forEach((imgPath) { isbns[imgPath] = Future(() async { - final decoder_process = await Process.run( + final decoderProcess = await Process.run( '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', ['-in=' + imgPath]); - if (decoder_process.exitCode != 0) { - print('stdout is ${decoder_process.stdout}'); - print('stderr is ${decoder_process.stderr}'); - throw Exception('decoder status is ${decoder_process.exitCode}'); + if (decoderProcess.exitCode != 0) { + print('stdout is ${decoderProcess.stdout}'); + print('stderr is ${decoderProcess.stderr}'); + throw Exception('decoder status is ${decoderProcess.exitCode}'); } - final s = decoder_process.stdout as String; + final s = decoderProcess.stdout as String; return s.split(' ').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); }); }); diff --git a/lib/main.dart b/lib/main.dart index a31beec..d9416dc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -111,7 +111,7 @@ class _MyHomePageState extends State { FutureBuilder( future: ad, builder: (context, snap) { - final style = Theme.of(context).textTheme.headline4; + final style = Theme.of(context).textTheme.headlineMedium; if (snap.error != null) { // An error has been encountered, so give an appropriate response and // pass the error details to an unobstructive tooltip. From 1c3e2f88f27b4cde60d5b514f92563ebe168d91c Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 09:09:19 +0100 Subject: [PATCH 029/150] Clean and use book title as ad title if only one book --- lib/ad_editing.dart | 14 +++--- native/src/api.rs | 116 +------------------------------------------- pubspec.yaml | 31 ------------ 3 files changed, 9 insertions(+), 152 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index f1e250e..902f022 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -32,16 +32,18 @@ class _AdEditingWidgetState extends State { @override void initState() { super.initState(); - final bookTitles = widget.step.metadata.entries.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); - final blurbs = widget.step.metadata.entries - .map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!) - .join('\n'); + final metadataFromIsbn = widget.step.metadata.entries; + final bookTitles = metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); + final blurbs = + metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!).join('\n'); var description = bookTitles + '\n\nRésumé:\n' + blurbs + '\n\n' + personal_info.customMessage; - final keywords = widget.step.metadata.entries.map((entry) => entry.value.keywords).join(', '); + final keywords = metadataFromIsbn.map((entry) => entry.value.keywords).join(', '); if (keywords.isNotEmpty) { description += '\n\nMots-clés:\n' + keywords; } - ad = Ad(title: '', description: description, priceCent: 1000, imgsPath: widget.step.imgsPaths); + + final title = metadataFromIsbn.length == 1 ? metadataFromIsbn.first.value.title : ''; + ad = Ad(title: title, description: description, priceCent: 1000, imgsPath: widget.step.imgsPaths); } @override diff --git a/native/src/api.rs b/native/src/api.rs index 6945840..eaf4c6c 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,11 +1,7 @@ -use crate::babelio::Babelio; use crate::common::Provider; use crate::common::{Ad, BookMetaData}; -use crate::google_books::GoogleBooks; use crate::publisher::Publisher; -use crate::{babelio, common, google_books, leboncoin}; -use itertools::Itertools; -use std::process::Command; +use crate::{babelio, google_books, leboncoin}; pub enum ProviderEnum { Babelio, @@ -21,117 +17,7 @@ pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Optio } } -/* -pub fn get_metadata_from_images(imgs_path: Vec) -> Ad { - let isbns: Vec = imgs_path - .clone() - .into_iter() - .map(|picture_path| { - println!("{picture_path}"); - let output = Command::new( - "/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode", - ) - .arg("-in=".to_string() + &picture_path) - .output() - .expect("failed to execute process"); - if !output.status.success() { - println!("stdout is {:?}", std::str::from_utf8(&output.stdout).unwrap()); - println!("stderr is {:?}", std::str::from_utf8(&output.stderr).unwrap()); - panic!("output.status is {}", output.status) - } - let output = std::str::from_utf8(&output.stdout).unwrap(); - println!("output is {:?}", output); - output - .split_ascii_whitespace() - .map(|x| x.to_string()) - .collect_vec() - }) - .flatten() - .unique() - .collect(); - - println!("isbns {:?}", isbns); - - let book_metadata_providers: Vec> = vec![ - Box::new(babelio::Babelio {}), - Box::new(google_books::GoogleBooks {}), - ]; - - let books: Vec> = isbns - .iter() - .map(|isbn| { - for provider in &book_metadata_providers { - let res = provider.get_book_metadata_from_isbn(&isbn); - if let Some(r) = res { - return Some(r); - } - } - None - }) - .collect(); - let books_titles = books.iter().map(book_format_title_and_author).join("\n"); - let blurbs = books - .iter().filter(|b| b.blurb.is_some()) - .map(|b| { - format!( - "{}:\n{}\n", - book_format_title_and_author(b), - b.blurb.as_ref().unwrap() - ) - }) - .join("\n"); - let keywords = books.iter().flat_map(|b| &b.keywords).unique().join(", "); - - let custom_message = leboncoin::personal_info::CUSTOM_MESSAGE; - - let mut ad_description = books_titles; - if !blurbs.is_empty() { - ad_description += &("\n\nRésumé:\n".to_owned() + &blurbs); - } - ad_description += &("\n\n".to_owned() + &custom_message); - if !keywords.is_empty() { - ad_description = ad_description + "\n\nMots-clés:\n" + &keywords; - } - - println!("ad_description: {:#?}", ad_description); - println!("ad_description: {}", ad_description); - - common::Ad { - title: if books.len() == 1 { - books.first().unwrap().title.clone() - } else { - "".to_string() - }, - description: ad_description, - price_cent: 1000, - imgs_path, - } -}*/ - pub fn publish_ad(ad: Ad) -> () { let lbc_publisher = leboncoin::Leboncoin {}; Publisher::publish(&lbc_publisher, ad); } -/* -fn book_format_title_and_author(book: &BookMetaData) -> String { - format!( - "\"{}\" {}", - book.title, - vec_fmt( - book.authors - .iter() - .map(|a| format!("{} {}", a.first_name, a.last_name)) - .collect_vec() - ) - ) -} - -fn vec_fmt(vec: Vec) -> String { - match vec.len() { - 0 => "".to_string(), - 1 => format!("de {}", vec[0]), - 2 => format!("de {} et {}", vec[0], vec[1]), - _ => panic!("More than 2 authors"), - } -} -*/ diff --git a/pubspec.yaml b/pubspec.yaml index 10b8a9e..66d5678 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,20 +1,8 @@ name: flutter_rust_bridge_template description: A new Flutter project. -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 1.0.0+1 environment: @@ -29,11 +17,6 @@ environment: dependencies: flutter: sdk: flutter - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 ffi: ^2.0.1 flutter_rust_bridge: ^1.45.0 meta: ^1.8.0 @@ -44,24 +27,10 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. flutter_lints: ^2.0.0 ffigen: ^7.2.7 -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter packages. flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true # To add assets to your application, add an assets section, like this: From 557e97ef513e972f856c7b03afdd2b054db39c1d Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 09:13:30 +0100 Subject: [PATCH 030/150] Clean --- lib/common.dart | 8 +++++ lib/main.dart | 82 +++++-------------------------------------------- 2 files changed, 15 insertions(+), 75 deletions(-) diff --git a/lib/common.dart b/lib/common.dart index 89d7f29..a1dd726 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -51,3 +51,11 @@ class AsyncSnapshotWidget extends StatelessWidget { extension AuthorsExt on List { String toText() => map((a) => '${a.firstName} ${a.lastName}').join('\n'); } + +extension IntExt on int { + int divide(int other) => this ~/ other; +} + +extension DoubleExt on double { + double multiply(double other) => this * other; +} diff --git a/lib/main.dart b/lib/main.dart index d9416dc..4c4dfe8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,13 +10,6 @@ void main() { runApp(const MyApp()); } -class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - - @override - State createState() => _MyAppState(); -} - sealed class BookyStep {} class ImageSelectionStep implements BookyStep {} @@ -39,6 +32,13 @@ class AdEditingStep implements BookyStep { AdEditingStep({required this.imgsPaths, required this.metadata}); } +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + class _MyAppState extends State { BookyStep step = //ImageSelectionStep(); MetadataCollectingStep(imgsPaths: [ @@ -74,71 +74,3 @@ class _MyAppState extends State { }); } } - -class MyHomePage extends StatefulWidget { - const MyHomePage(this.imgsPaths); - final List imgsPaths; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - late Future ad; - @override - void initState() { - super.initState(); - // ad = api.getMetadataFromImages(imgsPath: widget.imgsPaths); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Create an automatic online book ad from picture'), - ), - body: Center( - child: Column( - children: [ - // To render the results of a Future, a FutureBuilder is used which - // turns a Future into an AsyncSnapshot, which can be used to - // extract the error state, the loading state and the data if - // available. - // - // Here, the generic type that the FutureBuilder manages is - // explicitly named, because if omitted the snapshot will have the - // type of AsyncSnapshot. - FutureBuilder( - future: ad, - builder: (context, snap) { - final style = Theme.of(context).textTheme.headlineMedium; - if (snap.error != null) { - // An error has been encountered, so give an appropriate response and - // pass the error details to an unobstructive tooltip. - debugPrint(snap.error.toString()); - return Tooltip( - message: snap.error.toString(), - child: Text('Error during image decoding', style: style), - ); - } - - final ad = snap.data; - if (ad == null) return const Text('Extracting info from images'); - - return const Text('extract finish'); - }, - ) - ], - ), - ), - ); - } -} - -extension IntExt on int { - int divide(int other) => this ~/ other; -} - -extension DoubleExt on double { - double multiply(double other) => this * other; -} From e8d6ea16c53adc6321ee4c5712390fc940cc1db5 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 21:23:01 +0100 Subject: [PATCH 031/150] Better description if one book --- lib/ad_editing.dart | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 902f022..7bb6c2e 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -33,19 +33,30 @@ class _AdEditingWidgetState extends State { void initState() { super.initState(); final metadataFromIsbn = widget.step.metadata.entries; - final bookTitles = metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); - final blurbs = - metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!).join('\n'); - var description = bookTitles + '\n\nRésumé:\n' + blurbs + '\n\n' + personal_info.customMessage; - final keywords = metadataFromIsbn.map((entry) => entry.value.keywords).join(', '); + + final title = metadataFromIsbn.length == 1 ? metadataFromIsbn.first.value.title : ''; + var description = _getDescription(metadataFromIsbn); + + final keywords = metadataFromIsbn.map((entry) => entry.value.keywords).expand((kw) => kw).toSet().join(', '); if (keywords.isNotEmpty) { description += '\n\nMots-clés:\n' + keywords; } - final title = metadataFromIsbn.length == 1 ? metadataFromIsbn.first.value.title : ''; ad = Ad(title: title, description: description, priceCent: 1000, imgsPath: widget.step.imgsPaths); } + String _getDescription(Iterable> metadataFromIsbn) { + final blurbs = + metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!).join('\n'); + if (metadataFromIsbn.length == 1) { + return 'Résumé:\n' + metadataFromIsbn.single.value.blurb!; + } else { + final bookTitles = metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); + final description = bookTitles + '\n\nRésumés:\n' + blurbs + '\n\n' + personal_info.customMessage; + return description; + } + } + @override Widget build(BuildContext context) { return Scaffold( From b73299197d76d72b3c02e6ae52a2662f76503c96 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 21:52:33 +0100 Subject: [PATCH 032/150] publish_ad return bool --- lib/ad_editing.dart | 8 ++++++-- lib/bridge_definitions.dart | 2 +- lib/bridge_generated.dart | 12 ++++++------ native/src/api.rs | 4 ++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 7bb6c2e..0b6e652 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -109,9 +109,13 @@ class _AdEditingWidgetState extends State { ElevatedButton( onPressed: (ad.title.length < 2 || ad.description.length < 15 || ad.priceCent == null) ? null - : () { + : () async { print('Try to publish...'); - api.publishAd(ad: ad); + final res = await api.publishAd(ad: ad); + + if (!context.mounted) return; + ScaffoldMessenger.of(context) + .showSnackBar(SnackBar(content: Text(res ? 'Success' : 'Failure'))); }, child: const Text('Publish')) ], diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 6287545..a80a140 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -13,7 +13,7 @@ abstract class Native { FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; - Future publishAd({required Ad ad, dynamic hint}); + Future publishAd({required Ad ad, dynamic hint}); FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta; } diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 2e6a4cb..9d28f06 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -44,11 +44,11 @@ class NativeImpl implements Native { argNames: ["provider", "isbn"], ); - Future publishAd({required Ad ad, dynamic hint}) { + Future publishAd({required Ad ad, dynamic hint}) { var arg0 = _platform.api2wire_box_autoadd_ad(ad); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => _platform.inner.wire_publish_ad(port_, arg0), - parseSuccessData: _wire2api_unit, + parseSuccessData: _wire2api_bool, constMeta: kPublishAdConstMeta, argValues: [ad], hint: hint, @@ -96,6 +96,10 @@ class NativeImpl implements Native { ); } + bool _wire2api_bool(dynamic raw) { + return raw as bool; + } + BookMetaData _wire2api_box_autoadd_book_meta_data(dynamic raw) { return _wire2api_book_meta_data(raw); } @@ -119,10 +123,6 @@ class NativeImpl implements Native { Uint8List _wire2api_uint_8_list(dynamic raw) { return raw as Uint8List; } - - void _wire2api_unit(dynamic raw) { - return; - } } // Section: api2wire diff --git a/native/src/api.rs b/native/src/api.rs index eaf4c6c..76a7c68 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -17,7 +17,7 @@ pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Optio } } -pub fn publish_ad(ad: Ad) -> () { +pub fn publish_ad(ad: Ad) -> bool { let lbc_publisher = leboncoin::Leboncoin {}; - Publisher::publish(&lbc_publisher, ad); + Publisher::publish(&lbc_publisher, ad) } From 8e351c5611f62f83d66dad5cc330def98cfb25cd Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 17 Mar 2023 22:44:45 +0100 Subject: [PATCH 033/150] Better handle BookMD = null, add begining of test when desc is better is GBook first request than in the second one --- lib/main.dart | 6 +- lib/metadata_collecting.dart | 33 ++++++---- native/src/google_books/parser.rs | 35 +++++++++- .../test/9782266162777/isbn_response.html | 64 +++++++++++++++++++ .../9782266162777/self_link_response.html | 64 +++++++++++++++++++ .../{ => 9782744170812}/isbn_response.html | 0 .../self_link_response.html | 0 7 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 native/src/google_books/test/9782266162777/isbn_response.html create mode 100644 native/src/google_books/test/9782266162777/self_link_response.html rename native/src/google_books/test/{ => 9782744170812}/isbn_response.html (100%) rename native/src/google_books/test/{ => 9782744170812}/self_link_response.html (100%) diff --git a/lib/main.dart b/lib/main.dart index 4c4dfe8..ffa9ced 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -40,8 +40,8 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BookyStep step = //ImageSelectionStep(); - MetadataCollectingStep(imgsPaths: [ + BookyStep step = ImageSelectionStep(); + /* MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', @@ -49,7 +49,7 @@ class _MyAppState extends State { ], isbns: { '9782253029854', '9782277223634', - }); + });*/ @override Widget build(BuildContext context) { return MaterialApp( diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 7c4af51..f46487c 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -4,6 +4,8 @@ import 'package:flutter_rust_bridge_template/common.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'main.dart'; +const noneText = Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + class MetadataCollectingWidget extends StatefulWidget { const MetadataCollectingWidget({required this.step, required this.onSubmit}); final MetadataCollectingStep step; @@ -14,7 +16,7 @@ class MetadataCollectingWidget extends StatefulWidget { } class Metadatas { - final Map> mdFromProviders; + final Map> mdFromProviders; BookMetaData manual; Metadatas({required this.mdFromProviders, required this.manual}); } @@ -31,9 +33,13 @@ class _MetadataCollectingWidgetState extends State { () => Metadatas( manual: BookMetaData(title: '', authors: [], keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { - final md = api.getMetadataFromProvider(provider: provider, isbn: isbn).then((value) => value!); + final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); //.then((value) => value!); if (provider == ProviderEnum.Babelio) { - md.then((value) => metadata[isbn]!.manual = value); + md.then((value) { + if (value != null) { + metadata[isbn]!.manual = value; + } + }); } return MapEntry(provider, md); })))); @@ -70,21 +76,22 @@ class _MetadataCollectingWidgetState extends State { FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data.title, + initialValue: data?.title, onChanged: (newText) => setState(() => manual.title = newText), decoration: const InputDecoration( icon: Icon(Icons.title), labelText: 'Book title', ), )), - ...metadata[isbn]!.mdFromProviders.entries.map( - (e) => FutureWidget(future: e.value, builder: (data) => SelectableText(data.title))), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) => data == null ? noneText : SelectableText(data.title))), ]), TableRow(children: [ FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data.authors.toText(), + initialValue: data?.authors.toText(), onChanged: (newText) => setState(() => manual.authors = newText .split('\n') .map((line) => Author(firstName: '', lastName: line)) @@ -98,9 +105,9 @@ class _MetadataCollectingWidgetState extends State { ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( future: e.value, builder: (data) { - final authors = data.authors; - if (authors.isEmpty) { - return const Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + final authors = data?.authors; + if (authors == null || authors.isEmpty) { + return noneText; } return SelectableText(authors.toText()); })), @@ -109,7 +116,7 @@ class _MetadataCollectingWidgetState extends State { FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data.blurb, + initialValue: data?.blurb, onChanged: (newText) => setState(() => manual.blurb = newText), decoration: const InputDecoration( icon: Icon(Icons.description), @@ -119,9 +126,9 @@ class _MetadataCollectingWidgetState extends State { ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( future: e.value, builder: (data) { - final blurb = data.blurb; + final blurb = data?.blurb; if (blurb == null) { - return const Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + return noneText; } return SelectableText(blurb); })), diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index ea041b8..2de3c74 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -34,7 +34,9 @@ mod tests { #[test] fn extract_self_link_from_file() { - let html = std::fs::read_to_string("src/google_books/test/isbn_response.html").unwrap(); + let html = + std::fs::read_to_string("src/google_books/test/9782744170812/isbn_response.html") + .unwrap(); let self_link = extract_self_link_from_isbn_response(&html); assert_eq!( self_link, @@ -45,7 +47,8 @@ mod tests { #[test] fn extract_metadata_from_file() { let html = - std::fs::read_to_string("src/google_books/test/self_link_response.html").unwrap(); + std::fs::read_to_string("src/google_books/test/9782744170812/self_link_response.html") + .unwrap(); let metadata = extract_metadata_from_self_link_response(&html); assert_eq!(metadata, BookMetaData{ title: "La cité de Dieu".to_string(), @@ -54,6 +57,34 @@ mod tests { ..Default::default() }); } + + #[test] + fn extract_self_link_from_file_2() { + let html = + std::fs::read_to_string("src/google_books/test/9782266162777/isbn_response.html") + .unwrap(); + let self_link = extract_self_link_from_isbn_response(&html); + assert_eq!( + self_link, + Some("https://www.googleapis.com/books/v1/volumes/HY_FNwAACAAJ".to_string()) + ) + } + + #[test] + fn extract_metadata_from_file_2() { + let html = + std::fs::read_to_string("src/google_books/test/9782266162777/self_link_response.html") + .unwrap(); + let metadata = extract_metadata_from_self_link_response(&html); + assert_eq!(metadata, BookMetaData{ + title: "L'essence du Tao".to_string(), + authors:vec![common::Author{first_name: "".to_string(), last_name: "Pamela J. Ball".to_string()}], + blurb: None, + // Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux. + //Some("Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang et devient, quelques années plus tard, le maître de la cité.".to_string()), + ..Default::default() + }); + } } mod structs { diff --git a/native/src/google_books/test/9782266162777/isbn_response.html b/native/src/google_books/test/9782266162777/isbn_response.html new file mode 100644 index 0000000..56d1645 --- /dev/null +++ b/native/src/google_books/test/9782266162777/isbn_response.html @@ -0,0 +1,64 @@ +{ + "kind": "books#volumes", + "totalItems": 1, + "items": [ + { + "kind": "books#volume", + "id": "HY_FNwAACAAJ", + "etag": "PyqPNtbM744", + "selfLink": "https://www.googleapis.com/books/v1/volumes/HY_FNwAACAAJ", + "volumeInfo": { + "title": "L'essence du Tao", + "authors": [ + "Pamela Ball" + ], + "publishedDate": "2007-11-02", + "description": "Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.", + "industryIdentifiers": [ + { + "type": "ISBN_10", + "identifier": "2266162772" + }, + { + "type": "ISBN_13", + "identifier": "9782266162777" + } + ], + "readingModes": { + "text": false, + "image": false + }, + "pageCount": 283, + "printType": "BOOK", + "maturityRating": "NOT_MATURE", + "allowAnonLogging": false, + "contentVersion": "preview-1.0.0", + "language": "fr", + "previewLink": "http://books.google.fr/books?id=HY_FNwAACAAJ&dq=isbn:9782266162777&hl=&cd=1&source=gbs_api", + "infoLink": "http://books.google.fr/books?id=HY_FNwAACAAJ&dq=isbn:9782266162777&hl=&source=gbs_api", + "canonicalVolumeLink": "https://books.google.com/books/about/L_essence_du_Tao.html?hl=&id=HY_FNwAACAAJ" + }, + "saleInfo": { + "country": "FR", + "saleability": "NOT_FOR_SALE", + "isEbook": false + }, + "accessInfo": { + "country": "FR", + "viewability": "NO_PAGES", + "embeddable": false, + "publicDomain": false, + "textToSpeechPermission": "ALLOWED", + "epub": { + "isAvailable": false + }, + "pdf": { + "isAvailable": false + }, + "webReaderLink": "http://play.google.com/books/reader?id=HY_FNwAACAAJ&hl=&source=gbs_api", + "accessViewStatus": "NONE", + "quoteSharingAllowed": false + } + } + ] +} \ No newline at end of file diff --git a/native/src/google_books/test/9782266162777/self_link_response.html b/native/src/google_books/test/9782266162777/self_link_response.html new file mode 100644 index 0000000..be5e2c8 --- /dev/null +++ b/native/src/google_books/test/9782266162777/self_link_response.html @@ -0,0 +1,64 @@ +{ + "kind": "books#volume", + "id": "HY_FNwAACAAJ", + "etag": "xbuWWeqB6KI", + "selfLink": "https://www.googleapis.com/books/v1/volumes/HY_FNwAACAAJ", + "volumeInfo": { + "title": "L'essence du Tao", + "authors": [ + "Pamela J. Ball" + ], + "publisher": "Pocket", + "publishedDate": "2007", + "industryIdentifiers": [ + { + "type": "ISBN_10", + "identifier": "2266162772" + }, + { + "type": "ISBN_13", + "identifier": "9782266162777" + } + ], + "readingModes": { + "text": false, + "image": false + }, + "pageCount": 283, + "printedPageCount": 283, + "dimensions": { + "height": "18.00 cm", + "width": "11.00 cm", + "thickness": "1.00 cm" + }, + "printType": "BOOK", + "maturityRating": "NOT_MATURE", + "allowAnonLogging": false, + "contentVersion": "preview-1.0.0", + "language": "fr", + "previewLink": "http://books.google.fr/books?id=HY_FNwAACAAJ&hl=&source=gbs_api", + "infoLink": "https://play.google.com/store/books/details?id=HY_FNwAACAAJ&source=gbs_api", + "canonicalVolumeLink": "https://play.google.com/store/books/details?id=HY_FNwAACAAJ" + }, + "saleInfo": { + "country": "FR", + "saleability": "NOT_FOR_SALE", + "isEbook": false + }, + "accessInfo": { + "country": "FR", + "viewability": "NO_PAGES", + "embeddable": false, + "publicDomain": false, + "textToSpeechPermission": "ALLOWED", + "epub": { + "isAvailable": false + }, + "pdf": { + "isAvailable": false + }, + "webReaderLink": "http://play.google.com/books/reader?id=HY_FNwAACAAJ&hl=&source=gbs_api", + "accessViewStatus": "NONE", + "quoteSharingAllowed": false + } +} \ No newline at end of file diff --git a/native/src/google_books/test/isbn_response.html b/native/src/google_books/test/9782744170812/isbn_response.html similarity index 100% rename from native/src/google_books/test/isbn_response.html rename to native/src/google_books/test/9782744170812/isbn_response.html diff --git a/native/src/google_books/test/self_link_response.html b/native/src/google_books/test/9782744170812/self_link_response.html similarity index 100% rename from native/src/google_books/test/self_link_response.html rename to native/src/google_books/test/9782744170812/self_link_response.html From cbe554d0014b950ea584bce0f89cdf178e6cc698 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 19 Mar 2023 17:59:35 +0100 Subject: [PATCH 034/150] Convert title to Option, Add GB.extract_metadata_from_isbn_response and add test --- lib/metadata_collecting.dart | 2 +- native/src/babelio.rs | 4 +- native/src/babelio/parser.rs | 4 +- native/src/common.rs | 4 +- native/src/google_books.rs | 73 ++++++++++++++++++++++++++++++- native/src/google_books/parser.rs | 53 +++++++++++++++++----- 6 files changed, 121 insertions(+), 19 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index f46487c..54e711e 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -33,7 +33,7 @@ class _MetadataCollectingWidgetState extends State { () => Metadatas( manual: BookMetaData(title: '', authors: [], keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { - final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); //.then((value) => value!); + final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); if (provider == ProviderEnum.Babelio) { md.then((value) { if (value != null) { diff --git a/native/src/babelio.rs b/native/src/babelio.rs index 3e20eb1..8c800e6 100644 --- a/native/src/babelio.rs +++ b/native/src/babelio.rs @@ -39,7 +39,7 @@ mod tests { let isbn = "9782266071529"; let md = Babelio {}.get_book_metadata_from_isbn(isbn); assert_eq!(md, Some(BookMetaData { - title: "Le nom de la bête".to_string(), + title: Some("Le nom de la bête".to_string()), authors: vec![Author{first_name:"Daniel".to_string(), last_name: "Easterman".to_string()}], blurb: Some("Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède\n".to_string()), keywords: @@ -75,7 +75,7 @@ mod tests { let isbn = "9782070541898"; let md = Babelio {}.get_book_metadata_from_isbn(isbn); assert_eq!(md, Some(BookMetaData { - title: "À la croisée des mondes, tome 2 : La tour des anges".to_string(), + title: Some("À la croisée des mondes, tome 2 : La tour des anges".to_string()), authors: vec![Author{first_name:"Philip".to_string(), last_name: "Pullman".to_string()}], blurb: Some(r#"Le jeune Will, à la recherche de son père disparu depuis de longues années, est persuadé d’avoir tué un homme. Dans sa fuite, il franchit une brèche presque invisible qui lui permet de passer dans un monde parallèle. Là, à Cittàgazze, la ville au-delà de l’Aurore, il rencontre Lyra, l’héroïne des "Royaumes du Nord". Elle aussi cherche à rejoindre son père, elle aussi est investie d’une mission dont elle ne connaît pas encore toute l’importance. diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index d366eac..ee1d6f5 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -142,7 +142,7 @@ pub fn extract_title_author_keywords(html: &str) -> Option { }) .collect(); Some(BookMetaData { - title, + title: Some(title), authors, keywords, ..Default::default() @@ -170,7 +170,7 @@ mod tests { assert_eq!( title_author_keywords, Some(BookMetaData { - title: "Le nom de la bête".to_string(), + title: Some("Le nom de la bête".to_string()), authors: vec![crate::common::Author { first_name: "Daniel".to_string(), last_name: "Easterman".to_string() diff --git a/native/src/common.rs b/native/src/common.rs index 4723920..8019ee9 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -1,6 +1,6 @@ #[derive(Default, Debug, PartialEq)] pub struct BookMetaData { - pub title: String, + pub title: Option, pub authors: Vec, // A book blurb is a short promotional description. // A synopsis summarizes the twists, turns, and conclusion of the story. @@ -8,7 +8,7 @@ pub struct BookMetaData { pub keywords: Vec, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct Author { pub first_name: String, pub last_name: String, diff --git a/native/src/google_books.rs b/native/src/google_books.rs index fddea62..a5e5fdb 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -1,17 +1,88 @@ use crate::common; mod parser; mod request; +use itertools::Itertools; pub struct GoogleBooks; +fn merge(first: Option, other: Option, resolver: F) -> Option +where + F: FnOnce(T, T) -> T, +{ + if let None = first { + return other; + } + if let None = other { + return first; + } + Some(resolver(first.unwrap(), other.unwrap())) +} + +fn longest_string_merger(first: Option, other: Option) -> Option { + merge( + first, + other, + |s1, s2| if s1.len() > s2.len() { s1 } else { s2 }, + ) +} + +fn merge_vec( + v1: Vec, + v2: Vec, +) -> Vec { + v1.iter() + .chain(&v2) + .unique() + .map(|f| (*f).clone()) + .collect_vec() +} + +fn merge_bmd(bmd1: common::BookMetaData, bmd2: common::BookMetaData) -> common::BookMetaData { + common::BookMetaData { + title: longest_string_merger(bmd1.title, bmd2.title), + // Some authors are not display the same way in the first and second request. Sometimes GoogleBooks display the middle name, sometimes not + // So a basic merge would result in diplicate authors + // authors: merge_vec(bmd1.authors, bmd2.authors), + authors: bmd1.authors, + blurb: longest_string_merger(bmd1.blurb, bmd2.blurb), + keywords: merge_vec(bmd1.keywords, bmd2.keywords), + } +} + impl common::Provider for GoogleBooks { fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { // TODO: For some books (eg 9782703305033), the description is better on the first page than in the second // The number of authors can be different too ! let client = reqwest::blocking::Client::builder().build().unwrap(); let isbn_search_response = request::search_by_isbn(&client, isbn); + + let metadata_from_isbn_search = + parser::extract_metadata_from_isbn_response(&isbn_search_response); + let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response)?; let book_page = request::get_volume(&client, &self_link); - Some(parser::extract_metadata_from_self_link_response(&book_page)) + + let metadata_from_self_link_response = + parser::extract_metadata_from_self_link_response(&book_page); + + Some(merge_bmd( + metadata_from_isbn_search, + metadata_from_self_link_response, + )) + } +} + +#[cfg(test)] +mod tests { + use crate::common::BookMetaData; + use crate::common::Provider; + + use super::*; + + #[test] + fn get_book_metadata_from_isbn_9782266162777() { + let g = GoogleBooks{}; + let md = g.get_book_metadata_from_isbn("9782266162777"); + assert_eq!(md, Some(BookMetaData { title: Some("L'essence du Tao".to_owned()), authors: vec![common::Author{first_name: "".to_owned(), last_name: "Pamela Ball".to_owned()}], blurb: Some("Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.".to_string()), keywords: vec![] })) } } diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index 2de3c74..8458ea1 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -1,17 +1,45 @@ use itertools::Itertools; -use crate::common; +use crate::common::{self, BookMetaData}; pub fn extract_self_link_from_isbn_response(html: &str) -> Option { let s: structs::Root = serde_json::from_str(html).unwrap(); s.items.map(|items| items[0].self_link.to_string()) } +pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaData { + let s: structs::Root = serde_json::from_str(html).unwrap(); + let a = s.items.map(|items| { + let first_book = &items[0].volume_info; + + let authors = first_book + .authors + .iter() + .map(|s| common::Author { + first_name: "".to_string(), + last_name: s.to_string(), + }) + .collect_vec(); + let blurb = items[0] + .volume_info + .description + .clone() + .map(|d| d.to_string()); + BookMetaData { + authors, + blurb, + ..Default::default() + } + }); + a.unwrap_or(BookMetaData { + ..Default::default() + }) +} pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaData { let s: structs::Item = serde_json::from_str(html).unwrap(); let first_book = &s.volume_info; common::BookMetaData { - title: first_book.title.to_string(), + title: Some(first_book.title.to_string()), authors: first_book .authors .iter() @@ -51,7 +79,7 @@ mod tests { .unwrap(); let metadata = extract_metadata_from_self_link_response(&html); assert_eq!(metadata, BookMetaData{ - title: "La cité de Dieu".to_string(), + title: Some("La cité de Dieu".to_string()), authors:vec![common::Author{first_name: "".to_string(), last_name: "Paulo Lins".to_string()}], blurb: Some("Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang et devient, quelques années plus tard, le maître de la cité.".to_string()), ..Default::default() @@ -76,14 +104,17 @@ mod tests { std::fs::read_to_string("src/google_books/test/9782266162777/self_link_response.html") .unwrap(); let metadata = extract_metadata_from_self_link_response(&html); - assert_eq!(metadata, BookMetaData{ - title: "L'essence du Tao".to_string(), - authors:vec![common::Author{first_name: "".to_string(), last_name: "Pamela J. Ball".to_string()}], - blurb: None, - // Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux. - //Some("Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang et devient, quelques années plus tard, le maître de la cité.".to_string()), - ..Default::default() - }); + assert_eq!( + metadata, + BookMetaData { + title: Some("L'essence du Tao".to_string()), + authors: vec![common::Author { + first_name: "".to_string(), + last_name: "Pamela J. Ball".to_string() + }], + ..Default::default() + } + ); } } From 26421ad571a4a773c040cfb6833bad24d9b450bb Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 19 Mar 2023 19:48:40 +0100 Subject: [PATCH 035/150] Rust: GB: remove all field not useful --- native/src/google_books/parser.rs | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index 8458ea1..3e8ee75 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -132,14 +132,14 @@ mod structs { #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Item<'a> { - pub kind: &'a str, + // pub kind: &'a str, pub id: &'a str, - pub etag: &'a str, + // pub etag: &'a str, pub self_link: &'a str, pub volume_info: VolumeInfo<'a>, - pub sale_info: SaleInfo<'a>, - pub access_info: AccessInfo<'a>, - pub search_info: Option>, + // pub sale_info: SaleInfo<'a>, + // pub access_info: AccessInfo<'a>, + // pub search_info: Option>, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] @@ -148,27 +148,27 @@ mod structs { pub title: &'a str, pub subtitle: Option<&'a str>, pub authors: Vec<&'a str>, - pub publisher: Option<&'a str>, - pub published_date: &'a str, + // pub publisher: Option<&'a str>, + // pub published_date: &'a str, // Should be an owned String in case the description contain escape characters like (\") // TODO: change all &str to String pub description: Option, - pub industry_identifiers: Vec>, - pub reading_modes: ReadingModes, - pub page_count: i64, - pub print_type: &'a str, - pub categories: Option>, - pub maturity_rating: &'a str, - pub image_links: Option>, - pub language: &'a str, - pub preview_link: &'a str, - pub info_link: &'a str, - pub canonical_volume_link: &'a str, - } - - #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] + // pub industry_identifiers: Vec>, + // pub reading_modes: ReadingModes, + // pub page_count: i64, + // pub print_type: &'a str, + // pub categories: Option>, + // pub maturity_rating: &'a str, + // pub image_links: Option>, + // pub language: &'a str, + // pub preview_link: &'a str, + // pub info_link: &'a str, + // pub canonical_volume_link: &'a str, + } + + /* #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] - pub struct IndustryIdentifier<'a> { + struct IndustryIdentifier<'a> { #[serde(rename = "type")] pub type_field: &'a str, pub identifier: &'a str, @@ -234,5 +234,5 @@ mod structs { #[serde(rename_all = "camelCase")] pub struct SearchInfo<'a> { pub text_snippet: &'a str, - } + }*/ } From 3b6e135055c5b9dd76d3fc289751250a4c81986b Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 22 Mar 2023 22:06:19 +0100 Subject: [PATCH 036/150] Babelio handle authors with only last name --- native/src/babelio/parser.rs | 115 +- ...253143321_le_livre_tibetain_des_morts.html | 1807 +++++++++++++++++ 2 files changed, 1889 insertions(+), 33 deletions(-) create mode 100644 native/src/babelio/test/9782253143321_le_livre_tibetain_des_morts.html diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index ee1d6f5..0a94177 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -1,5 +1,5 @@ use crate::common::{html_select, BookMetaData}; -use itertools::{Itertools}; +use itertools::Itertools; #[derive(PartialEq, Debug)] pub enum BlurbRes { @@ -60,6 +60,42 @@ pub fn extract_blurb(html: &str) -> Option { } } +fn extract_author(author_scope: scraper::ElementRef) -> crate::common::Author { + let author_span = author_scope + .first_child() + .expect("author_scope shoud have a first child ") + .first_child() + .expect("author scope > a shoud have a first child "); + let mut children = author_span.children(); + let first_element = children + .next() + .expect("author scope > a > span shoud have a first child"); + let first_name; + let last_name_element; + if let Some(text) = first_element.value().as_text() { + first_name = text.trim().to_string(); + last_name_element = children + .next() + .expect("author scope > a > span shoud have a second child which is the last name"); + } else { + first_name = "".to_string(); + last_name_element = first_element; + } + + let last_name = last_name_element + .first_child() + .unwrap() + .value() + .as_text() + .expect("should be a text") + .trim() + .to_string(); + crate::common::Author { + first_name, + last_name, + } +} + pub fn extract_title_author_keywords(html: &str) -> Option { let doc = scraper::Html::parse_document(html); @@ -91,38 +127,7 @@ pub fn extract_title_author_keywords(html: &str) -> Option { html_select("[itemprop=\"author\"][itemscope][itemtype=\"https://schema.org/Person\"]"); let r = book_scope.select(&binding); - let authors = r - .map(|author_scope| { - let author_span = author_scope - .first_child() - .expect("author_scope shoud have a first child ") - .first_child() - .expect("author scope > a shoud have a first child "); - let first_name = author_span - .first_child() - .expect("author scope > a > span shoud have a first child which is first name") - .value() - .as_text() - .expect("should be a text") - .trim() - .to_string(); - let last_name = author_span - .children() - .nth(1) - .expect("author scope > a > span shoud have a second child which is the last name") - .first_child() - .unwrap() - .value() - .as_text() - .expect("should be a text") - .trim() - .to_string(); - crate::common::Author { - first_name, - last_name, - } - }) - .collect_vec(); + let authors = r.map(extract_author).collect_vec(); let keywords_scope = book_scope .select(&html_select("[class=\"tags\"]")) @@ -203,4 +208,48 @@ mod tests { }) ); } + + #[test] + pub fn extract_title_author_keywords_from_file_9782253143321() { + let html = std::fs::read_to_string( + "src/babelio/test/9782253143321_le_livre_tibetain_des_morts.html", + ) + .unwrap(); + let title_author_keywords = extract_title_author_keywords(&html); + assert_eq!( + title_author_keywords, + Some(BookMetaData { + title: Some("Bardo-Thödol : Le livre tibétain des morts".to_string()), + authors: vec![crate::common::Author { + first_name: "".to_string(), + last_name: "Padmasambhava".to_string() + }], + blurb: None, + keywords: [ + "document", + "classique", + "histoire", + "mystique", + "zen", + "mort", + "croyances", + "pensées philosophiques", + "libération", + "réincarnation", + "Médiumnité", + "religion", + "spiritualité", + "Bouddhistes", + "bouddhisme tibétain", + "vie après la mort", + "bouddhisme", + "voyage initiatique", + "ésotérisme", + "philosophie" + ] + .map(|s| s.to_string()) + .to_vec(), + }) + ); + } } diff --git a/native/src/babelio/test/9782253143321_le_livre_tibetain_des_morts.html b/native/src/babelio/test/9782253143321_le_livre_tibetain_des_morts.html new file mode 100644 index 0000000..56c99db --- /dev/null +++ b/native/src/babelio/test/9782253143321_le_livre_tibetain_des_morts.html @@ -0,0 +1,1807 @@ + + + + + + + + + + + + + + + + Bardo-Thödol : Le livre tibétain des morts - Babelio + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + +
+
+ +
+ +
+
+
+
+ +
+
+
+ +
+
+ +

Padma Sambhava (Auteur présumé) Karma-glin-pa (Éditeur + scientifique)Robert A. + F. Thurman (Éditeur scientifique)Gilles Poulain (Traducteur)Rose-Anne Huart + (Traducteur) +
+ + EAN : 9782253143321
+ 412 pages
Le Livre de Poche + + (01/11/1997) + +
4.13/5 +   + 39 notes + + + +
+ Résumé :
+
+ Le Bardo Thödol ou Livre tibétain des morts est un texte du bouddhisme tibétain décrivant les états de + conscience et les perceptions se succédant pendant la période qui s’étend de la mort à la renaissance. + L’étude de son vivant ou la récitation du principal chapitre par un lama lors de l’agonie ou après la + mort est censée aider à la libération du cycle des réincarnations, ou du moins à obtenir une meilleure + réincarnation.

+ Like a Star @ heaven Les thöd... >Voir plus + +
+
+ +
+
+ +
+
+ Acheter ce livre sur + +
+ + + + + + + + +
FnacAmazonRakutenCulturaMomox
Toutes les offres à + partir de 0.90€ +
+
+
+
+ étiquettes + + + + + Ajouter des étiquettes +
+ +
+ Critiques, Analyses et Avis (3) + + + + Ajouter une critique +
+
+ + +
+
FredMartineau
+ +
+ 31 mai 2015 +
+ +
+
+ + J'ai trouvé intéressant d'appréhender leur façon d'aborder la mort, de découvrir le Bardo, cette étape + intermédiaire de 49 jours durant laquelle l'âme choisit son chemin vers le prochain cycle. On y + retrouve des concepts s'approchant de ceux de la religion chrétienne, que certains lamas définirent + comme un bouddhisme imparfait. L'origine de la Trinité serait bien antérieure à l'emprunt chrétien, un + exemple supplémentaire de l'absurdité de ceux revendiquant leur construction théologique comme des + vérité irréfragables commencement de toute spiritualité... + + + +
+
+
Commenter +  J’apprécie        +   140
+
+
+
+ + +
+
karmon34
+ +
+ 11 juin 2013 +
+ +
+
+ + Après la mort apparente, le défunt traverse le Chikai Bardo, au cours duquel il ne comprend pas qu'il + est mort et se trouve dans un état proche du sommeil. La récitation de la première partie du livre est + destinée à lui faire prendre conscience de son état, à lui épargner les regrets et à le préparer à la + Libération finale, qui se manifeste comme la vision de la « Claire Lumière primordiale ». Si, mal préparé et apeurée il ne saisit pas cette + occasion d'entrer dans le nirvana, le défunt se trouvera dans le Chonyid Bardo. + + +
+ Lien : http://www.buddhachannel.tv/.. +
+
+
Commenter +  J’apprécie        +   70
+
+
+
+ + +
+
catherinejean
+ +
+ 08 février 2023 +
+ +
+
+ + Un beau texte sacré sur la mort et l'éveil!

+ Le Bardo Thödol, ou Livre des morts, est lu + au défunt à haute voix pendant 49 jours pour l'encourager et le guider dans son cheminement. Au moment + de la mort, nous errons dans des états intermédiaires appelés « Bardo ». Ainsi le Livre des morts + contient notamment la description des transformations de la conscience et des perceptions au cours des + états intermédiaires (« Bardo ») qui se succèdent de la mort à la renaissance.

+ Tout au long de la lecture du Bardo Thodöl, l'emphase est mise sur la compassion. La compassion, + l'amour sincère et désintéressé pour les autres est nécessaire pour atteindre la libération. Selon le + bouddhisme tibétain, si on ne se soucie pas des autres, on ne peut jamais connaître son propre + esprit.

+ En fait, le Livre des morts nous enseigne à nous préparer à la mort. Pour cela, il nous faut prendre + conscience de l'état d'endormissement, « de rêve », dans lequel nous vivons notre vie.

+ Un très beau texte de référence de la spiritualité et de la culture tibétaine. + + +
+ Lien : https://www.catherinejeanaut.. +
+
+
Commenter +  J’apprécie        +   20
+
+
+

+
+ Citations et extraits (6) + Voir plus + + Ajouter une citation +
+
+
+
Danieljean
+ +
29 juillet + 2015
+ +
+
+ + + Les deux premiers mantra sont dénommés chant du vajra. Ils ne sont pas en sanskrit, comme la plupart des + mantra, mais dans une langue d'une autre dimension appelée langue de l'Oddiyana et pafois langue des + dakini. On trouve plusieurs versions du chant du vajra : soit sous deux formes distinctes comme ici, + l'une, masculine, étant liée à Samantabhadra, et l'autre féminine, à Samantabhadri; soit sous une forme + unique qui combine les deux précédentes, comme c'est le cas dans le Tantra de l'Union du soleil et de la + lune, le Longchen Nyinghthik ou dans les écrits et terma de Namkhai Norbu Rinpoche. Chaque groupe de + syllabe du chant du vajra correspond à un aspect de l'enseignement dzogchen et à un point énergétique du + corps du yogi. On trouve dans le Longchen Nyingthik la signification du chant du vajra unifié : +

+ Sans naissance ni cessation,
+ Sans allées, ni venues, il embrasse toutes choses.
+ Grande félicité, suprême doctrine immuable,
+ Semblable au ciel, liberté absolue sans oripeaux,
+ Sans origine ni support,
+ Sans lieu ni prise, grand phénomène
+ Libre depuis l'origine, immensité s'étendant à l'infini,
+ Sans entraves, il n'a pas à être libéré;
+ Immensité de l'espace céleste,
+ Grand phénomène flamboyant, mandala du soleil et de la lune,
+ Il manifeste la présence spontanée :
+ Montagne de diamant, vaste lotus,
+ Lion solaire, chant de la sagesse,
+ Grand son, mélodie sans pareil,
+ Plénitude de qualités jusqu'aux confins de l'espace,
+ Eveil parfait, champ où s'égalisent tous les Eveil parfaits,
+ Vaste Samantabhadra, cime de l'enseignement,
+ Et, dans la matrice spacieuse du ciel de Samantabhadri,
+ Clarté spatiale, Présence spontanée, Grande Perfection de toujours + +
+
+
+
Commenter +  J’apprécie        +   130
+
+
+
+
+
+
Danieljean
+ +
05 octobre + 2017
+ +
+
+ + + Dans cette claire vacuité
+ où les pensées passées se sont évanouies
+ sans trace aucune,
+ Dans cette fraîcheur
+ où les pensées à venir ne sont pas encore :
+ A l’instant où s’établit le mode naturel sans fabrications,
+ Voici cette conscience qui, à ce moment,
+ est en elle-même tout ordinaire,
+ Et dès que vous tournez votre regard nu sur vous-même,
+ Ce regard qui n’a rien à voir débouche sur la clarté,
+ La Présence dans son évidence, nue et vive,
+ C’est une pure vacuité qui n’a été créée d’aucune manière.
+ Un état inaltéré où clarté et vide sont indivisibles,
+ Ni éternel puisque rien n’y existe vraiment
+ Ni néant puisqu’il est clair et vif.
+ Il ne se réduit pas à l’un,
+ étant présent et limpide en toutes choses.
+ Et n’est pas le multiple,
+ car tout y est d’une saveur unique dans l’inséparabilité,
+ Telle est cette Présence intrinsèque
+ et elle n’est rien d’autre. + +
+
+
+
Commenter +  J’apprécie        +   130
+
+
+
+
+
+
Danieljean
+ +
19 juillet + 2015
+ +
+
+ + + Ce mot [de mort qui figure dans le titre courant de ce livre] dévie totalement le sens de l'œuvre qui + réside dans l'idée de libération c'est-à-dire libération des illusions de notre conscience égocentrique + qui oscille perpétuellement entre naissance et mort, être et ne pas être, espoir et doute, sans parvenir + à l'éveil, à la paix du nirvana, cet état stable, loin des illusions du samsara et des états + intermédiaires. + +
+
+
+
Commenter +  J’apprécie        +   150
+
+
+
+
+
+
karmon34
+ +
11 juin + 2013
+ +
+
+ + + Noble fils (un tel), maintenant que ta respiration a presque cessé, voici pour toi le moment de chercher + une voie car la lumière fondamentale qui apparaît lors du premier état intermédiaire va poindre. Ton + Lama t’avait déjà montré cette lumière, la Vérité en Soi (Dharmata) vide et nue, comme l’espace sans + limites et n’ayant pas de centre, lucide ; c’est l’esprit vierge et sans tache. Voici le moment de le + reconnaître. Demeure donc ainsi en elle. Moi aussi je te la ferai découvrir ". + +
+
+
+
Commenter +  J’apprécie        +   90
+
+
+
+
+
+
Danieljean
+ +
01 octobre + 2018
+ +
+
+ + + La vie passe aussi vite que les nuages d'automne ;
+ Parents et amis sont comme les badauds d'un marché ;
+ Le démon de la mort rôde, furtif, comme les ombres du crépuscule ;
+ L'au-delà est [pour nous] comme un poisson transparent en eau trouble ;
+ Le monde, comme le rêve de la nuit passée ;
+ Les plaisirs des sens, comme une fête illusoire ;
+ Et les activités ordinaires aussi futiles
+ Que les ondes se succédant à la surface de l'eau. + +
+
+
+
Commenter +  J’apprécie        +   100
+
+
+
+

+
+
+ Dans la catégorie : + + BouddhismeVoir plus +
>Religion comparée. Autres religions>Religions d'origine hindoue>Bouddhisme (225) + + +
+ autres livres classés : bouddhismeVoir plus
+ + +
+ Notre sélection Non-fiction + Voir plus +
+ +
+
+ +
+
+ Acheter ce livre sur + +
+ + + + + + + + +
FnacAmazonRakutenCulturaMomox
Toutes les offres à + partir de 0.90€ +
+
+ +
+

+
+ + + + +

+ +

+

+ +
+
+ Quiz + Voir plus +
+
+

Jésus qui est-il ?

+

Jésus était-il vraiment Juif ?

+
+
Oui
+
Non
+
Plutôt Zen
+
Catholique
+
+

+
10 questions
+ 1675 lecteurs ont répondu +
+ + Thèmes : + christianisme + , religion + , bibleCréer un quiz sur ce livre +
+
+
+
+
+

+
+ +
+
+ + + + + + \ No newline at end of file From 22fa29402ab5a1c274f4412578f183336f2630f9 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 23 Mar 2023 00:54:09 +0100 Subject: [PATCH 037/150] All test pass without internet. GB with mock folder, Babelio with cache --- native/Cargo.toml | 3 +- ..._slash_v1_slash_volumes_slash_HY_FNwAACAAJ | 64 +++++++++++++++++++ .../google_books/search_by_isbn_9782266162777 | 64 +++++++++++++++++++ native/src/api.rs | 8 ++- native/src/babelio/request.rs | 22 +++---- native/src/cached_client.rs | 43 +++++++++++-- native/src/common.rs | 4 ++ native/src/google_books.rs | 17 +++-- native/src/google_books/request.rs | 26 ++++---- 9 files changed, 213 insertions(+), 38 deletions(-) create mode 100644 native/mock/google_books/get_volume_https:_slash__slash_www.googleapis.com_slash_books_slash_v1_slash_volumes_slash_HY_FNwAACAAJ create mode 100644 native/mock/google_books/search_by_isbn_9782266162777 diff --git a/native/Cargo.toml b/native/Cargo.toml index a013ab0..29de5cb 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -18,4 +18,5 @@ regex = "1.7.1" reqwest = { version = "0.11.14", features = ["blocking", "json", "multipart"] } scraper = "0.14.0" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.91" \ No newline at end of file +serde_json = "1.0.91" +mockito = "1.0.0" diff --git a/native/mock/google_books/get_volume_https:_slash__slash_www.googleapis.com_slash_books_slash_v1_slash_volumes_slash_HY_FNwAACAAJ b/native/mock/google_books/get_volume_https:_slash__slash_www.googleapis.com_slash_books_slash_v1_slash_volumes_slash_HY_FNwAACAAJ new file mode 100644 index 0000000..1d54583 --- /dev/null +++ b/native/mock/google_books/get_volume_https:_slash__slash_www.googleapis.com_slash_books_slash_v1_slash_volumes_slash_HY_FNwAACAAJ @@ -0,0 +1,64 @@ +{ + "kind": "books#volume", + "id": "HY_FNwAACAAJ", + "etag": "4DT81Rt3Ygg", + "selfLink": "https://www.googleapis.com/books/v1/volumes/HY_FNwAACAAJ", + "volumeInfo": { + "title": "L'essence du Tao", + "authors": [ + "Pamela J. Ball" + ], + "publisher": "Pocket", + "publishedDate": "2007", + "industryIdentifiers": [ + { + "type": "ISBN_10", + "identifier": "2266162772" + }, + { + "type": "ISBN_13", + "identifier": "9782266162777" + } + ], + "readingModes": { + "text": false, + "image": false + }, + "pageCount": 283, + "printedPageCount": 283, + "dimensions": { + "height": "18.00 cm", + "width": "11.00 cm", + "thickness": "1.00 cm" + }, + "printType": "BOOK", + "maturityRating": "NOT_MATURE", + "allowAnonLogging": false, + "contentVersion": "preview-1.0.0", + "language": "fr", + "previewLink": "http://books.google.fr/books?id=HY_FNwAACAAJ&hl=&source=gbs_api", + "infoLink": "https://play.google.com/store/books/details?id=HY_FNwAACAAJ&source=gbs_api", + "canonicalVolumeLink": "https://play.google.com/store/books/details?id=HY_FNwAACAAJ" + }, + "saleInfo": { + "country": "FR", + "saleability": "NOT_FOR_SALE", + "isEbook": false + }, + "accessInfo": { + "country": "FR", + "viewability": "NO_PAGES", + "embeddable": false, + "publicDomain": false, + "textToSpeechPermission": "ALLOWED", + "epub": { + "isAvailable": false + }, + "pdf": { + "isAvailable": false + }, + "webReaderLink": "http://play.google.com/books/reader?id=HY_FNwAACAAJ&hl=&source=gbs_api", + "accessViewStatus": "NONE", + "quoteSharingAllowed": false + } +} \ No newline at end of file diff --git a/native/mock/google_books/search_by_isbn_9782266162777 b/native/mock/google_books/search_by_isbn_9782266162777 new file mode 100644 index 0000000..736d47c --- /dev/null +++ b/native/mock/google_books/search_by_isbn_9782266162777 @@ -0,0 +1,64 @@ +{ + "kind": "books#volumes", + "totalItems": 1, + "items": [ + { + "kind": "books#volume", + "id": "HY_FNwAACAAJ", + "etag": "PrJ4VYAFw40", + "selfLink": "https://www.googleapis.com/books/v1/volumes/HY_FNwAACAAJ", + "volumeInfo": { + "title": "L'essence du Tao", + "authors": [ + "Pamela Ball" + ], + "publishedDate": "2007-11-02", + "description": "Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.", + "industryIdentifiers": [ + { + "type": "ISBN_10", + "identifier": "2266162772" + }, + { + "type": "ISBN_13", + "identifier": "9782266162777" + } + ], + "readingModes": { + "text": false, + "image": false + }, + "pageCount": 283, + "printType": "BOOK", + "maturityRating": "NOT_MATURE", + "allowAnonLogging": false, + "contentVersion": "preview-1.0.0", + "language": "fr", + "previewLink": "http://books.google.fr/books?id=HY_FNwAACAAJ&dq=isbn:9782266162777&hl=&cd=1&source=gbs_api", + "infoLink": "http://books.google.fr/books?id=HY_FNwAACAAJ&dq=isbn:9782266162777&hl=&source=gbs_api", + "canonicalVolumeLink": "https://books.google.com/books/about/L_essence_du_Tao.html?hl=&id=HY_FNwAACAAJ" + }, + "saleInfo": { + "country": "FR", + "saleability": "NOT_FOR_SALE", + "isEbook": false + }, + "accessInfo": { + "country": "FR", + "viewability": "NO_PAGES", + "embeddable": false, + "publicDomain": false, + "textToSpeechPermission": "ALLOWED", + "epub": { + "isAvailable": false + }, + "pdf": { + "isAvailable": false + }, + "webReaderLink": "http://play.google.com/books/reader?id=HY_FNwAACAAJ&hl=&source=gbs_api", + "accessViewStatus": "NONE", + "quoteSharingAllowed": false + } + } + ] +} \ No newline at end of file diff --git a/native/src/api.rs b/native/src/api.rs index 76a7c68..1440673 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,3 +1,4 @@ +use crate::cached_client::CachedClient; use crate::common::Provider; use crate::common::{Ad, BookMetaData}; use crate::publisher::Publisher; @@ -11,9 +12,12 @@ pub enum ProviderEnum { pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Option { match provider { ProviderEnum::Babelio => babelio::Babelio {}.get_book_metadata_from_isbn(&isbn), - ProviderEnum::GoogleBooks => { - google_books::GoogleBooks {}.get_book_metadata_from_isbn(&isbn) + ProviderEnum::GoogleBooks => google_books::GoogleBooks { + client: Box::new(CachedClient { + http_client: reqwest::blocking::Client::builder().build().unwrap(), + }), } + .get_book_metadata_from_isbn(&isbn), } } diff --git a/native/src/babelio/request.rs b/native/src/babelio/request.rs index 7895a40..62d7e28 100644 --- a/native/src/babelio/request.rs +++ b/native/src/babelio/request.rs @@ -1,4 +1,4 @@ -use crate::cached_client::CachedClient; +use crate::cached_client::{CachedClient, Client}; use itertools::Itertools; #[derive(serde::Serialize, serde::Deserialize, Debug)] @@ -17,10 +17,10 @@ struct BabelioISBNResponse { url: String, } -pub fn get_book_url(client: &CachedClient, isbn: &str) -> Option { - let raw_search_results = client.get_from_cache( +pub fn get_book_url(client: &dyn Client, isbn: &str) -> Option { + let raw_search_results = client.make_request( format!("babelio/get_book_url_{}.html", isbn).as_str(), - |http_client| { + &|http_client| { http_client .post("https://www.babelio.com/aj_recherche.php") .body(format!("{{\"isMobile\":false,\"term\":\"{}\"}}", isbn)) @@ -36,13 +36,9 @@ pub fn get_book_url(client: &CachedClient, isbn: &str) -> Option { } pub fn get_book_page(client: &CachedClient, url: String) -> String { - client.get_from_cache( - format!( - "babelio/get_book_page_{}.html", - url.replace("/", "_slash_") - ) - .as_str(), - |http_client| { + client.make_request( + format!("babelio/get_book_page_{}.html", url.replace("/", "_slash_")).as_str(), + &|http_client| { let resp = http_client .get(format!("https://www.babelio.com{url}")) .send() @@ -53,9 +49,9 @@ pub fn get_book_page(client: &CachedClient, url: String) -> String { } pub fn get_book_blurb_see_more(client: &CachedClient, id_obj: &str) -> String { - client.get_from_cache( + client.make_request( format!("babelio/get_book_blurb_see_more_{}.html", id_obj).as_str(), - |http_client| { + &|http_client| { let params = std::collections::HashMap::from([("type", "1"), ("id_obj", id_obj)]); let voir_plus_resp = http_client diff --git a/native/src/cached_client.rs b/native/src/cached_client.rs index 366cb18..41a5b92 100644 --- a/native/src/cached_client.rs +++ b/native/src/cached_client.rs @@ -1,23 +1,54 @@ +pub trait Client { + fn make_request( + &self, + cache_file_path: &str, + _make_request: &dyn Fn(&reqwest::blocking::Client) -> String, + ) -> String; +} + +pub struct MockClient { + pub dir: &'static str, +} + +impl Client for MockClient { + fn make_request( + &self, + cache_file_path: &str, + _make_request: &dyn Fn(&reqwest::blocking::Client) -> String, + ) -> String { + let cache_file_path = format!("{}/{}", self.dir, cache_file_path); + let html = std::fs::read_to_string(&cache_file_path); + + match html { + Ok(f) => { + println!("Read request from cache {}", &cache_file_path); + f + } + Err(e) => panic!("Cannot find mock file {}. Error is {}", cache_file_path, e), + } + } +} + pub struct CachedClient { pub http_client: reqwest::blocking::Client, } -impl CachedClient { - pub fn get_from_cache String>( +impl Client for CachedClient { + fn make_request( &self, cache_file_path: &str, - make_request: F, + _make_request: &dyn Fn(&reqwest::blocking::Client) -> String, ) -> String { - let cache_file_path = format!("{}/{}", crate::config::CACHE_PATH , cache_file_path); + let cache_file_path = format!("{}/{}", crate::config::CACHE_PATH, cache_file_path); let html = std::fs::read_to_string(&cache_file_path); match html { Ok(f) => { println!("Read request from cache {}", &cache_file_path); f - }, + } Err(_) => { println!("No file name {} in the cache", &cache_file_path); - let resp = make_request(&self.http_client); + let resp = _make_request(&self.http_client); let write_res = std::fs::write(&cache_file_path, &resp); write_res.expect(format!("Can't write to file {}", cache_file_path).as_str()); resp diff --git a/native/src/common.rs b/native/src/common.rs index 8019ee9..a8bedf5 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -28,3 +28,7 @@ pub struct Ad { pub price_cent: i32, pub imgs_path: Vec, } + +pub fn url_to_path(url: &str) -> String { + url.replace("/", "_slash_") +} diff --git a/native/src/google_books.rs b/native/src/google_books.rs index a5e5fdb..67fd6cd 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -1,9 +1,12 @@ use crate::common; mod parser; mod request; +use crate::cached_client::Client; use itertools::Itertools; -pub struct GoogleBooks; +pub struct GoogleBooks { + pub client: Box, +} fn merge(first: Option, other: Option, resolver: F) -> Option where @@ -53,14 +56,13 @@ impl common::Provider for GoogleBooks { fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { // TODO: For some books (eg 9782703305033), the description is better on the first page than in the second // The number of authors can be different too ! - let client = reqwest::blocking::Client::builder().build().unwrap(); - let isbn_search_response = request::search_by_isbn(&client, isbn); + let isbn_search_response = request::search_by_isbn(&self.client, isbn); let metadata_from_isbn_search = parser::extract_metadata_from_isbn_response(&isbn_search_response); let self_link = parser::extract_self_link_from_isbn_response(&isbn_search_response)?; - let book_page = request::get_volume(&client, &self_link); + let book_page = request::get_volume(&self.client, &self_link); let metadata_from_self_link_response = parser::extract_metadata_from_self_link_response(&book_page); @@ -74,6 +76,7 @@ impl common::Provider for GoogleBooks { #[cfg(test)] mod tests { + use crate::cached_client::MockClient; use crate::common::BookMetaData; use crate::common::Provider; @@ -81,7 +84,11 @@ mod tests { #[test] fn get_book_metadata_from_isbn_9782266162777() { - let g = GoogleBooks{}; + let g = GoogleBooks { + client: Box::new(MockClient { + dir: "mock/google_books", + }), + }; let md = g.get_book_metadata_from_isbn("9782266162777"); assert_eq!(md, Some(BookMetaData { title: Some("L'essence du Tao".to_owned()), authors: vec![common::Author{first_name: "".to_owned(), last_name: "Pamela Ball".to_owned()}], blurb: Some("Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.".to_string()), keywords: vec![] })) } diff --git a/native/src/google_books/request.rs b/native/src/google_books/request.rs index f0df992..ae29895 100644 --- a/native/src/google_books/request.rs +++ b/native/src/google_books/request.rs @@ -1,13 +1,17 @@ -pub fn search_by_isbn(client: &reqwest::blocking::Client, isbn: &str) -> String { - let resp = client - .get(format!( - "https://www.googleapis.com/books/v1/volumes?q=isbn:{isbn}" - )) - .send() - .unwrap(); - resp.text().unwrap() +use crate::cached_client::Client; + +pub fn search_by_isbn(client: &Box, isbn: &str) -> String { + client.make_request(&format!("search_by_isbn_{}", isbn), &|client| { + client + .get(format!( + "https://www.googleapis.com/books/v1/volumes?q=isbn:{isbn}" + )) + .send() + .unwrap() + .text() + .unwrap() + }) } -pub fn get_volume(client: &reqwest::blocking::Client, url: &str) -> String { - let resp = client.get(url).send().unwrap(); - resp.text().unwrap() +pub fn get_volume(client: &Box, url: &str) -> String { + client.make_request(&format!("get_volume_{}", crate::common::url_to_path(url)), &|http_client| http_client.get(url).send().unwrap().text().unwrap()) } From 5750c5fbcb85461ae7b5180553097292f7fe3138 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 23 Mar 2023 22:05:35 +0100 Subject: [PATCH 038/150] Move credential to dart --- lib/ad_editing.dart | 32 ++++++++++++++--- lib/bridge_definitions.dart | 16 +++++++-- lib/bridge_generated.dart | 59 ++++++++++++++++++++++++++----- lib/metadata_collecting.dart | 3 +- native/src/api.rs | 6 ++-- native/src/bridge_generated.io.rs | 50 ++++++++++++++++++++++++-- native/src/bridge_generated.rs | 11 ++++-- native/src/common.rs | 5 +++ native/src/leboncoin.rs | 11 +++--- native/src/leboncoin/request.rs | 54 ++++++---------------------- native/src/publisher.rs | 4 +-- 11 files changed, 175 insertions(+), 76 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 0b6e652..d93c739 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -28,15 +28,18 @@ String _bookFormatTitleAndAuthor(BookMetaData book) { class _AdEditingWidgetState extends State { late Ad ad; + var credential = LbcCredential(lbcToken: '', datadomeCookie: ''); @override void initState() { super.initState(); final metadataFromIsbn = widget.step.metadata.entries; - final title = metadataFromIsbn.length == 1 ? metadataFromIsbn.first.value.title : ''; + final title = metadataFromIsbn.length == 1 ? (metadataFromIsbn.first.value.title ?? '') : ''; var description = _getDescription(metadataFromIsbn); + description += '\n\n' + personal_info.customMessage; + final keywords = metadataFromIsbn.map((entry) => entry.value.keywords).expand((kw) => kw).toSet().join(', '); if (keywords.isNotEmpty) { description += '\n\nMots-clés:\n' + keywords; @@ -46,13 +49,14 @@ class _AdEditingWidgetState extends State { } String _getDescription(Iterable> metadataFromIsbn) { - final blurbs = - metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!).join('\n'); if (metadataFromIsbn.length == 1) { return 'Résumé:\n' + metadataFromIsbn.single.value.blurb!; } else { final bookTitles = metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); - final description = bookTitles + '\n\nRésumés:\n' + blurbs + '\n\n' + personal_info.customMessage; + final blurbs = metadataFromIsbn + .map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!) + .join('\n'); + final description = bookTitles + '\n\nRésumés:\n' + blurbs; return description; } } @@ -106,12 +110,30 @@ class _AdEditingWidgetState extends State { ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), ]), ), + TextFormField( + initialValue: '', + onChanged: (newText) => setState(() => credential.lbcToken = newText), + decoration: const InputDecoration( + icon: Icon(Icons.key), + labelText: 'LBC Bearer token', + ), + style: const TextStyle(fontSize: 20), + ), + TextFormField( + initialValue: '', + onChanged: (newText) => setState(() => credential.datadomeCookie = newText), + decoration: const InputDecoration( + icon: Icon(Icons.cookie), + labelText: 'datadome cookie', + ), + style: const TextStyle(fontSize: 20), + ), ElevatedButton( onPressed: (ad.title.length < 2 || ad.description.length < 15 || ad.priceCent == null) ? null : () async { print('Try to publish...'); - final res = await api.publishAd(ad: ad); + final res = await api.publishAd(ad: ad, credential: credential); if (!context.mounted) return; ScaffoldMessenger.of(context) diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index a80a140..50af226 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -13,7 +13,7 @@ abstract class Native { FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; - Future publishAd({required Ad ad, dynamic hint}); + Future publishAd({required Ad ad, required LbcCredential credential, dynamic hint}); FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta; } @@ -43,19 +43,29 @@ class Author { } class BookMetaData { - String title; + String? title; List authors; String? blurb; List keywords; BookMetaData({ - required this.title, + this.title, required this.authors, this.blurb, required this.keywords, }); } +class LbcCredential { + String lbcToken; + String datadomeCookie; + + LbcCredential({ + required this.lbcToken, + required this.datadomeCookie, + }); +} + enum ProviderEnum { Babelio, GoogleBooks, diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 9d28f06..c209bb1 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -44,13 +44,15 @@ class NativeImpl implements Native { argNames: ["provider", "isbn"], ); - Future publishAd({required Ad ad, dynamic hint}) { + Future publishAd( + {required Ad ad, required LbcCredential credential, dynamic hint}) { var arg0 = _platform.api2wire_box_autoadd_ad(ad); + var arg1 = _platform.api2wire_box_autoadd_lbc_credential(credential); return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_publish_ad(port_, arg0), + callFfi: (port_) => _platform.inner.wire_publish_ad(port_, arg0, arg1), parseSuccessData: _wire2api_bool, constMeta: kPublishAdConstMeta, - argValues: [ad], + argValues: [ad, credential], hint: hint, )); } @@ -58,7 +60,7 @@ class NativeImpl implements Native { FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta => const FlutterRustBridgeTaskConstMeta( debugName: "publish_ad", - argNames: ["ad"], + argNames: ["ad", "credential"], ); void dispose() { @@ -89,7 +91,7 @@ class NativeImpl implements Native { if (arr.length != 4) throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); return BookMetaData( - title: _wire2api_String(arr[0]), + title: _wire2api_opt_String(arr[0]), authors: _wire2api_list_author(arr[1]), blurb: _wire2api_opt_String(arr[2]), keywords: _wire2api_StringList(arr[3]), @@ -170,6 +172,14 @@ class NativePlatform extends FlutterRustBridgeBase { return ptr; } + @protected + ffi.Pointer api2wire_box_autoadd_lbc_credential( + LbcCredential raw) { + final ptr = inner.new_box_autoadd_lbc_credential_0(); + _api_fill_to_wire_lbc_credential(raw, ptr.ref); + return ptr; + } + @protected ffi.Pointer api2wire_uint_8_list(Uint8List raw) { final ans = inner.new_uint_8_list_0(raw.length); @@ -191,6 +201,17 @@ class NativePlatform extends FlutterRustBridgeBase { Ad apiObj, ffi.Pointer wireObj) { _api_fill_to_wire_ad(apiObj, wireObj.ref); } + + void _api_fill_to_wire_box_autoadd_lbc_credential( + LbcCredential apiObj, ffi.Pointer wireObj) { + _api_fill_to_wire_lbc_credential(apiObj, wireObj.ref); + } + + void _api_fill_to_wire_lbc_credential( + LbcCredential apiObj, wire_LbcCredential wireObj) { + wireObj.lbc_token = api2wire_String(apiObj.lbcToken); + wireObj.datadome_cookie = api2wire_String(apiObj.datadomeCookie); + } } // ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names @@ -312,19 +333,22 @@ class NativeWire implements FlutterRustBridgeWireBase { void wire_publish_ad( int port_, ffi.Pointer ad, + ffi.Pointer credential, ) { return _wire_publish_ad( port_, ad, + credential, ); } late final _wire_publish_adPtr = _lookup< ffi.NativeFunction< - ffi.Void Function( - ffi.Int64, ffi.Pointer)>>('wire_publish_ad'); - late final _wire_publish_ad = _wire_publish_adPtr - .asFunction)>(); + ffi.Void Function(ffi.Int64, ffi.Pointer, + ffi.Pointer)>>('wire_publish_ad'); + late final _wire_publish_ad = _wire_publish_adPtr.asFunction< + void Function( + int, ffi.Pointer, ffi.Pointer)>(); ffi.Pointer new_StringList_0( int len, @@ -350,6 +374,17 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _new_box_autoadd_ad_0 = _new_box_autoadd_ad_0Ptr.asFunction Function()>(); + ffi.Pointer new_box_autoadd_lbc_credential_0() { + return _new_box_autoadd_lbc_credential_0(); + } + + late final _new_box_autoadd_lbc_credential_0Ptr = + _lookup Function()>>( + 'new_box_autoadd_lbc_credential_0'); + late final _new_box_autoadd_lbc_credential_0 = + _new_box_autoadd_lbc_credential_0Ptr + .asFunction Function()>(); + ffi.Pointer new_uint_8_list_0( int len, ) { @@ -407,6 +442,12 @@ class wire_Ad extends ffi.Struct { external ffi.Pointer imgs_path; } +class wire_LbcCredential extends ffi.Struct { + external ffi.Pointer lbc_token; + + external ffi.Pointer datadome_cookie; +} + typedef DartPostCObjectFnType = ffi.Pointer< ffi.NativeFunction)>>; typedef DartPort = ffi.Int64; diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 54e711e..9452a69 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -85,7 +85,7 @@ class _MetadataCollectingWidgetState extends State { )), ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( future: e.value, - builder: (data) => data == null ? noneText : SelectableText(data.title))), + builder: (data) => data == null ? noneText : SelectableText(data.title ?? ''))), ]), TableRow(children: [ FutureWidget( @@ -118,6 +118,7 @@ class _MetadataCollectingWidgetState extends State { builder: (data) => TextFormField( initialValue: data?.blurb, onChanged: (newText) => setState(() => manual.blurb = newText), + maxLines: null, decoration: const InputDecoration( icon: Icon(Icons.description), labelText: 'Book blurb', diff --git a/native/src/api.rs b/native/src/api.rs index 1440673..c692fec 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,5 +1,5 @@ use crate::cached_client::CachedClient; -use crate::common::Provider; +use crate::common::{Provider, LbcCredential}; use crate::common::{Ad, BookMetaData}; use crate::publisher::Publisher; use crate::{babelio, google_books, leboncoin}; @@ -21,7 +21,7 @@ pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Optio } } -pub fn publish_ad(ad: Ad) -> bool { +pub fn publish_ad(ad: Ad, credential: LbcCredential) -> bool { let lbc_publisher = leboncoin::Leboncoin {}; - Publisher::publish(&lbc_publisher, ad) + Publisher::publish(&lbc_publisher, ad, credential) } diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index e24e918..628e8de 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -11,8 +11,12 @@ pub extern "C" fn wire_get_metadata_from_provider( } #[no_mangle] -pub extern "C" fn wire_publish_ad(port_: i64, ad: *mut wire_Ad) { - wire_publish_ad_impl(port_, ad) +pub extern "C" fn wire_publish_ad( + port_: i64, + ad: *mut wire_Ad, + credential: *mut wire_LbcCredential, +) { + wire_publish_ad_impl(port_, ad, credential) } // Section: allocate functions @@ -31,6 +35,11 @@ pub extern "C" fn new_box_autoadd_ad_0() -> *mut wire_Ad { support::new_leak_box_ptr(wire_Ad::new_with_null_ptr()) } +#[no_mangle] +pub extern "C" fn new_box_autoadd_lbc_credential_0() -> *mut wire_LbcCredential { + support::new_leak_box_ptr(wire_LbcCredential::new_with_null_ptr()) +} + #[no_mangle] pub extern "C" fn new_uint_8_list_0(len: i32) -> *mut wire_uint_8_list { let ans = wire_uint_8_list { @@ -75,6 +84,21 @@ impl Wire2Api for *mut wire_Ad { Wire2Api::::wire2api(*wrap).into() } } +impl Wire2Api for *mut wire_LbcCredential { + fn wire2api(self) -> LbcCredential { + let wrap = unsafe { support::box_from_leak_ptr(self) }; + Wire2Api::::wire2api(*wrap).into() + } +} + +impl Wire2Api for wire_LbcCredential { + fn wire2api(self) -> LbcCredential { + LbcCredential { + lbc_token: self.lbc_token.wire2api(), + datadome_cookie: self.datadome_cookie.wire2api(), + } + } +} impl Wire2Api> for *mut wire_uint_8_list { fn wire2api(self) -> Vec { @@ -102,6 +126,13 @@ pub struct wire_Ad { imgs_path: *mut wire_StringList, } +#[repr(C)] +#[derive(Clone)] +pub struct wire_LbcCredential { + lbc_token: *mut wire_uint_8_list, + datadome_cookie: *mut wire_uint_8_list, +} + #[repr(C)] #[derive(Clone)] pub struct wire_uint_8_list { @@ -138,6 +169,21 @@ impl Default for wire_Ad { } } +impl NewWithNullPtr for wire_LbcCredential { + fn new_with_null_ptr() -> Self { + Self { + lbc_token: core::ptr::null_mut(), + datadome_cookie: core::ptr::null_mut(), + } + } +} + +impl Default for wire_LbcCredential { + fn default() -> Self { + Self::new_with_null_ptr() + } +} + // Section: sync execution mode utility #[no_mangle] diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index cec2d9f..f99f0c4 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -22,6 +22,7 @@ use std::sync::Arc; use crate::common::Ad; use crate::common::Author; use crate::common::BookMetaData; +use crate::common::LbcCredential; // Section: wire functions @@ -43,7 +44,11 @@ fn wire_get_metadata_from_provider_impl( }, ) } -fn wire_publish_ad_impl(port_: MessagePort, ad: impl Wire2Api + UnwindSafe) { +fn wire_publish_ad_impl( + port_: MessagePort, + ad: impl Wire2Api + UnwindSafe, + credential: impl Wire2Api + UnwindSafe, +) { FLUTTER_RUST_BRIDGE_HANDLER.wrap( WrapInfo { debug_name: "publish_ad", @@ -52,7 +57,8 @@ fn wire_publish_ad_impl(port_: MessagePort, ad: impl Wire2Api + UnwindSafe) }, move || { let api_ad = ad.wire2api(); - move |task_callback| Ok(publish_ad(api_ad)) + let api_credential = credential.wire2api(); + move |task_callback| Ok(publish_ad(api_ad, api_credential)) }, ) } @@ -84,6 +90,7 @@ impl Wire2Api for i32 { self } } + impl Wire2Api for i32 { fn wire2api(self) -> ProviderEnum { match self { diff --git a/native/src/common.rs b/native/src/common.rs index a8bedf5..7b1a52a 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -22,6 +22,11 @@ pub trait Provider { fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option; } +pub struct LbcCredential { + pub lbc_token: String, + pub datadome_cookie: String, +} + pub struct Ad { pub title: String, pub description: String, diff --git a/native/src/leboncoin.rs b/native/src/leboncoin.rs index 8235aa8..826aa13 100644 --- a/native/src/leboncoin.rs +++ b/native/src/leboncoin.rs @@ -10,9 +10,10 @@ mod request; use itertools::Itertools; use std::path::Path; + impl Publisher for Leboncoin { - fn publish(&self, ad: crate::common::Ad) -> bool { - crate::jwt_decoder::check_jwt_expiration(personal_info::LBC_TOKEN); + fn publish(&self, ad: crate::common::Ad, credential: crate::common::LbcCredential) -> bool { + crate::jwt_decoder::check_jwt_expiration(&credential.lbc_token); let img_lbc_refs = ad .imgs_path .clone() @@ -22,7 +23,7 @@ impl Publisher for Leboncoin { let compressed_img_filepath = Path::new("compressed/") .join(input_path.file_name().unwrap().to_str().unwrap()); image_tools::downsize_image(800, 800, &input_path, &compressed_img_filepath); - let imgs_upload_response = request::upload_file(&compressed_img_filepath); + let imgs_upload_response = request::upload_file(&compressed_img_filepath, &credential); let imgs_lbc_ref = parser::parse_file_upload(&imgs_upload_response); Image { name: imgs_lbc_ref.filename, @@ -32,9 +33,9 @@ impl Publisher for Leboncoin { .collect_vec(); // let img_lbc_refs = vec![]; - let send_answer: String = request::send(ad, img_lbc_refs); + let send_answer: String = request::send(ad, img_lbc_refs, &credential); let ad_id = parser::parse_send(&send_answer); - let submit_answer = request::submit(ad_id).unwrap(); + let submit_answer = request::submit(ad_id, credential).unwrap(); let submit_ret = parser::parse_submit(&submit_answer); println!("submit_ret = {:#?}", submit_ret); match submit_ret { diff --git a/native/src/leboncoin/request.rs b/native/src/leboncoin/request.rs index d49ad99..1e7682e 100644 --- a/native/src/leboncoin/request.rs +++ b/native/src/leboncoin/request.rs @@ -3,11 +3,11 @@ use std::path::Path; use reqwest; use serde::{Deserialize, Serialize}; -use crate::leboncoin::personal_info; +use crate::{common::LbcCredential, leboncoin::personal_info}; use super::Image; -pub fn send(ad: crate::common::Ad, images: Vec) -> String { +pub fn send(ad: crate::common::Ad, images: Vec, credential: &LbcCredential) -> String { let mut headers = reqwest::header::HeaderMap::new(); headers.insert("authority", "api.leboncoin.fr".parse().unwrap()); headers.insert("accept", "*/*".parse().unwrap()); @@ -17,10 +17,7 @@ pub fn send(ad: crate::common::Ad, images: Vec) -> String { ); headers.insert( "authorization", - ["Bearer ", personal_info::LBC_TOKEN] - .concat() - .parse() - .unwrap(), + ["Bearer ", &credential.lbc_token].concat().parse().unwrap(), ); headers.insert("cache-control", "no-cache".parse().unwrap()); headers.insert("content-type", "application/json".parse().unwrap()); @@ -44,7 +41,7 @@ pub fn send(ad: crate::common::Ad, images: Vec) -> String { headers.insert("sec-fetch-mode", "cors".parse().unwrap()); headers.insert( reqwest::header::COOKIE, - personal_info::DATA_DOME_COOKIE.parse().unwrap(), + credential.datadome_cookie.parse().unwrap(), ); headers.insert("sec-fetch-site", "same-site".parse().unwrap()); headers.insert("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36".parse().unwrap()); @@ -186,7 +183,7 @@ pub struct Location { pub zipcode: String, } -pub fn submit(ad_id: i64) -> Result> { +pub fn submit(ad_id: i64, credential: LbcCredential) -> Result> { let mut headers = reqwest::header::HeaderMap::new(); headers.insert("authority", "api.leboncoin.fr".parse().unwrap()); headers.insert("accept", "*/*".parse().unwrap()); @@ -196,10 +193,7 @@ pub fn submit(ad_id: i64) -> Result> { ); headers.insert( "authorization", - ["Bearer ", personal_info::LBC_TOKEN] - .concat() - .parse() - .unwrap(), + ["Bearer ", &credential.lbc_token].concat().parse().unwrap(), ); headers.insert("cache-control", "no-cache".parse().unwrap()); headers.insert("content-type", "application/json".parse().unwrap()); @@ -223,7 +217,7 @@ pub fn submit(ad_id: i64) -> Result> { headers.insert("sec-fetch-mode", "cors".parse().unwrap()); headers.insert( reqwest::header::COOKIE, - personal_info::DATA_DOME_COOKIE.parse().unwrap(), + credential.datadome_cookie.parse().unwrap(), ); headers.insert("sec-fetch-site", "same-site".parse().unwrap()); headers.insert("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36".parse().unwrap()); @@ -278,14 +272,11 @@ pub struct SubmitAd { pub transaction_type: String, } -pub fn upload_file(img_path: &Path) -> String { +pub fn upload_file(img_path: &Path, credential: &LbcCredential) -> String { let mut headers = reqwest::header::HeaderMap::new(); headers.insert( "authorization", - ["Bearer ", personal_info::LBC_TOKEN] - .concat() - .parse() - .unwrap(), + ["Bearer ", &credential.lbc_token].concat().parse().unwrap(), ); headers.insert( "sec-ch-ua", @@ -299,7 +290,7 @@ pub fn upload_file(img_path: &Path) -> String { headers.insert("sec-fetch-mode", "cors".parse().unwrap()); headers.insert( reqwest::header::COOKIE, - personal_info::DATA_DOME_COOKIE.parse().unwrap(), + credential.datadome_cookie.parse().unwrap(), ); headers.insert("sec-fetch-site", "same-site".parse().unwrap()); headers.insert("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36".parse().unwrap()); @@ -320,28 +311,3 @@ pub fn upload_file(img_path: &Path) -> String { println!("upload_file response = {}", res); res } - -//curl 'https://api.leboncoin.fr/api/pintad/v1/public/upload/image' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0' -H 'Accept: */*' -H 'Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3' -H 'Accept-Encoding: gzip, deflate, br' -H 'Referer: https://www.leboncoin.fr/annonce/2305203826/editer' -H 'api_key: ba0c2dad52b3ec' -H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxNTYxMiwiaWF0IjoxNjc3NjA4NDExLCJpZCI6IjliYzg5OWM1LTMxN2UtNDE1Ny1iMzEyLTAyMWQ1ZTQ3YTlkYSIsImluc3RhbGxfaWQiOiI3MDQ1YjhmMy0xMzYyLTRiN2UtYjhmZC1lY2Y0OWU4ODRjOGQiLCJqdGkiOiIyY2FhMzU5OS1jZDk3LTQxYTEtYmIzMC1hNmI2YjlmMDA1MzciLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6ImVjMDZjZTM0LThhMzItNDkyNC05NDc1LTc4MzU4MmY3ZGI3YiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiIyNGUyNDA2Mi0xNmE2LTQ4NWQtYjg0Yi1iNWY4MGViNjkzYTUiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.GRqf3gDdFiq1ukFhN_2i8HhWycirauVUM7rDoZZHsSgD-wv5VOwuKDWc6axDoPK3Wsbg_oFXfrSHX-bcDkE2SRaOqNB734eqD-fbceCG1ntf-afgeLWf-MPnas0n_ylOB2ZSK1LAG2aCXZSDm3ZEkXs_-KZhwQtsmqLgIte0PJUUk_qP4tYYDqLe3FvUeGIkrPAFHKxfnAXmKXf-kh9RvbykGiek9lqFT-Hg95X21eS3Z8HH2li-OMP4B2I-PQysOLuaAZ47wkjkt8PKgC6qG3rlitr28MRbkBYrsuo5ic9JMEKTlmbYa5WsyzZJL5F5Y3CdKTXxiQ4ae2kY2hRLgubk2Dihy8vdqLhitX-Fm_sGQSFnP7vy7iHhQCK5m4jLnD-p-sD_DAehNkGYF8lQqG44myb7XdmTtY9uoR_1Tv2LXYSncKQzEpCn-G6Pf0DJg4xb02CCxXWqB7oysooBgFzgPGdixJeBnFSX_8H0zbmoszUUW7Wqw_aSKv7aAQ3p2Foha3U7B4B-3lHPetec6wEo1eLvq5XXRbDAZuvIqbG6cvQHS5HDNkiBQIHfED3VwVOnextu0BADL7hYl4bOM50yNquNIoecPbEOC0Tij3JdYdGjHJ_ywDhsGwD08awZLIpPsJ1ppcaxMv3thoMsiInKqX5wLHNmTec13Lyn978' -H 'Content-Type: multipart/form-data; boundary=---------------------------153785532732722146451504606153' -H 'Origin: https://www.leboncoin.fr' -H 'Connection: keep-alive' -H 'Cookie: datadome=Ysu9fziXa098YNozRd6iS4oq62CcYXd0SZUP9cw4Rl5NMfVROqQrM7eT96i618vhu3M4l~JZWv80IUWEBCzBJdRnqF2Z-W6M0oyZXXOyLvLbDiGsWPQcoQw_UU7qXS-; __Secure-Install=7045b8f3-1362-4b7e-b8fd-ecf49e884c8d; __Secure-InstanceId=7045b8f3-1362-4b7e-b8fd-ecf49e884c8d; utag_main=v_id:0186993fcaf6000e3c437fda305405046001900900bd0$_sn:1$_ss:0$_pn:5%3Bexp-session$_st:1677610707192$ses_id:1677608340214%3Bexp-session; didomi_token=eyJ1c2VyX2lkIjoiMTg2OTkzZmMtYjVmNi02ZTFiLWI2YWQtZDFmMjhmOTJiODk4IiwiY3JlYXRlZCI6IjIwMjMtMDItMjhUMTg6MTk6MDIuMzQyWiIsInVwZGF0ZWQiOiIyMDIzLTAyLTI4VDE4OjE5OjAyLjM0MloiLCJ2ZW5kb3JzIjp7ImVuYWJsZWQiOlsiZ29vZ2xlIiwiYzpsYmNmcmFuY2UiLCJjOnJldmxpZnRlci1jUnBNbnA1eCIsImM6ZGlkb21pIl19LCJwdXJwb3NlcyI6eyJlbmFibGVkIjpbImV4cGVyaWVuY2V1dGlsaXNhdGV1ciIsIm1lc3VyZWF1ZGllbmNlIiwicGVyc29ubmFsaXNhdGlvbm1hcmtldGluZyIsInByaXgiXX0sInZlbmRvcnNfbGkiOnsiZW5hYmxlZCI6WyJnb29nbGUiXX0sInZlcnNpb24iOjIsImFjIjoiRExXQkFBRUlBSXdBV1FCLWdHRkFQeUFra0JKWUVBd0lrZ1NrQXR5QnhBRHB3SFZnUU1BaW9CSE9DU2NFdFlLREFVSWdvdEJYT0N3VUZ0NExqQVhMQXdHQmhFREUwR1dvLkRMV0JBQUVJQUl3QVdRQi1nR0ZBUHlBa2tCSllFQXdJa2dTa0F0eUJ4QURwd0hWZ1FNQWlvQkhPQ1NjRXRZS0RBVUlnb3RCWE9Dd1VGdDRMakFYTEF3R0JoRURFMEdXbyJ9; euconsent-v2=CPn5KgAPn5KgAAHABBENC5CgAPLAAH7AAAAAIsNB_G_dTyPi-f59YvtwYQ1P4VQnoyACjgaNgwwJiRLBMI0EhmAIKAHqAAACIBAkICJAAQBlCAHAAAAA4IEAASMMAAAAIRAIIgCAAEAAAmJICABZCxAAAQAQgkwAABQAgAICABMgSDAAAAAAFAAAAAgAAAAAAAAAAAAAQAAAAAAAAgAAAAAAAAAAAAAEEAQATDVuIAGxLHAmkDCIAACMIAgCgBABRQBCwQAEBIgAEEYACjAAAAAFAAAAAAAAEAMAAAAAgAQgAAAAcEAgAIAEAAAAEAgEAAAAACAAADAAAAAAAMAAAAAAgAIAAAKAQAABAAgAJAgACAAAAgAAAAAAAAAgEAAAAAAAAAAAAAAAAQAxQAGAAIJQjAAMAAQShIAAYAAglCAA.flgAD9gAAAAA; include_in_experiment=true; _hjSessionUser_2783207=eyJpZCI6IjBlOTAwNGIzLWVmNmUtNWM5ZC1iNzQ4LTU4NjFlMWVmMmUyMSIsImNyZWF0ZWQiOjE2Nzc2MDgzNDM2NzksImV4aXN0aW5nIjp0cnVlfQ==; _hjFirstSeen=1; _hjIncludedInSessionSample_2783207=1; _hjSession_2783207=eyJpZCI6ImE1MjQxMjgxLWY5YzQtNDU5YS05NzNkLWFiODc4YjM4MzU2MyIsImNyZWF0ZWQiOjE2Nzc2MDgzNDM2ODEsImluU2FtcGxlIjp0cnVlfQ==; _hjAbsoluteSessionInProgress=0; ry_ry-l3b0nco_realytics=eyJpZCI6InJ5XzI5RjFCQTE5LUQ3ODYtNEUxQy05NUNDLTYwMjE4QUVEOUI3NyIsImNpZCI6bnVsbCwiZXhwIjoxNzA5MTQ0MzQ0Nzc3LCJjcyI6bnVsbH0%3D; ry_ry-l3b0nco_so_realytics=eyJpZCI6InJ5XzI5RjFCQTE5LUQ3ODYtNEUxQy05NUNDLTYwMjE4QUVEOUI3NyIsImNpZCI6bnVsbCwib3JpZ2luIjp0cnVlLCJyZWYiOm51bGwsImNvbnQiOm51bGwsIm5zIjpmYWxzZX0%3D; _gcl_au=1.1.701707994.1677608345; cto_bundle=CNZ-SV9JaWtsRlAyVjVqODhGWWhVYldZN3BzOFl6eG5hJTJCMTZibnQ2R2RNakRCS3N5WDdWWFVleHpzRCUyQmNtQUQwUmt0bW5CdldxZUFLem02b1clMkZTMDdTdXQ3emtxYUtISDJqUzZEazclMkZZMFlFTzQzS2dRaU5NZnVMc3JtR2NMTFBBcGNBZ2hINGczcm44TFpQSSUyRmF1YiUyQnJ3OXclM0QlM0Q; __gads=ID=f3fe3612fb4470d8:T=1677608347:S=ALNI_MbMwkuo3rfJtYKI9L6tyyLC8sIwbg; __gpi=UID=00000be0047e7d68:T=1677608347:RT=1677608347:S=ALNI_Mav2FKNNTDv7opgvCIg2Kld9HMpbw; luat=eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxNTYxMiwiaWF0IjoxNjc3NjA4NDExLCJpZCI6IjliYzg5OWM1LTMxN2UtNDE1Ny1iMzEyLTAyMWQ1ZTQ3YTlkYSIsImluc3RhbGxfaWQiOiI3MDQ1YjhmMy0xMzYyLTRiN2UtYjhmZC1lY2Y0OWU4ODRjOGQiLCJqdGkiOiIyY2FhMzU5OS1jZDk3LTQxYTEtYmIzMC1hNmI2YjlmMDA1MzciLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6ImVjMDZjZTM0LThhMzItNDkyNC05NDc1LTc4MzU4MmY3ZGI3YiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiIyNGUyNDA2Mi0xNmE2LTQ4NWQtYjg0Yi1iNWY4MGViNjkzYTUiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.GRqf3gDdFiq1ukFhN_2i8HhWycirauVUM7rDoZZHsSgD-wv5VOwuKDWc6axDoPK3Wsbg_oFXfrSHX-bcDkE2SRaOqNB734eqD-fbceCG1ntf-afgeLWf-MPnas0n_ylOB2ZSK1LAG2aCXZSDm3ZEkXs_-KZhwQtsmqLgIte0PJUUk_qP4tYYDqLe3FvUeGIkrPAFHKxfnAXmKXf-kh9RvbykGiek9lqFT-Hg95X21eS3Z8HH2li-OMP4B2I-PQysOLuaAZ47wkjkt8PKgC6qG3rlitr28MRbkBYrsuo5ic9JMEKTlmbYa5WsyzZJL5F5Y3CdKTXxiQ4ae2kY2hRLgubk2Dihy8vdqLhitX-Fm_sGQSFnP7vy7iHhQCK5m4jLnD-p-sD_DAehNkGYF8lQqG44myb7XdmTtY9uoR_1Tv2LXYSncKQzEpCn-G6Pf0DJg4xb02CCxXWqB7oysooBgFzgPGdixJeBnFSX_8H0zbmoszUUW7Wqw_aSKv7aAQ3p2Foha3U7B4B-3lHPetec6wEo1eLvq5XXRbDAZuvIqbG6cvQHS5HDNkiBQIHfED3VwVOnextu0BADL7hYl4bOM50yNquNIoecPbEOC0Tij3JdYdGjHJ_ywDhsGwD08awZLIpPsJ1ppcaxMv3thoMsiInKqX5wLHNmTec13Lyn978; _schn=_27faqp; _scid=d6571009-7472-41bb-9cb9-8510b51a6a95; _fbp=fb.1.1677608908547.2034999500' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-site' -H 'TE: trailers' --data-binary $'-----------------------------153785532732722146451504606153\r\nContent-Disposition: form-data; name="file"; filename="20230228_192237.jpg"\r\nContent-Type: image/jpeg\r\n\r\n-----------------------------153785532732722146451504606153--\r\n' - -// curl 'https://api.leboncoin.fr/api/pintad/v1/public/upload/image' \ -// -H 'authority: api.leboncoin.fr' \ - -// -H 'accept: */*' \ -/* --H 'accept-language: en-US,en;q=0.9,fr;q=0.8' \ --H 'api_key: ba0c2dad52b3ec' \ --H 'authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxMTY1NywiaWF0IjoxNjc3NjA0NDU2LCJpZCI6IjQ1MTM1OTUxLTY0ZjQtNDFjZS05NGVjLWJkMzkzZDRjY2U2ZiIsImluc3RhbGxfaWQiOiIwNTA1NzA2YS05NDJhLTQzNjktYTdlYy02MGYxZDYxYWZiNjUiLCJqdGkiOiJlYmQzZDI2My1mMTMzLTQ3MjktYjVjOS1kNTA3ZmYwZjUxNDEiLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6IjcwN2M3M2ZiLTk2NzQtNDZlNC05N2NmLTRkZTk2MzQ3NTYwZiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiI3NGM1ZGY4Yy05MzQ3LTQ0MWQtYWViYi0zZWIzYjYyZTk1MjMiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.dZMGwYAei7ovgsB6REy1XjtTjqbgVj4oXQomz7teIj-z0KEajW1pyLyBp4EweGpyb1McgSu8BOS74da9GEeNZ60x9pOAn9KiS8VNfqMBYiwknURnwI8NJdf9KiB6k__SzXb05uTyDeazLK76MoUIAImT8LwzMrFvdZewmmkqyYqT4o4Bcn0tynDkRLv5dSZ87n4ca0AsOsHt6zgWipwpqsGBom0ysYnOzq-hCkyM1-3SsjR4ohVT--qqR2EIijO2-SGk90kmwDzR9aYwCZzzRAlUTFhpE6-zHO7TquAV9oIQAU2Wmq5HgzhEREjUhJOI0fqXy9xk1dPRzb1A__rDbAm8Nkfxq-mF1JcaRM-nB2Pb1VgDV8j6P2MtPC8TlKyr9dMQFzuTWpvFa8sMYg92f3i1oLwvbgsHu5nweMqrWItDAwja7v35T3IReejBwKGOXXEmsTlJEcq589b2AdtZwH82mcFfwn6QkTPbJVGv7YiSLbyGNCQLbUJ-FhLptq2fLZwJUTEye72u-WzY5yeCxs8ZaIfaQHTduJrVviMlEfam9rnUUU-cUdA7NJx8bg63FqOYhEH-hFHYeo5gSF5EqA97jBwC2KoApADf4t1q5EhUPw7gGR9U7qQuhoRiTLVFV4kEjIWeX1QRntOHkVXfuoWTaTE-X1A6XsBvkcINJho' \ --H 'content-type: multipart/form-data; boundary=----WebKitFormBoundaryZ9zBRAquVv1qb78o' \ --H 'cookie: s=red1xa04ffeea4ed8b07c235277adc932a3e4d092859d; log_from=http%3A%2F%2Fwww2.leboncoin.fr%2Fdc%2Frules%3Fca%3D12_s; xtvrn=$266818$; ry_ry-l3b0nco_realytics=eyJpZCI6InJ5Xzg1M0VCQzVGLURCNjgtNDk1NC1COEE0LTM1OTY0RjhDN0U1RSIsImNpZCI6bnVsbCwiZXhwIjoxNjg0MzI0MDMzMDQ1LCJjcyI6bnVsbH0%3D; _pin_unauth=dWlkPU1HVXhPREF4TnpZdE1qTXhZUzAwTXpBd0xXSmpZVFF0TXpoallqRTNNamczTldVeg; _hjSessionUser_2783207=eyJpZCI6ImZjZTk5MmM1LTY0NmItNTViOS05OWY4LWRkYzc2YmE3ODc1YyIsImNyZWF0ZWQiOjE2NTI3ODgwMzQ1MjcsImV4aXN0aW5nIjp0cnVlfQ==; _scid=e7942114-373a-4d77-b41c-1717c1b77529; __Secure-Install=0505706a-942a-4369-a7ec-60f1d61afb65; __Secure-InstanceId=0505706a-942a-4369-a7ec-60f1d61afb65; didomi_token=eyJ1c2VyX2lkIjoiMTcyNmY1NWMtMjgwYS02Njc3LWFmMDAtZjk2YzM1MDM1NDQ3IiwiY3JlYXRlZCI6IjIwMjItMTItMDFUMTk6NTA6MDAuNzMwWiIsInVwZGF0ZWQiOiIyMDIyLTEyLTAxVDE5OjUwOjAwLjczMFoiLCJ2ZW5kb3JzIjp7ImVuYWJsZWQiOlsiZ29vZ2xlIiwiYzpsYmNmcmFuY2UiLCJjOnJldmxpZnRlci1jUnBNbnA1eCIsImM6ZGlkb21pIl19LCJwdXJwb3NlcyI6eyJlbmFibGVkIjpbInBlcnNvbm5hbGlzYXRpb25tYXJrZXRpbmciLCJwcml4IiwibWVzdXJlYXVkaWVuY2UiLCJleHBlcmllbmNldXRpbGlzYXRldXIiXX0sInZlbmRvcnNfbGkiOnsiZW5hYmxlZCI6WyJnb29nbGUiXX0sInZlcnNpb24iOjIsImFjIjoiRExXQkFBRUlBSXdBV1FCLWdHRkFQeUFra0JKWUVBd0lrZ1NrQXR5QnhBRHB3SFZnUU1BaW9CSE9DU2NFdFlLREFVSWdvdEJYT0N3VUZ0NExqQVhMQXdHQmhFREUwR1dvLkRMV0EtQUVJQUl3QV9RRENnSDVBU1NBa3NDQVlFU1FKU0FXNUE0Z0IwNERxd0lHQVJVQWpuQkpPQ1dzRkJnS0VRVVdncm5CWUtDMjhGeGdMbGdZREF3aUJpYURMVUFBQSJ9; euconsent-v2=CPjT1EAPjT1EAAHABBENCsCgAPLAAHLAAAAAIAtB_G_dTyPi-f59YvtwYQ1P4VQnoyACjgaNgwwJiRLBMI0EhmAIKAHqAAACIBAkICJAAQBlCAHAAAAA4IEAASMMAAAAIRAIIgCAAEAAAiJICABZCxAAAQAQgkwAABQAgAICABMgSDAAAAAAFAAAAAgAAAAAAAAAAAAAQAAAAAAAAggCACYatxAA2JY4E0gYRAAARhAEAUAIAKKAIWCAAgJEAAgjAAUYAAAAAoAAAAAAAAgBgAAAAEACEAAAADggEABAAgAAAAgEAgAAAAAQAAAYAAAAAABgAAAAAEABAAABQCAAAIAEABIEAAQAAAEAAAAAAAAAEAgAAAAAAAAAAAAAAACAGKAAwABBJYYABgACCSxAADAAEElg.flgADlgAAAAA; include_in_experiment=true; _gcl_au=1.1.396577628.1672180056; _fbp=fb.1.1674497618789.664585017; __gads=ID=3756ef2df471787f:T=1674498902:S=ALNI_MZYJfxMcpbJdw6KbbvraZ_khGHZnA; __gpi=UID=00000bc9e3cb6789:T=1674498902:RT=1674566983:S=ALNI_MbBn6iqQ8F-8Zpmb1WCKLM9NSFmxg; __gsas=ID=8ce0a4b086087df5:T=1674567077:S=ALNI_Mby9K0wXjz1NzkAZsVzbLFcccNtNw; adview_clickmeter=search__listing__4__8c78a2b2-4b20-497b-ae05-cf1e963b741d; luat=eyJhbGciOiJSUzI1NiIsImtpZCI6IjgyYjFjNmYwLWRiM2EtNTQ2Ny1hYmI2LTJlMzAxNDViZjc3MiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiJsYmMtZnJvbnQtd2ViIiwiZGVwcmVjYXRlZF9zdG9yZV9pZCI6NTU3OTE3NDQsImV4cCI6MTY3NzYxMTY1NywiaWF0IjoxNjc3NjA0NDU2LCJpZCI6IjQ1MTM1OTUxLTY0ZjQtNDFjZS05NGVjLWJkMzkzZDRjY2U2ZiIsImluc3RhbGxfaWQiOiIwNTA1NzA2YS05NDJhLTQzNjktYTdlYy02MGYxZDYxYWZiNjUiLCJqdGkiOiJlYmQzZDI2My1mMTMzLTQ3MjktYjVjOS1kNTA3ZmYwZjUxNDEiLCJyZWZ1c2VkX3Njb3BlcyI6bnVsbCwicmVxdWVzdF9pZCI6IjcwN2M3M2ZiLTk2NzQtNDZlNC05N2NmLTRkZTk2MzQ3NTYwZiIsInNjb3BlcyI6WyJsYmMucHJpdmF0ZSIsImxiY2dycC5hdXRoLnR3b2ZhY3Rvci5zbXMubWUuYWN0aXZhdGUiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRpc3BsYXkiLCJvZmZsaW5lIiwibGJjLmF1dGguZW1haWwucGFydC5jaGFuZ2UiLCJsYmMuZXNjcm93YWNjb3VudC5tYWludGVuYW5jZS5yZWFkIiwibGJjZ3JwLmF1dGgudHdvZmFjdG9yLm1lLioiLCJsYmNncnAuYXV0aC5zZXNzaW9uLm1lLmRlbGV0ZSIsImxiY2dycC5hdXRoLnNlc3Npb24ubWUucmVhZCIsImxiYy4qLm1lLioiLCJsYmMuKi4qLm1lLioiLCJiZXRhLmxiYy5hdXRoLnR3b2ZhY3Rvci5tZS4qIl0sInNlc3Npb25faWQiOiI3NGM1ZGY4Yy05MzQ3LTQ0MWQtYWViYi0zZWIzYjYyZTk1MjMiLCJzdWIiOiJsYmM7MTgzMGE2NGEtZjJjYy00Y2Q4LTg3ZjAtYzVkYjdmOTU2N2Q4OzU1NzkxNzQ0In0.dZMGwYAei7ovgsB6REy1XjtTjqbgVj4oXQomz7teIj-z0KEajW1pyLyBp4EweGpyb1McgSu8BOS74da9GEeNZ60x9pOAn9KiS8VNfqMBYiwknURnwI8NJdf9KiB6k__SzXb05uTyDeazLK76MoUIAImT8LwzMrFvdZewmmkqyYqT4o4Bcn0tynDkRLv5dSZ87n4ca0AsOsHt6zgWipwpqsGBom0ysYnOzq-hCkyM1-3SsjR4ohVT--qqR2EIijO2-SGk90kmwDzR9aYwCZzzRAlUTFhpE6-zHO7TquAV9oIQAU2Wmq5HgzhEREjUhJOI0fqXy9xk1dPRzb1A__rDbAm8Nkfxq-mF1JcaRM-nB2Pb1VgDV8j6P2MtPC8TlKyr9dMQFzuTWpvFa8sMYg92f3i1oLwvbgsHu5nweMqrWItDAwja7v35T3IReejBwKGOXXEmsTlJEcq589b2AdtZwH82mcFfwn6QkTPbJVGv7YiSLbyGNCQLbUJ-FhLptq2fLZwJUTEye72u-WzY5yeCxs8ZaIfaQHTduJrVviMlEfam9rnUUU-cUdA7NJx8bg63FqOYhEH-hFHYeo5gSF5EqA97jBwC2KoApADf4t1q5EhUPw7gGR9U7qQuhoRiTLVFV4kEjIWeX1QRntOHkVXfuoWTaTE-X1A6XsBvkcINJho; _hjSession_2783207=eyJpZCI6IjlmOWFiNjllLTk0MTctNDIwOS1iMTU0LWQwODM1OGU2MDVlZSIsImNyZWF0ZWQiOjE2Nzc2MDQ0NTc4NTgsImluU2FtcGxlIjp0cnVlfQ==; _hjAbsoluteSessionInProgress=0; ry_ry-l3b0nco_so_realytics=eyJpZCI6InJ5Xzg1M0VCQzVGLURCNjgtNDk1NC1COEE0LTM1OTY0RjhDN0U1RSIsImNpZCI6bnVsbCwib3JpZ2luIjpmYWxzZSwicmVmIjpudWxsLCJjb250IjpudWxsLCJucyI6ZmFsc2V9; _hjIncludedInSessionSample_2783207=1; cto_bundle=XggTMV9lYzVPa0lSRm5hMGZrMzB1ciUyRkhyamtZUmRmUHFhYkVVVDlMRWZMSUxRQW1PMGdOYyUyQkNwOE5QbFp3Y0U5NmV1aWtyZjg3b2xMZ09tOFVTdWFMenBPR2Y1anNhb0tteFZqUng5NDVBVmdzakRjdVh1cyUyRkdiYWxGVnZXTyUyRkl4dk9Q; datadome=4cwibCI1RYexaCD1wjZqJZ-6hUi16_fPyqPGEtsMSG-r3~8EoWMghXY6ZUZ3L~1GpA3vzRzDw__LqSlDp~FlYdAu_jP3M0N9vV8ZWBSbzQ0~ijJp6tNET4wW0fjfuKGn; utag_main=v_id:01792c6c39a9001726449a99708002069002106100bd0$_sn:149$_ss:0$_st:1677607622614$_pn:3%3Bexp-session$ses_id:1677604455561%3Bexp-session' \ --H 'origin: https://www.leboncoin.fr' \ --H 'referer: https://www.leboncoin.fr/deposer-une-annonce' \ --H 'sec-ch-ua: "Not A(Brand";v="24", "Chromium";v="110"' \ --H 'sec-ch-ua-mobile: ?0' \ --H 'sec-ch-ua-platform: "Linux"' \ --H 'sec-fetch-dest: empty' \ --H 'sec-fetch-mode: cors' \ --H 'sec-fetch-site: same-site' \ --H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' \ ---data-raw $'------WebKitFormBoundaryZ9zBRAquVv1qb78o\r\nContent-Disposition: form-data; name="file"; filename="20230204_194811.jpg"\r\nContent-Type: image/jpeg\r\n\r\n\r\n------WebKitFormBoundaryZ9zBRAquVv1qb78o--\r\n' \ ---compressed -*/ diff --git a/native/src/publisher.rs b/native/src/publisher.rs index 329cbf3..6008400 100644 --- a/native/src/publisher.rs +++ b/native/src/publisher.rs @@ -1,5 +1,5 @@ -use crate::common::Ad; +use crate::common::{Ad, LbcCredential}; pub trait Publisher { - fn publish(&self, ad: Ad) -> bool; + fn publish(&self, ad: Ad, credential: LbcCredential) -> bool; } From d9cb49b422fae09d47b457f91d2ddb115e9ef603 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 23 Mar 2023 22:55:14 +0100 Subject: [PATCH 039/150] MetadataCollectingWidget: Add deep copy to fix bug where manual blurb is mirror to Babelio and stay to its old value when submiting --- lib/ad_editing.dart | 5 ++++- lib/common.dart | 5 +++++ lib/metadata_collecting.dart | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index d93c739..d2b0598 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -129,7 +129,10 @@ class _AdEditingWidgetState extends State { style: const TextStyle(fontSize: 20), ), ElevatedButton( - onPressed: (ad.title.length < 2 || ad.description.length < 15 || ad.priceCent == null) + onPressed: (ad.title.length < 2 || + ad.description.length < 15 || + ad.description.length > 4000 || + ad.priceCent == null) ? null : () async { print('Try to publish...'); diff --git a/lib/common.dart b/lib/common.dart index a1dd726..be0a498 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -59,3 +59,8 @@ extension IntExt on int { extension DoubleExt on double { double multiply(double other) => this * other; } + +extension BookMetadataExt on BookMetaData { + BookMetaData deepCopy() => + BookMetaData(title: '$title', authors: List.from(authors), blurb: '$blurb', keywords: List.from(keywords)); +} diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 9452a69..8eea2b9 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -37,7 +37,7 @@ class _MetadataCollectingWidgetState extends State { if (provider == ProviderEnum.Babelio) { md.then((value) { if (value != null) { - metadata[isbn]!.manual = value; + metadata[isbn]!.manual = value.deepCopy(); } }); } @@ -117,7 +117,7 @@ class _MetadataCollectingWidgetState extends State { future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( initialValue: data?.blurb, - onChanged: (newText) => setState(() => manual.blurb = newText), + onChanged: (newText) => setState(() => metadata[isbn]!.manual.blurb = newText), maxLines: null, decoration: const InputDecoration( icon: Icon(Icons.description), From 5f9cc9dbb75c64d5de31f08d997df139676ef293 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 24 Mar 2023 00:16:33 +0100 Subject: [PATCH 040/150] MetadataCollectingWidget: add controller and SelectableTextAndUse for blurb only --- lib/metadata_collecting.dart | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 8eea2b9..c64b718 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -6,11 +6,29 @@ import 'main.dart'; const noneText = Text('None', style: TextStyle(fontStyle: FontStyle.italic)); +class SelectableTextAndUse extends StatelessWidget { + const SelectableTextAndUse(this.s, {required this.onUse}); + final String s; + final void Function(String) onUse; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TextButton(onPressed: () => onUse(s), child: const Text('Use')), + SelectableText(s), + ], + ); + } +} + class MetadataCollectingWidget extends StatefulWidget { - const MetadataCollectingWidget({required this.step, required this.onSubmit}); + MetadataCollectingWidget({required this.step, required this.onSubmit}); final MetadataCollectingStep step; final void Function(AdEditingStep newStep) onSubmit; + final blurbTextFieldController = TextEditingController(); + @override State createState() => _MetadataCollectingWidgetState(); } @@ -38,6 +56,7 @@ class _MetadataCollectingWidgetState extends State { md.then((value) { if (value != null) { metadata[isbn]!.manual = value.deepCopy(); + widget.blurbTextFieldController.text = metadata[isbn]!.manual.blurb ?? ''; } }); } @@ -116,7 +135,7 @@ class _MetadataCollectingWidgetState extends State { FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data?.blurb, + controller: widget.blurbTextFieldController, onChanged: (newText) => setState(() => metadata[isbn]!.manual.blurb = newText), maxLines: null, decoration: const InputDecoration( @@ -131,7 +150,13 @@ class _MetadataCollectingWidgetState extends State { if (blurb == null) { return noneText; } - return SelectableText(blurb); + return SelectableTextAndUse( + blurb, + onUse: (b) => setState(() { + widget.blurbTextFieldController.text = b; + metadata[isbn]!.manual.blurb = b; + }), + ); })), ]), ], From aaffa8745eb6277644e94e467b671e579f664920 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 24 Mar 2023 09:07:22 +0100 Subject: [PATCH 041/150] Replace title from provider if better --- lib/metadata_collecting.dart | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index c64b718..969689a 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -28,6 +28,7 @@ class MetadataCollectingWidget extends StatefulWidget { final void Function(AdEditingStep newStep) onSubmit; final blurbTextFieldController = TextEditingController(); + final titleTextFieldController = TextEditingController(); @override State createState() => _MetadataCollectingWidgetState(); @@ -42,6 +43,18 @@ class Metadatas { class _MetadataCollectingWidgetState extends State { Map metadata = {}; + void replaceIfBetterString(String? providerStr, String manualStr, void Function() onReplace) { + if (providerStr == null || manualStr.length > providerStr.length) return; + onReplace(); + } + + void _updateManualTitle(String isbn, String newTitle) { + setState(() { + metadata[isbn]!.manual.title = newTitle; + widget.titleTextFieldController.text = newTitle; + }); + } + @override void initState() { super.initState(); @@ -52,14 +65,13 @@ class _MetadataCollectingWidgetState extends State { manual: BookMetaData(title: '', authors: [], keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); - if (provider == ProviderEnum.Babelio) { - md.then((value) { - if (value != null) { - metadata[isbn]!.manual = value.deepCopy(); - widget.blurbTextFieldController.text = metadata[isbn]!.manual.blurb ?? ''; - } - }); - } + md.then((value) { + if (value != null) { + replaceIfBetterString(value.title, metadata[isbn]!.manual.title!, () { + _updateManualTitle(isbn, value.title!); + }); + } + }); return MapEntry(provider, md); })))); }); @@ -95,7 +107,7 @@ class _MetadataCollectingWidgetState extends State { FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data?.title, + controller: widget.titleTextFieldController, onChanged: (newText) => setState(() => manual.title = newText), decoration: const InputDecoration( icon: Icon(Icons.title), From 40c1130287f3982a809c88e708d25b0ffff64df4 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 24 Mar 2023 09:12:47 +0100 Subject: [PATCH 042/150] Replace blurb from provider if better --- lib/metadata_collecting.dart | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 969689a..73cca4c 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -55,6 +55,13 @@ class _MetadataCollectingWidgetState extends State { }); } + void _updateManualBlurb(String isbn, String newBlurb) { + setState(() { + metadata[isbn]!.manual.blurb = newBlurb; + widget.blurbTextFieldController.text = newBlurb; + }); + } + @override void initState() { super.initState(); @@ -62,7 +69,7 @@ class _MetadataCollectingWidgetState extends State { metadata.putIfAbsent( isbn, () => Metadatas( - manual: BookMetaData(title: '', authors: [], keywords: []), + manual: BookMetaData(title: '', authors: [], blurb: '', keywords: []), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); md.then((value) { @@ -70,6 +77,9 @@ class _MetadataCollectingWidgetState extends State { replaceIfBetterString(value.title, metadata[isbn]!.manual.title!, () { _updateManualTitle(isbn, value.title!); }); + replaceIfBetterString(value.blurb, metadata[isbn]!.manual.blurb!, () { + _updateManualBlurb(isbn, value.blurb!); + }); } }); return MapEntry(provider, md); @@ -164,10 +174,7 @@ class _MetadataCollectingWidgetState extends State { } return SelectableTextAndUse( blurb, - onUse: (b) => setState(() { - widget.blurbTextFieldController.text = b; - metadata[isbn]!.manual.blurb = b; - }), + onUse: (b) => _updateManualBlurb(isbn, b), ); })), ]), From ed5096ae5c85345bf7ffbcf44f37136f23f055f7 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 24 Mar 2023 22:41:44 +0100 Subject: [PATCH 043/150] Store LBC credential in credential.json --- .gitignore | 1 + analysis_options.yaml | 6 +++++- lib/ad_editing.dart | 26 +++++++++++++++++++------- lib/credential.dart | 29 +++++++++++++++++++++++++++++ lib/credential.g.dart | 18 ++++++++++++++++++ pubspec.yaml | 3 +++ 6 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 lib/credential.dart create mode 100644 lib/credential.g.dart diff --git a/.gitignore b/.gitignore index 38d2398..c9e953d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ /build/ personal_info.* +credential.json # Web related diff --git a/analysis_options.yaml b/analysis_options.yaml index 8529660..456dfc6 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -12,4 +12,8 @@ analyzer: enable-experiment: - records - patterns - - sealed-class \ No newline at end of file + - sealed-class + language: + strict-casts: true + strict-inference: true + strict-raw-types: true \ No newline at end of file diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index d2b0598..12b4821 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -3,6 +3,7 @@ import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; import 'common.dart'; +import 'credential.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; class AdEditingWidget extends StatefulWidget { @@ -28,7 +29,7 @@ String _bookFormatTitleAndAuthor(BookMetaData book) { class _AdEditingWidgetState extends State { late Ad ad; - var credential = LbcCredential(lbcToken: '', datadomeCookie: ''); + late Credential credential; @override void initState() { @@ -46,6 +47,9 @@ class _AdEditingWidgetState extends State { } ad = Ad(title: title, description: description, priceCent: 1000, imgsPath: widget.step.imgsPaths); + + credential = Credential.loadFromFile(); + print('credential ${credential.lbcToken} ${credential.dataDomeCookie}'); } String _getDescription(Iterable> metadataFromIsbn) { @@ -111,7 +115,7 @@ class _AdEditingWidgetState extends State { ]), ), TextFormField( - initialValue: '', + initialValue: credential.lbcToken, onChanged: (newText) => setState(() => credential.lbcToken = newText), decoration: const InputDecoration( icon: Icon(Icons.key), @@ -120,8 +124,8 @@ class _AdEditingWidgetState extends State { style: const TextStyle(fontSize: 20), ), TextFormField( - initialValue: '', - onChanged: (newText) => setState(() => credential.datadomeCookie = newText), + initialValue: credential.dataDomeCookie, + onChanged: (newText) => setState(() => credential.dataDomeCookie = newText), decoration: const InputDecoration( icon: Icon(Icons.cookie), labelText: 'datadome cookie', @@ -136,11 +140,19 @@ class _AdEditingWidgetState extends State { ? null : () async { print('Try to publish...'); - final res = await api.publishAd(ad: ad, credential: credential); + + final res = await api.publishAd( + ad: ad, + credential: LbcCredential( + lbcToken: credential.lbcToken, datadomeCookie: credential.dataDomeCookie)); if (!context.mounted) return; - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text(res ? 'Success' : 'Failure'))); + if (res) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Success'))); + credential.saveToFile(); + } else { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Failure'))); + } }, child: const Text('Publish')) ], diff --git a/lib/credential.dart b/lib/credential.dart new file mode 100644 index 0000000..b7790ac --- /dev/null +++ b/lib/credential.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:json_annotation/json_annotation.dart'; + +part 'credential.g.dart'; + +@JsonSerializable() +class Credential { + String lbcToken; + String dataDomeCookie; + + Credential({required this.lbcToken, required this.dataDomeCookie}); + + static final _file = File('credential.json'); + + void saveToFile() { + _file.writeAsStringSync(jsonEncode(toJson())); + } + + factory Credential.loadFromFile() { + final json = _file.readAsStringSync(); + return Credential.fromJson(jsonDecode(json) as Map); + } + + factory Credential.fromJson(Map json) => _$CredentialFromJson(json); + + Map toJson() => _$CredentialToJson(this); +} diff --git a/lib/credential.g.dart b/lib/credential.g.dart new file mode 100644 index 0000000..8a8e092 --- /dev/null +++ b/lib/credential.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'credential.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Credential _$CredentialFromJson(Map json) => Credential( + lbcToken: json['lbcToken'] as String, + dataDomeCookie: json['dataDomeCookie'] as String, + ); + +Map _$CredentialToJson(Credential instance) => + { + 'lbcToken': instance.lbcToken, + 'dataDomeCookie': instance.dataDomeCookie, + }; diff --git a/pubspec.yaml b/pubspec.yaml index 66d5678..1ccfdf5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,12 +23,15 @@ dependencies: super_drag_and_drop: ^0.2.3 collection: ^1.17.1 super_clipboard: ^0.2.3+1 + json_annotation: ^4.8.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 ffigen: ^7.2.7 + build_runner: ^2.4.1 + json_serializable: ^6.6.1 flutter: uses-material-design: true From 5a8583f87ac9d364674e7690b5ca7716c17e6b41 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 25 Mar 2023 15:11:04 +0100 Subject: [PATCH 044/150] Add jwt token validator --- lib/ad_editing.dart | 13 ++++++++++++- lib/main.dart | 8 ++++++++ pubspec.yaml | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 12b4821..4291583 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; +import 'package:jwt_decoder/jwt_decoder.dart'; import 'common.dart'; import 'credential.dart'; @@ -54,7 +55,9 @@ class _AdEditingWidgetState extends State { String _getDescription(Iterable> metadataFromIsbn) { if (metadataFromIsbn.length == 1) { - return 'Résumé:\n' + metadataFromIsbn.single.value.blurb!; + final blurb = metadataFromIsbn.single.value.blurb; + if (blurb == null) return ''; + return 'Résumé:\n' + blurb; } else { final bookTitles = metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); final blurbs = metadataFromIsbn @@ -122,6 +125,14 @@ class _AdEditingWidgetState extends State { labelText: 'LBC Bearer token', ), style: const TextStyle(fontSize: 20), + autovalidateMode: AutovalidateMode.always, + validator: (token) { + final remainingDuration = JwtDecoder.getRemainingTime(token!); + if (remainingDuration.isNegative) { + return 'Token expired'; + } + return null; + }, ), TextFormField( initialValue: credential.dataDomeCookie, diff --git a/lib/main.dart b/lib/main.dart index ffa9ced..23bfee3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -41,6 +41,14 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { BookyStep step = ImageSelectionStep(); + /* AdEditingStep(imgsPaths: [ + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg' + ], metadata: { + 'myisbn': BookMetaData( + title: 'Mock title', + authors: [Author(firstName: 'Mock firstname', lastName: 'mock lastname')], + keywords: ['mock kw']) + });*/ /* MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', diff --git a/pubspec.yaml b/pubspec.yaml index 1ccfdf5..dd525bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: collection: ^1.17.1 super_clipboard: ^0.2.3+1 json_annotation: ^4.8.0 + jwt_decoder: ^2.0.1 dev_dependencies: flutter_test: From dca23c101cf2bca3b4124a8ed4ce463309fcf5c5 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 27 Mar 2023 22:37:36 +0200 Subject: [PATCH 045/150] Add bookprices with basic example of Selenium --- native/Cargo.toml | 6 +++- native/src/booksprice.rs | 2 ++ native/src/booksprice/request.rs | 52 ++++++++++++++++++++++++++++++++ native/src/lib.rs | 1 + 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 native/src/booksprice.rs create mode 100644 native/src/booksprice/request.rs diff --git a/native/Cargo.toml b/native/Cargo.toml index 29de5cb..082fd8f 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -11,7 +11,6 @@ crate-type = ["cdylib", "staticlib"] [dependencies] anyhow = "1" flutter_rust_bridge = "1" -#reqwest = "0.11.14" base64 = "0.21.0" itertools = "0.10.5" regex = "1.7.1" @@ -20,3 +19,8 @@ scraper = "0.14.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.91" mockito = "1.0.0" +thirtyfour = "0.31.0" +tokio = { version = "1.20", features = ["fs", "macros", "rt-multi-thread", "io-util", "sync"] } + +[dev-dependencies] +color-eyre = "0.6.2" \ No newline at end of file diff --git a/native/src/booksprice.rs b/native/src/booksprice.rs new file mode 100644 index 0000000..c6a30a3 --- /dev/null +++ b/native/src/booksprice.rs @@ -0,0 +1,2 @@ +mod request; + diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs new file mode 100644 index 0000000..a98e15e --- /dev/null +++ b/native/src/booksprice/request.rs @@ -0,0 +1,52 @@ +//! Requires chromedriver running on port 9515: +//! +//! chromedriver --port=9515 +//! +//! Run as follows: +//! +//! cargo run --example tokio_async + +use thirtyfour::prelude::*; +use tokio; + +#[tokio::main] +async fn selenium_fn() -> color_eyre::Result<()> { + // The use of color_eyre gives much nicer error reports, including making + // it much easier to locate where the error occurred. + color_eyre::install()?; + + let caps = DesiredCapabilities::chrome(); + let driver = WebDriver::new("http://localhost:9515", caps).await?; + // Navigate to https://wikipedia.org. + driver.goto("https://wikipedia.org").await?; + let elem_form = driver.find(By::Id("search-form")).await?; + + // Find element from element. + let elem_text = elem_form.find(By::Id("searchInput")).await?; + + // Type in the search terms. + elem_text.send_keys("selenium").await?; + + // Click the search button. + let elem_button = elem_form.find(By::Css("button[type='submit']")).await?; + elem_button.click().await?; + + // Look for header to implicitly wait for the page to load. + driver.find(By::ClassName("firstHeading")).await?; + assert_eq!(driver.title().await?, "Selenium - Wikipedia"); + + // Always explicitly close the browser. There are no async destructors. + driver.quit().await?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_selenium() { + selenium_fn(); + } +} diff --git a/native/src/lib.rs b/native/src/lib.rs index c8c5bf4..73cb00d 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -9,3 +9,4 @@ mod image_tools; mod jwt_decoder; mod leboncoin; mod publisher; +mod booksprice; From 2c95d480f2881fd5e7d61bcf96aa75c696b1bb33 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 29 Mar 2023 22:37:35 +0200 Subject: [PATCH 046/150] WIP Selenium offline test work --- native/Cargo.toml | 5 +- native/src/booksprice.rs | 1 + native/src/booksprice/request.rs | 41 +- native/src/booksprice/selenium_common.rs | 209 +++++++++ native/src/lib.rs | 3 + native/tests/test_html/output_bookprice.html | 424 +++++++++++++++++++ native/tests/test_html/sample_page.html | 1 + 7 files changed, 675 insertions(+), 9 deletions(-) create mode 100644 native/src/booksprice/selenium_common.rs create mode 100644 native/tests/test_html/output_bookprice.html create mode 120000 native/tests/test_html/sample_page.html diff --git a/native/Cargo.toml b/native/Cargo.toml index 082fd8f..6a79611 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -22,5 +22,6 @@ mockito = "1.0.0" thirtyfour = "0.31.0" tokio = { version = "1.20", features = ["fs", "macros", "rt-multi-thread", "io-util", "sync"] } -[dev-dependencies] -color-eyre = "0.6.2" \ No newline at end of file +# [dev-dependencies] +color-eyre = "0.6.2" +hyper = { version = "0.14", features = ["server", "tcp"] } diff --git a/native/src/booksprice.rs b/native/src/booksprice.rs index c6a30a3..a5f8510 100644 --- a/native/src/booksprice.rs +++ b/native/src/booksprice.rs @@ -1,2 +1,3 @@ mod request; +mod selenium_common; diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index a98e15e..8c35315 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -1,24 +1,27 @@ //! Requires chromedriver running on port 9515: //! //! chromedriver --port=9515 -//! -//! Run as follows: -//! -//! cargo run --example tokio_async use thirtyfour::prelude::*; use tokio; +// mod selenium_common; + +// use crate::common; +use crate::booksprice::selenium_common::sample_page_url; + #[tokio::main] async fn selenium_fn() -> color_eyre::Result<()> { // The use of color_eyre gives much nicer error reports, including making // it much easier to locate where the error occurred. color_eyre::install()?; + // thirtyfour::resolve!(); + // crate::local_tester!(); + let caps = DesiredCapabilities::chrome(); let driver = WebDriver::new("http://localhost:9515", caps).await?; - // Navigate to https://wikipedia.org. - driver.goto("https://wikipedia.org").await?; + driver.goto("https://www.booksprice.com/comparePrice.do?l=y&searchType=compare&inputData=9782266071529").await?; let elem_form = driver.find(By::Id("search-form")).await?; // Find element from element. @@ -41,12 +44,36 @@ async fn selenium_fn() -> color_eyre::Result<()> { Ok(()) } +async fn parse_booksprices(c: WebDriver, port: u16) -> Result<(), WebDriverError> { + let url = sample_page_url(port); + + c.goto(&url).await?; + println!("{:#?}", c.source().await); + c.find(By::Css("#select1")).await?.click().await?; + + let active = c.active_element().await?; + assert_eq!(active.attr("id").await?, Some(String::from("select1"))); + + c.close_window().await +} + #[cfg(test)] mod tests { + use crate::booksprice::selenium_common::handle_test_error; + use crate::booksprice::selenium_common::make_capabilities; + use crate::booksprice::selenium_common::make_url; + use crate::booksprice::selenium_common::setup_server; + use crate::{local_tester, tester_inner}; + use super::*; + // #[test] + /* fn test_selenium() { + selenium_fn(); + } */ + #[test] fn test_selenium() { - selenium_fn(); + local_tester!(parse_booksprices, "chrome"); } } diff --git a/native/src/booksprice/selenium_common.rs b/native/src/booksprice/selenium_common.rs new file mode 100644 index 0000000..5a85212 --- /dev/null +++ b/native/src/booksprice/selenium_common.rs @@ -0,0 +1,209 @@ +#![allow(dead_code)] +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Request, Response, Server, StatusCode}; +use std::convert::Infallible; +use std::future::Future; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::path::Path; +use thirtyfour::prelude::*; +use tokio::fs::read_to_string; + +const ASSETS_DIR: &str = "tests/test_html"; + +pub fn make_capabilities(s: &str) -> Capabilities { + match s { + "firefox" => { + let mut caps = DesiredCapabilities::firefox(); + caps.set_headless().unwrap(); + caps.into() + } + "chrome" => { + let mut caps = DesiredCapabilities::chrome(); + caps.set_headless().unwrap(); + caps.set_no_sandbox().unwrap(); + caps.set_disable_gpu().unwrap(); + caps.set_disable_dev_shm_usage().unwrap(); + caps.into() + } + browser => unimplemented!("unsupported browser backend {}", browser), + } +} + +pub fn make_url(s: &str) -> &'static str { + match s { + "firefox" => "http://localhost:4444", + "chrome" => "http://localhost:9515", + browser => unimplemented!("unsupported browser backend {}", browser), + } +} + +pub fn handle_test_error( + res: Result, Box>, +) -> bool { + match res { + Ok(Ok(_)) => true, + Ok(Err(e)) => { + eprintln!("test future failed to resolve: {:?}", e); + false + } + Err(e) => { + if let Some(e) = e.downcast_ref::() { + eprintln!("test future panicked: {:?}", e); + } else { + eprintln!("test future panicked; an assertion probably failed"); + } + false + } + } +} + +#[macro_export] +macro_rules! tester { + ($f:ident, $endpoint:expr) => {{ + use common::{make_capabilities, make_url}; + let url = make_url($endpoint); + let caps = make_capabilities($endpoint); + tester_inner!($f, WebDriver::new(url, caps)); + }}; +} + +#[macro_export] +macro_rules! tester_inner { + ($f:ident, $connector:expr) => {{ + use std::sync::{Arc, Mutex}; + use std::thread; + + let c = $connector; + + // we'll need the session_id from the thread + // NOTE: even if it panics, so can't just return it + // let session_id = Arc::new(Mutex::new(None)); + + // run test in its own thread to catch panics + // let sid = session_id.clone(); + let res = thread::spawn(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + let c = rt.block_on(c).expect("failed to construct test WebDriver"); + // let _sid = c.session_id().clone(); + // *sid.lock().unwrap() = Some(_sid); + // make sure we close, even if an assertion fails + let client = c.clone(); + let x = rt.block_on(async move { + let r = tokio::spawn($f(c)).await; + let _ = client.quit().await; + r + }); + drop(rt); + x.expect("test panicked") + }) + .join(); + let success = handle_test_error(res); + assert!(success); + }}; +} + +#[macro_export] +macro_rules! local_tester { + ($f:ident, $endpoint:expr) => {{ + use thirtyfour::prelude::*; + + let port = setup_server(); + let url = make_url($endpoint); + let caps = make_capabilities($endpoint); + let f = move |c: WebDriver| async move { $f(c, port).await }; + tester_inner!(f, WebDriver::new(url, caps)); + }}; +} + +/// Sets up the server and returns the port it bound to. +pub fn setup_server() -> u16 { + let (tx, rx) = std::sync::mpsc::channel(); + + std::thread::spawn(move || { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + let _ = rt.block_on(async { + let (socket_addr, server) = start_server(); + tx.send(socket_addr.port()) + .expect("To be able to send port"); + server.await.expect("To start the server") + }); + }); + + rx.recv().expect("To get the bound port.") +} + +/// Configures and starts the server +fn start_server() -> ( + SocketAddr, + impl Future> + 'static, +) { + let socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); + + let server = Server::bind(&socket_addr).serve(make_service_fn(move |_| async { + Ok::<_, Infallible>(service_fn(handle_file_request)) + })); + + let addr = server.local_addr(); + (addr, server) +} + +/// Tries to return the requested html file +async fn handle_file_request(req: Request) -> Result, Infallible> { + let uri_path = req.uri().path().trim_matches(&['/', '\\'][..]); + + // tests only contain html files + // needed because the content-type: text/html is returned + if !uri_path.ends_with(".html") { + return Ok(file_not_found()); + } + + // this does not protect against a directory traversal attack + // but in this case it's not a risk + let asset_file = Path::new(ASSETS_DIR).join(uri_path); + + let ctn = match read_to_string(&asset_file).await { + Ok(ctn) => ctn, + Err(err) => { + eprintln!( + "could not find file {:#?}, error is {}", + asset_file.to_str(), + err + ); + return Ok(file_not_found()); + } + }; + + let res = Response::builder() + .header("content-type", "text/html") + .header("content-length", ctn.len()) + .body(ctn.into()) + .unwrap(); + + Ok(res) +} + +/// Response returned when a file is not found or could not be read +fn file_not_found() -> Response { + Response::builder() + .status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .unwrap() +} + +pub fn sample_page_url(port: u16) -> String { + format!("http://localhost:{}/sample_page.html", port) +} + +pub fn other_page_url(port: u16) -> String { + format!("http://localhost:{}/other_page.html", port) +} + +pub fn drag_to_url(port: u16) -> String { + format!("http://localhost:{}/drag_to.html", port) +} diff --git a/native/src/lib.rs b/native/src/lib.rs index 73cb00d..691c70e 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -1,3 +1,6 @@ + +// #[macro_use] +// extern crate thirtyfour; mod api; mod babelio; mod bridge_generated; diff --git a/native/tests/test_html/output_bookprice.html b/native/tests/test_html/output_bookprice.html new file mode 100644 index 0000000..55071ca --- /dev/null +++ b/native/tests/test_html/output_bookprice.html @@ -0,0 +1,424 @@ + +2884747974 Book Price Comparison + + + + + + + + + + + + +
+
+
BooksPrice.com

book price comparison

+
+

 

+
+
+ +
+
+
+
+

Histoire d'une maison

+

+Viollet-le-Duc, Eugène-Emmanuel Bressani, Martin  +

+

+9782884747974 / Paperback + + + / INFOLIO + +

+ + +

+ +
+
+ +
Set Email Price Alert + + + + +
+ +
+ +
+ + +
+
+ + + + +
+ + +
+ +
+
+ + +
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Store NameConditionTermPriceShippingTotal Price Go to Store (#ad)
+
  +New + [+] + + $16.55Free + + $ + 16.55 + + + + + + + + + Buy at +
+ +Abebooks
  +New + + $17.86$3.99 $ + 21.85 + + + + + + + + + Buy at +
+ +Amazon
  +New + + $19.76$3.99 $ + 23.75 + + + + + + + + + Buy at +
+ +Amazon
  +Used (Very Good) + + $27.17Free + + $ + 27.17 + + + + + + + + + Buy at +
+ +Amazon
  +Used (Mint) + + $28.15Free + + $ + 28.15 + + + + + + + + + Buy at +
+ +Amazon
  +Used (Very Good) + [+] + + $10.97$32.23 $ + 43.20 + + + + + + + + + Buy at +
+ +Abebooks
+ +
+
+ + + +
+ + + + + +
+

* Shipping cost is an estimate. Please check the +shipping cost at the site before making the purchase!

+

The prices shown may have risen since the time we +last updated them.
+The actual price of the product on the sellers site at the time of +purchase will govern the sale.
+It is not technically possible for the prices displayed above to be +updated in real-time.

+
+
+
+ +
 
+
+ + + + + + + + + + +. + + \ No newline at end of file diff --git a/native/tests/test_html/sample_page.html b/native/tests/test_html/sample_page.html new file mode 120000 index 0000000..3c2a2e7 --- /dev/null +++ b/native/tests/test_html/sample_page.html @@ -0,0 +1 @@ +output_bookprice.html \ No newline at end of file From 398cac936b620071f1794a89c4dbf3fcfc296241 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 29 Mar 2023 23:32:03 +0200 Subject: [PATCH 047/150] WIP --- native/src/booksprice/request.rs | 32 +++++++++++------------- native/src/booksprice/selenium_common.rs | 14 +++-------- native/tests/test_html/sample_page.html | 1 - 3 files changed, 17 insertions(+), 30 deletions(-) delete mode 120000 native/tests/test_html/sample_page.html diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index 8c35315..f508d8f 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -8,7 +8,6 @@ use tokio; // mod selenium_common; // use crate::common; -use crate::booksprice::selenium_common::sample_page_url; #[tokio::main] async fn selenium_fn() -> color_eyre::Result<()> { @@ -44,33 +43,30 @@ async fn selenium_fn() -> color_eyre::Result<()> { Ok(()) } -async fn parse_booksprices(c: WebDriver, port: u16) -> Result<(), WebDriverError> { - let url = sample_page_url(port); - - c.goto(&url).await?; - println!("{:#?}", c.source().await); - c.find(By::Css("#select1")).await?.click().await?; - - let active = c.active_element().await?; - assert_eq!(active.attr("id").await?, Some(String::from("select1"))); - - c.close_window().await -} - #[cfg(test)] mod tests { + use crate::booksprice::selenium_common; use crate::booksprice::selenium_common::handle_test_error; use crate::booksprice::selenium_common::make_capabilities; use crate::booksprice::selenium_common::make_url; use crate::booksprice::selenium_common::setup_server; + use crate::{local_tester, tester_inner}; use super::*; - // #[test] - /* fn test_selenium() { - selenium_fn(); - } */ + async fn parse_booksprices(c: WebDriver, port: u16) -> Result<(), WebDriverError> { + let url = selenium_common::url_from_path(port, "output_bookprice.html"); + + c.goto(&url).await?; + println!("{:#?}", c.source().await); + c.find(By::Css("#select1")).await?.click().await?; + + let active = c.active_element().await?; + assert_eq!(active.attr("id").await?, Some(String::from("select1"))); + + c.close_window().await + } #[test] fn test_selenium() { diff --git a/native/src/booksprice/selenium_common.rs b/native/src/booksprice/selenium_common.rs index 5a85212..e537a40 100644 --- a/native/src/booksprice/selenium_common.rs +++ b/native/src/booksprice/selenium_common.rs @@ -70,7 +70,7 @@ macro_rules! tester { #[macro_export] macro_rules! tester_inner { ($f:ident, $connector:expr) => {{ - use std::sync::{Arc, Mutex}; + // use std::sync::{Arc, Mutex}; use std::thread; let c = $connector; @@ -196,14 +196,6 @@ fn file_not_found() -> Response { .unwrap() } -pub fn sample_page_url(port: u16) -> String { - format!("http://localhost:{}/sample_page.html", port) -} - -pub fn other_page_url(port: u16) -> String { - format!("http://localhost:{}/other_page.html", port) -} - -pub fn drag_to_url(port: u16) -> String { - format!("http://localhost:{}/drag_to.html", port) +pub fn url_from_path(port: u16, path: &str) -> String { + format!("http://localhost:{}/{}", port, path) } diff --git a/native/tests/test_html/sample_page.html b/native/tests/test_html/sample_page.html deleted file mode 120000 index 3c2a2e7..0000000 --- a/native/tests/test_html/sample_page.html +++ /dev/null @@ -1 +0,0 @@ -output_bookprice.html \ No newline at end of file From 3098a29de2f82d4d1cae288e79fcd763ab79d4f8 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 1 Apr 2023 16:19:10 +0200 Subject: [PATCH 048/150] Parse booksPrice works --- native/Cargo.toml | 1 + native/src/booksprice/request.rs | 36 +++++++++++++++---- ...tput_bookprice.html => 9782884747974.html} | 0 3 files changed, 31 insertions(+), 6 deletions(-) rename native/tests/test_html/{output_bookprice.html => 9782884747974.html} (100%) diff --git a/native/Cargo.toml b/native/Cargo.toml index 6a79611..1f31af6 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -25,3 +25,4 @@ tokio = { version = "1.20", features = ["fs", "macros", "rt-multi-thread", "io-u # [dev-dependencies] color-eyre = "0.6.2" hyper = { version = "0.14", features = ["server", "tcp"] } +futures = "0.3.28" diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index f508d8f..d0ebb98 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -45,6 +45,9 @@ async fn selenium_fn() -> color_eyre::Result<()> { #[cfg(test)] mod tests { + use itertools::Itertools; + use tokio::try_join; + use crate::booksprice::selenium_common; use crate::booksprice::selenium_common::handle_test_error; use crate::booksprice::selenium_common::make_capabilities; @@ -56,14 +59,35 @@ mod tests { use super::*; async fn parse_booksprices(c: WebDriver, port: u16) -> Result<(), WebDriverError> { - let url = selenium_common::url_from_path(port, "output_bookprice.html"); + let url = selenium_common::url_from_path(port, "9782884747974.html"); c.goto(&url).await?; - println!("{:#?}", c.source().await); - c.find(By::Css("#select1")).await?.click().await?; - - let active = c.active_element().await?; - assert_eq!(active.attr("id").await?, Some(String::from("select1"))); + // println!("{:#?}", c.source().await); + let entries = c + .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) + .await?; + assert_eq!(entries.len(), 6); + use futures::future::{self, try_join_all}; + + let prices = try_join_all(entries.iter().map(|e| async { + let price_text = e + .find(By::XPath("td[@title='Total']/a/em")) + .await + .unwrap() + .text() + .await; + + price_text.map(|price_text| { + use regex::Regex; + let re = Regex::new(r"\$ (\d+\.?\d+)").unwrap(); + let r = re.captures(&price_text).unwrap(); + r.get(1).unwrap().as_str().parse::().unwrap() + }) + })) + .await + .unwrap(); + + assert_eq!(prices, vec![16.55, 21.85, 23.75, 27.17, 28.15, 43.20]); c.close_window().await } diff --git a/native/tests/test_html/output_bookprice.html b/native/tests/test_html/9782884747974.html similarity index 100% rename from native/tests/test_html/output_bookprice.html rename to native/tests/test_html/9782884747974.html From ad31bd2ff972581a25d3db544263048eac2eedcf Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 1 Apr 2023 16:25:51 +0200 Subject: [PATCH 049/150] clean --- native/src/booksprice/request.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index d0ebb98..0668849 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -45,9 +45,6 @@ async fn selenium_fn() -> color_eyre::Result<()> { #[cfg(test)] mod tests { - use itertools::Itertools; - use tokio::try_join; - use crate::booksprice::selenium_common; use crate::booksprice::selenium_common::handle_test_error; use crate::booksprice::selenium_common::make_capabilities; @@ -62,14 +59,12 @@ mod tests { let url = selenium_common::url_from_path(port, "9782884747974.html"); c.goto(&url).await?; - // println!("{:#?}", c.source().await); let entries = c .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) .await?; assert_eq!(entries.len(), 6); - use futures::future::{self, try_join_all}; - let prices = try_join_all(entries.iter().map(|e| async { + let prices = futures::future::try_join_all(entries.iter().map(|e| async { let price_text = e .find(By::XPath("td[@title='Total']/a/em")) .await From cee744c7cbe32e1c4a567d9fd6f5f6f9aba914ec Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 3 Apr 2023 20:41:47 +0200 Subject: [PATCH 050/150] Extract selenium POC into useful function --- native/src/booksprice/request.rs | 113 ++++++++++++++----------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index 0668849..728a74b 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -5,47 +5,55 @@ use thirtyfour::prelude::*; use tokio; -// mod selenium_common; - -// use crate::common; - #[tokio::main] -async fn selenium_fn() -> color_eyre::Result<()> { - // The use of color_eyre gives much nicer error reports, including making - // it much easier to locate where the error occurred. - color_eyre::install()?; - - // thirtyfour::resolve!(); - // crate::local_tester!(); - +async fn extract_price_from_isbn( + isbn: &str, +) -> Result, thirtyfour::prelude::WebDriverError> { let caps = DesiredCapabilities::chrome(); let driver = WebDriver::new("http://localhost:9515", caps).await?; - driver.goto("https://www.booksprice.com/comparePrice.do?l=y&searchType=compare&inputData=9782266071529").await?; - let elem_form = driver.find(By::Id("search-form")).await?; - - // Find element from element. - let elem_text = elem_form.find(By::Id("searchInput")).await?; - - // Type in the search terms. - elem_text.send_keys("selenium").await?; - // Click the search button. - let elem_button = elem_form.find(By::Css("button[type='submit']")).await?; - elem_button.click().await?; - - // Look for header to implicitly wait for the page to load. - driver.find(By::ClassName("firstHeading")).await?; - assert_eq!(driver.title().await?, "Selenium - Wikipedia"); - - // Always explicitly close the browser. There are no async destructors. - driver.quit().await?; + extract_price_from_url( + driver, + &format!( + "https://www.booksprice.com/comparePrice.do?l=y&searchType=compare&inputData={}", + isbn + ), + ) + .await +} - Ok(()) +async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, WebDriverError> { + c.goto(&url).await?; + let entries = c + .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) + .await?; + assert_eq!(entries.len(), 6); + + let prices = futures::future::try_join_all(entries.iter().map(|e| async { + let price_text = e + .find(By::XPath("td[@title='Total']/a/em")) + .await + .unwrap() + .text() + .await; + + price_text.map(|price_text| { + use regex::Regex; + let re = Regex::new(r"\$ (\d+\.?\d+)").unwrap(); + let r = re.captures(&price_text).unwrap(); + r.get(1).unwrap().as_str().parse::().unwrap() + }) + })) + .await + .unwrap(); + + c.close_window().await; + + Ok(prices) } #[cfg(test)] mod tests { - use crate::booksprice::selenium_common; use crate::booksprice::selenium_common::handle_test_error; use crate::booksprice::selenium_common::make_capabilities; use crate::booksprice::selenium_common::make_url; @@ -55,40 +63,23 @@ mod tests { use super::*; - async fn parse_booksprices(c: WebDriver, port: u16) -> Result<(), WebDriverError> { - let url = selenium_common::url_from_path(port, "9782884747974.html"); - - c.goto(&url).await?; - let entries = c - .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) - .await?; - assert_eq!(entries.len(), 6); - - let prices = futures::future::try_join_all(entries.iter().map(|e| async { - let price_text = e - .find(By::XPath("td[@title='Total']/a/em")) - .await - .unwrap() - .text() - .await; - - price_text.map(|price_text| { - use regex::Regex; - let re = Regex::new(r"\$ (\d+\.?\d+)").unwrap(); - let r = re.captures(&price_text).unwrap(); - r.get(1).unwrap().as_str().parse::().unwrap() - }) - })) - .await - .unwrap(); + async fn parse_booksprices_from_9782884747974( + c: WebDriver, + port: u16, + ) -> Result<(), WebDriverError> { + use crate::booksprice::selenium_common; - assert_eq!(prices, vec![16.55, 21.85, 23.75, 27.17, 28.15, 43.20]); + let prices = extract_price_from_url( + c, + &selenium_common::url_from_path(port, "9782884747974.html"), + ).await.unwrap(); - c.close_window().await + assert_eq!(prices, vec![16.55,21.85,23.75,27.17,28.15,43.20]); + Ok(()) } #[test] fn test_selenium() { - local_tester!(parse_booksprices, "chrome"); + local_tester!(parse_booksprices_from_9782884747974, "chrome"); } } From 40b2e48028633783934457b475594d29c108797f Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 3 Apr 2023 21:25:21 +0200 Subject: [PATCH 051/150] Add market price and add BooksPrice provider --- native/src/api.rs | 6 ++++-- native/src/babelio.rs | 2 ++ native/src/babelio/parser.rs | 2 ++ native/src/booksprice.rs | 10 ++++++++++ native/src/booksprice/request.rs | 14 ++++++++------ native/src/common.rs | 2 ++ native/src/google_books.rs | 9 ++++++++- 7 files changed, 36 insertions(+), 9 deletions(-) diff --git a/native/src/api.rs b/native/src/api.rs index c692fec..281affd 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,12 +1,13 @@ use crate::cached_client::CachedClient; -use crate::common::{Provider, LbcCredential}; use crate::common::{Ad, BookMetaData}; +use crate::common::{LbcCredential, Provider}; use crate::publisher::Publisher; -use crate::{babelio, google_books, leboncoin}; +use crate::{babelio, booksprice, google_books, leboncoin}; pub enum ProviderEnum { Babelio, GoogleBooks, + BooksPrice, } pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Option { @@ -18,6 +19,7 @@ pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Optio }), } .get_book_metadata_from_isbn(&isbn), + ProviderEnum::BooksPrice => booksprice::BooksPrice {}.get_book_metadata_from_isbn(&isbn), } } diff --git a/native/src/babelio.rs b/native/src/babelio.rs index 8c800e6..5e6115a 100644 --- a/native/src/babelio.rs +++ b/native/src/babelio.rs @@ -67,6 +67,7 @@ mod tests { ] .map(|s| s.to_string()) .to_vec(), + market_price: vec![], })); } @@ -87,6 +88,7 @@ Ensemble, les deux enfants devront lutter contre les forces obscures du mal et, ] .map(|s| s.to_string()) .to_vec(), + market_price: vec![], })); } } diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index 0a94177..0132f4b 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -205,6 +205,7 @@ mod tests { ] .map(|s| s.to_string()) .to_vec(), + market_price: vec![], }) ); } @@ -249,6 +250,7 @@ mod tests { ] .map(|s| s.to_string()) .to_vec(), + market_price: vec![], }) ); } diff --git a/native/src/booksprice.rs b/native/src/booksprice.rs index a5f8510..56495a0 100644 --- a/native/src/booksprice.rs +++ b/native/src/booksprice.rs @@ -1,3 +1,13 @@ +use crate::common::{self, BookMetaData}; + mod request; mod selenium_common; +pub struct BooksPrice; + +impl common::Provider for BooksPrice { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let prices = request::extract_price_from_isbn(isbn); + Some(BookMetaData{ title: None, authors: vec![], blurb: None, keywords: vec![], market_price: prices.unwrap() }) + } +} diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index 728a74b..1846f28 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -6,9 +6,9 @@ use thirtyfour::prelude::*; use tokio; #[tokio::main] -async fn extract_price_from_isbn( +pub async fn extract_price_from_isbn( isbn: &str, -) -> Result, thirtyfour::prelude::WebDriverError> { +) -> Result, thirtyfour::prelude::WebDriverError> { let caps = DesiredCapabilities::chrome(); let driver = WebDriver::new("http://localhost:9515", caps).await?; @@ -22,7 +22,7 @@ async fn extract_price_from_isbn( .await } -async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, WebDriverError> { +async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, WebDriverError> { c.goto(&url).await?; let entries = c .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) @@ -41,7 +41,7 @@ async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, Web use regex::Regex; let re = Regex::new(r"\$ (\d+\.?\d+)").unwrap(); let r = re.captures(&price_text).unwrap(); - r.get(1).unwrap().as_str().parse::().unwrap() + r.get(1).unwrap().as_str().parse::().unwrap() }) })) .await @@ -72,9 +72,11 @@ mod tests { let prices = extract_price_from_url( c, &selenium_common::url_from_path(port, "9782884747974.html"), - ).await.unwrap(); + ) + .await + .unwrap(); - assert_eq!(prices, vec![16.55,21.85,23.75,27.17,28.15,43.20]); + assert_eq!(prices, vec![16.55, 21.85, 23.75, 27.17, 28.15, 43.20]); Ok(()) } diff --git a/native/src/common.rs b/native/src/common.rs index 7b1a52a..1488f3a 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -6,6 +6,8 @@ pub struct BookMetaData { // A synopsis summarizes the twists, turns, and conclusion of the story. pub blurb: Option, pub keywords: Vec, + + pub market_price: Vec, } #[derive(Debug, PartialEq, Eq, Hash, Clone)] diff --git a/native/src/google_books.rs b/native/src/google_books.rs index 67fd6cd..1ebd774 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -49,6 +49,7 @@ fn merge_bmd(bmd1: common::BookMetaData, bmd2: common::BookMetaData) -> common:: authors: bmd1.authors, blurb: longest_string_merger(bmd1.blurb, bmd2.blurb), keywords: merge_vec(bmd1.keywords, bmd2.keywords), + market_price: vec![], } } @@ -90,6 +91,12 @@ mod tests { }), }; let md = g.get_book_metadata_from_isbn("9782266162777"); - assert_eq!(md, Some(BookMetaData { title: Some("L'essence du Tao".to_owned()), authors: vec![common::Author{first_name: "".to_owned(), last_name: "Pamela Ball".to_owned()}], blurb: Some("Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.".to_string()), keywords: vec![] })) + assert_eq!(md, Some(BookMetaData { + title: Some("L'essence du Tao".to_owned()), + authors: vec![common::Author{first_name: "".to_owned(), last_name: "Pamela Ball".to_owned()}], + blurb: Some("Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.".to_string()), + keywords: vec![], + market_price: vec![], + })) } } From 82593b0e70b87d50b296f7b9eb27e2eb7a292ca1 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 3 Apr 2023 21:38:39 +0200 Subject: [PATCH 052/150] Run rust codegen --- lib/bridge_definitions.dart | 14 +++++++++----- lib/bridge_generated.dart | 13 +++++++++++-- native/src/bridge_generated.rs | 2 ++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 50af226..c260598 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -2,18 +2,19 @@ // Generated by `flutter_rust_bridge`@ 1.68.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const -import 'dart:async'; import 'dart:convert'; - -import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; abstract class Native { - Future getMetadataFromProvider({required ProviderEnum provider, required String isbn, dynamic hint}); + Future getMetadataFromProvider( + {required ProviderEnum provider, required String isbn, dynamic hint}); FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; - Future publishAd({required Ad ad, required LbcCredential credential, dynamic hint}); + Future publishAd( + {required Ad ad, required LbcCredential credential, dynamic hint}); FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta; } @@ -47,12 +48,14 @@ class BookMetaData { List authors; String? blurb; List keywords; + Float32List marketPrice; BookMetaData({ this.title, required this.authors, this.blurb, required this.keywords, + required this.marketPrice, }); } @@ -69,4 +72,5 @@ class LbcCredential { enum ProviderEnum { Babelio, GoogleBooks, + BooksPrice, } diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index c209bb1..bcc2059 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -88,13 +88,14 @@ class NativeImpl implements Native { BookMetaData _wire2api_book_meta_data(dynamic raw) { final arr = raw as List; - if (arr.length != 4) - throw Exception('unexpected arr length: expect 4 but see ${arr.length}'); + if (arr.length != 5) + throw Exception('unexpected arr length: expect 5 but see ${arr.length}'); return BookMetaData( title: _wire2api_opt_String(arr[0]), authors: _wire2api_list_author(arr[1]), blurb: _wire2api_opt_String(arr[2]), keywords: _wire2api_StringList(arr[3]), + marketPrice: _wire2api_float_32_list(arr[4]), ); } @@ -106,6 +107,14 @@ class NativeImpl implements Native { return _wire2api_book_meta_data(raw); } + double _wire2api_f32(dynamic raw) { + return raw as double; + } + + Float32List _wire2api_float_32_list(dynamic raw) { + return raw as Float32List; + } + List _wire2api_list_author(dynamic raw) { return (raw as List).map(_wire2api_author).toList(); } diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index f99f0c4..4a6d663 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -96,6 +96,7 @@ impl Wire2Api for i32 { match self { 0 => ProviderEnum::Babelio, 1 => ProviderEnum::GoogleBooks, + 2 => ProviderEnum::BooksPrice, _ => unreachable!("Invalid variant for ProviderEnum: {}", self), } } @@ -122,6 +123,7 @@ impl support::IntoDart for BookMetaData { self.authors.into_dart(), self.blurb.into_dart(), self.keywords.into_dart(), + self.market_price.into_dart(), ] .into_dart() } From 3884605aff6e99e5952cf6584e9bccffd0fc6ad3 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 4 Apr 2023 19:56:43 +0200 Subject: [PATCH 053/150] Rename BookMetaData to BookMetaDataFromProvider --- lib/bridge_definitions.dart | 6 +++--- lib/bridge_generated.dart | 20 ++++++++++++-------- lib/common.dart | 9 +++++++-- lib/metadata_collecting.dart | 2 +- native/src/api.rs | 7 +++++-- native/src/babelio.rs | 8 ++++---- native/src/babelio/parser.rs | 10 +++++----- native/src/booksprice.rs | 14 ++++++++++---- native/src/bridge_generated.rs | 6 +++--- native/src/common.rs | 4 ++-- native/src/google_books.rs | 13 ++++++++----- native/src/google_books/parser.rs | 18 +++++++++--------- 12 files changed, 69 insertions(+), 48 deletions(-) diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index c260598..1f2b8df 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -8,7 +8,7 @@ import 'package:meta/meta.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; abstract class Native { - Future getMetadataFromProvider( + Future getMetadataFromProvider( {required ProviderEnum provider, required String isbn, dynamic hint}); FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; @@ -43,14 +43,14 @@ class Author { }); } -class BookMetaData { +class BookMetaDataFromProvider { String? title; List authors; String? blurb; List keywords; Float32List marketPrice; - BookMetaData({ + const BookMetaDataFromProvider({ this.title, required this.authors, this.blurb, diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index bcc2059..c53f603 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -24,14 +24,14 @@ class NativeImpl implements Native { factory NativeImpl.wasm(FutureOr module) => NativeImpl(module as ExternalLibrary); NativeImpl.raw(this._platform); - Future getMetadataFromProvider( + Future getMetadataFromProvider( {required ProviderEnum provider, required String isbn, dynamic hint}) { var arg0 = api2wire_provider_enum(provider); var arg1 = _platform.api2wire_String(isbn); return _platform.executeNormal(FlutterRustBridgeTask( callFfi: (port_) => _platform.inner.wire_get_metadata_from_provider(port_, arg0, arg1), - parseSuccessData: _wire2api_opt_box_autoadd_book_meta_data, + parseSuccessData: _wire2api_opt_box_autoadd_book_meta_data_from_provider, constMeta: kGetMetadataFromProviderConstMeta, argValues: [provider, isbn], hint: hint, @@ -86,11 +86,11 @@ class NativeImpl implements Native { ); } - BookMetaData _wire2api_book_meta_data(dynamic raw) { + BookMetaDataFromProvider _wire2api_book_meta_data_from_provider(dynamic raw) { final arr = raw as List; if (arr.length != 5) throw Exception('unexpected arr length: expect 5 but see ${arr.length}'); - return BookMetaData( + return BookMetaDataFromProvider( title: _wire2api_opt_String(arr[0]), authors: _wire2api_list_author(arr[1]), blurb: _wire2api_opt_String(arr[2]), @@ -103,8 +103,9 @@ class NativeImpl implements Native { return raw as bool; } - BookMetaData _wire2api_box_autoadd_book_meta_data(dynamic raw) { - return _wire2api_book_meta_data(raw); + BookMetaDataFromProvider _wire2api_box_autoadd_book_meta_data_from_provider( + dynamic raw) { + return _wire2api_book_meta_data_from_provider(raw); } double _wire2api_f32(dynamic raw) { @@ -123,8 +124,11 @@ class NativeImpl implements Native { return raw == null ? null : _wire2api_String(raw); } - BookMetaData? _wire2api_opt_box_autoadd_book_meta_data(dynamic raw) { - return raw == null ? null : _wire2api_box_autoadd_book_meta_data(raw); + BookMetaDataFromProvider? + _wire2api_opt_box_autoadd_book_meta_data_from_provider(dynamic raw) { + return raw == null + ? null + : _wire2api_box_autoadd_book_meta_data_from_provider(raw); } int _wire2api_u8(dynamic raw) { diff --git a/lib/common.dart b/lib/common.dart index be0a498..f67ab09 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; @@ -61,6 +62,10 @@ extension DoubleExt on double { } extension BookMetadataExt on BookMetaData { - BookMetaData deepCopy() => - BookMetaData(title: '$title', authors: List.from(authors), blurb: '$blurb', keywords: List.from(keywords)); + BookMetaData deepCopy() => BookMetaData( + title: '$title', + authors: List.from(authors), + blurb: '$blurb', + keywords: List.from(keywords), + marketPrice: Float32List.fromList(marketPrice)); } diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 73cca4c..00a7f80 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -69,7 +69,7 @@ class _MetadataCollectingWidgetState extends State { metadata.putIfAbsent( isbn, () => Metadatas( - manual: BookMetaData(title: '', authors: [], blurb: '', keywords: []), + manual: BookMetaData(title: '', authors: [], blurb: '', keywords: [], price), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); md.then((value) { diff --git a/native/src/api.rs b/native/src/api.rs index 281affd..dfdb8da 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,5 +1,5 @@ use crate::cached_client::CachedClient; -use crate::common::{Ad, BookMetaData}; +use crate::common::{Ad, BookMetaDataFromProvider}; use crate::common::{LbcCredential, Provider}; use crate::publisher::Publisher; use crate::{babelio, booksprice, google_books, leboncoin}; @@ -10,7 +10,10 @@ pub enum ProviderEnum { BooksPrice, } -pub fn get_metadata_from_provider(provider: ProviderEnum, isbn: String) -> Option { +pub fn get_metadata_from_provider( + provider: ProviderEnum, + isbn: String, +) -> Option { match provider { ProviderEnum::Babelio => babelio::Babelio {}.get_book_metadata_from_isbn(&isbn), ProviderEnum::GoogleBooks => google_books::GoogleBooks { diff --git a/native/src/babelio.rs b/native/src/babelio.rs index 5e6115a..fe7782b 100644 --- a/native/src/babelio.rs +++ b/native/src/babelio.rs @@ -5,7 +5,7 @@ mod request; pub struct Babelio; impl common::Provider for Babelio { - fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { let client = reqwest::blocking::Client::builder().build().unwrap(); let cached_client = CachedClient { http_client: client, @@ -30,7 +30,7 @@ impl common::Provider for Babelio { #[cfg(test)] mod tests { - use crate::common::{Author, BookMetaData, Provider}; + use crate::common::{Author, BookMetaDataFromProvider, Provider}; use super::*; @@ -38,7 +38,7 @@ mod tests { fn get_metadata_from_normal_book() { let isbn = "9782266071529"; let md = Babelio {}.get_book_metadata_from_isbn(isbn); - assert_eq!(md, Some(BookMetaData { + assert_eq!(md, Some(BookMetaDataFromProvider { title: Some("Le nom de la bête".to_string()), authors: vec![Author{first_name:"Daniel".to_string(), last_name: "Easterman".to_string()}], blurb: Some("Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède\n".to_string()), @@ -75,7 +75,7 @@ mod tests { fn get_metadata_from_book_with_see_more_bug() { let isbn = "9782070541898"; let md = Babelio {}.get_book_metadata_from_isbn(isbn); - assert_eq!(md, Some(BookMetaData { + assert_eq!(md, Some(BookMetaDataFromProvider { title: Some("À la croisée des mondes, tome 2 : La tour des anges".to_string()), authors: vec![Author{first_name:"Philip".to_string(), last_name: "Pullman".to_string()}], blurb: Some(r#"Le jeune Will, à la recherche de son père disparu depuis de longues années, est persuadé d’avoir tué un homme. Dans sa fuite, il franchit une brèche presque invisible qui lui permet de passer dans un monde parallèle. diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index 0132f4b..620c731 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -1,4 +1,4 @@ -use crate::common::{html_select, BookMetaData}; +use crate::common::{html_select, BookMetaDataFromProvider}; use itertools::Itertools; #[derive(PartialEq, Debug)] @@ -96,7 +96,7 @@ fn extract_author(author_scope: scraper::ElementRef) -> crate::common::Author { } } -pub fn extract_title_author_keywords(html: &str) -> Option { +pub fn extract_title_author_keywords(html: &str) -> Option { let doc = scraper::Html::parse_document(html); let book_select = html_select("div[itemscope][itemtype=\"https://schema.org/Book\"]"); @@ -146,7 +146,7 @@ pub fn extract_title_author_keywords(html: &str) -> Option { ) }) .collect(); - Some(BookMetaData { + Some(BookMetaDataFromProvider { title: Some(title), authors, keywords, @@ -174,7 +174,7 @@ mod tests { let title_author_keywords = extract_title_author_keywords(&html); assert_eq!( title_author_keywords, - Some(BookMetaData { + Some(BookMetaDataFromProvider { title: Some("Le nom de la bête".to_string()), authors: vec![crate::common::Author { first_name: "Daniel".to_string(), @@ -219,7 +219,7 @@ mod tests { let title_author_keywords = extract_title_author_keywords(&html); assert_eq!( title_author_keywords, - Some(BookMetaData { + Some(BookMetaDataFromProvider { title: Some("Bardo-Thödol : Le livre tibétain des morts".to_string()), authors: vec![crate::common::Author { first_name: "".to_string(), diff --git a/native/src/booksprice.rs b/native/src/booksprice.rs index 56495a0..e8be244 100644 --- a/native/src/booksprice.rs +++ b/native/src/booksprice.rs @@ -1,4 +1,4 @@ -use crate::common::{self, BookMetaData}; +use crate::common::{self, BookMetaDataFromProvider}; mod request; mod selenium_common; @@ -6,8 +6,14 @@ mod selenium_common; pub struct BooksPrice; impl common::Provider for BooksPrice { - fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { - let prices = request::extract_price_from_isbn(isbn); - Some(BookMetaData{ title: None, authors: vec![], blurb: None, keywords: vec![], market_price: prices.unwrap() }) + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let prices = request::extract_price_from_isbn(isbn); + Some(BookMetaDataFromProvider { + title: None, + authors: vec![], + blurb: None, + keywords: vec![], + market_price: prices.unwrap(), + }) } } diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 4a6d663..719b492 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use crate::common::Ad; use crate::common::Author; -use crate::common::BookMetaData; +use crate::common::BookMetaDataFromProvider; use crate::common::LbcCredential; // Section: wire functions @@ -116,7 +116,7 @@ impl support::IntoDart for Author { } impl support::IntoDartExceptPrimitive for Author {} -impl support::IntoDart for BookMetaData { +impl support::IntoDart for BookMetaDataFromProvider { fn into_dart(self) -> support::DartAbi { vec![ self.title.into_dart(), @@ -128,7 +128,7 @@ impl support::IntoDart for BookMetaData { .into_dart() } } -impl support::IntoDartExceptPrimitive for BookMetaData {} +impl support::IntoDartExceptPrimitive for BookMetaDataFromProvider {} // Section: executor diff --git a/native/src/common.rs b/native/src/common.rs index 1488f3a..28ad14e 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -1,5 +1,5 @@ #[derive(Default, Debug, PartialEq)] -pub struct BookMetaData { +pub struct BookMetaDataFromProvider { pub title: Option, pub authors: Vec, // A book blurb is a short promotional description. @@ -21,7 +21,7 @@ pub fn html_select(sel: &str) -> scraper::Selector { } pub trait Provider { - fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option; + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option; } pub struct LbcCredential { diff --git a/native/src/google_books.rs b/native/src/google_books.rs index 1ebd774..08b49ac 100644 --- a/native/src/google_books.rs +++ b/native/src/google_books.rs @@ -40,8 +40,11 @@ fn merge_vec( .collect_vec() } -fn merge_bmd(bmd1: common::BookMetaData, bmd2: common::BookMetaData) -> common::BookMetaData { - common::BookMetaData { +fn merge_bmd( + bmd1: common::BookMetaDataFromProvider, + bmd2: common::BookMetaDataFromProvider, +) -> common::BookMetaDataFromProvider { + common::BookMetaDataFromProvider { title: longest_string_merger(bmd1.title, bmd2.title), // Some authors are not display the same way in the first and second request. Sometimes GoogleBooks display the middle name, sometimes not // So a basic merge would result in diplicate authors @@ -54,7 +57,7 @@ fn merge_bmd(bmd1: common::BookMetaData, bmd2: common::BookMetaData) -> common:: } impl common::Provider for GoogleBooks { - fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { // TODO: For some books (eg 9782703305033), the description is better on the first page than in the second // The number of authors can be different too ! let isbn_search_response = request::search_by_isbn(&self.client, isbn); @@ -78,7 +81,7 @@ impl common::Provider for GoogleBooks { #[cfg(test)] mod tests { use crate::cached_client::MockClient; - use crate::common::BookMetaData; + use crate::common::BookMetaDataFromProvider; use crate::common::Provider; use super::*; @@ -91,7 +94,7 @@ mod tests { }), }; let md = g.get_book_metadata_from_isbn("9782266162777"); - assert_eq!(md, Some(BookMetaData { + assert_eq!(md, Some(BookMetaDataFromProvider { title: Some("L'essence du Tao".to_owned()), authors: vec![common::Author{first_name: "".to_owned(), last_name: "Pamela Ball".to_owned()}], blurb: Some("Le Tao est moins une religion qu'un principe de vie universel, une recherche de la sagesse. C'est la \" Voie\" telle que les grands philosophes chinois, Lao Tse, Chuang Tse surtout, l'ont définie il y a plus de deux mille ans : une façon d'être; un ensemble de clés pour une existence harmonieuse et paisible. Pamela Bali nous aide à trouver le chemin qui est le nôtre par le biais de pratiques et de préceptes simples propres au Tao. Après en avoir brossé un bref historique, l'auteur développe les pratiques du Tao, son principe libérateur, évoquant aussi bien la méditation que le Li Chi, le Chi Cung, le Feng Shui ou art du placement, et l'interprétation du I Ching ou Livre des mutations. Un ouvrage clair, accessible et lumineux.".to_string()), diff --git a/native/src/google_books/parser.rs b/native/src/google_books/parser.rs index 3e8ee75..f27a2aa 100644 --- a/native/src/google_books/parser.rs +++ b/native/src/google_books/parser.rs @@ -1,12 +1,12 @@ use itertools::Itertools; -use crate::common::{self, BookMetaData}; +use crate::common::{self, BookMetaDataFromProvider}; pub fn extract_self_link_from_isbn_response(html: &str) -> Option { let s: structs::Root = serde_json::from_str(html).unwrap(); s.items.map(|items| items[0].self_link.to_string()) } -pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaData { +pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaDataFromProvider { let s: structs::Root = serde_json::from_str(html).unwrap(); let a = s.items.map(|items| { let first_book = &items[0].volume_info; @@ -24,21 +24,21 @@ pub fn extract_metadata_from_isbn_response(html: &str) -> common::BookMetaData { .description .clone() .map(|d| d.to_string()); - BookMetaData { + BookMetaDataFromProvider { authors, blurb, ..Default::default() } }); - a.unwrap_or(BookMetaData { + a.unwrap_or(BookMetaDataFromProvider { ..Default::default() }) } -pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaData { +pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaDataFromProvider { let s: structs::Item = serde_json::from_str(html).unwrap(); let first_book = &s.volume_info; - common::BookMetaData { + common::BookMetaDataFromProvider { title: Some(first_book.title.to_string()), authors: first_book .authors @@ -56,7 +56,7 @@ pub fn extract_metadata_from_self_link_response(html: &str) -> common::BookMetaD #[cfg(test)] mod tests { - use crate::common::BookMetaData; + use crate::common::BookMetaDataFromProvider; use super::*; @@ -78,7 +78,7 @@ mod tests { std::fs::read_to_string("src/google_books/test/9782744170812/self_link_response.html") .unwrap(); let metadata = extract_metadata_from_self_link_response(&html); - assert_eq!(metadata, BookMetaData{ + assert_eq!(metadata, BookMetaDataFromProvider{ title: Some("La cité de Dieu".to_string()), authors:vec![common::Author{first_name: "".to_string(), last_name: "Paulo Lins".to_string()}], blurb: Some("Au Brésil, l'évolution d'un bidonville entre les années 1960 et 1980, à travers l'histoire de deux garçons qui suivent des voies différentes : l'un fait des études et s'efforce de devenir photographe, l'autre crée son premier gang et devient, quelques années plus tard, le maître de la cité.".to_string()), @@ -106,7 +106,7 @@ mod tests { let metadata = extract_metadata_from_self_link_response(&html); assert_eq!( metadata, - BookMetaData { + BookMetaDataFromProvider { title: Some("L'essence du Tao".to_string()), authors: vec![common::Author { first_name: "".to_string(), From 54790b41ecf31da3267c8f79017a0f1fe28f5572 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 4 Apr 2023 21:01:59 +0200 Subject: [PATCH 054/150] Adaot flutter --- lib/ad_editing.dart | 17 ++++++++++------- lib/bridge_definitions.dart | 10 +++++----- lib/common.dart | 20 ++++++++++++++++++-- lib/main.dart | 12 ++++++------ lib/metadata_collecting.dart | 6 +++--- 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 4291583..cc33a8d 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -16,16 +16,16 @@ class AdEditingWidget extends StatefulWidget { State createState() => _AdEditingWidgetState(); } -String vecFmt(List vec) { +String vecFmt(Iterable it) { + final vec = it.toList(); if (vec.length == 0) return ''; if (vec.length == 1) return 'de ${vec[0]}'; if (vec.length == 2) return 'de ${vec[0]} et ${vec[1]}'; throw UnimplementedError('More than 2 authors'); } -String _bookFormatTitleAndAuthor(BookMetaData book) { - final authors = book.authors.map((a) => '${a.firstName} ${a.lastName}').toList(); - return '"${book.title}" ${vecFmt(authors)}'; +String _bookFormatTitleAndAuthor(String title, Iterable authors) { + return '"$title" ${vecFmt(authors.map((a) => '${a.firstName} ${a.lastName}'))}'; } class _AdEditingWidgetState extends State { @@ -53,15 +53,18 @@ class _AdEditingWidgetState extends State { print('credential ${credential.lbcToken} ${credential.dataDomeCookie}'); } - String _getDescription(Iterable> metadataFromIsbn) { + String _getDescription(Iterable> metadataFromIsbn) { if (metadataFromIsbn.length == 1) { final blurb = metadataFromIsbn.single.value.blurb; if (blurb == null) return ''; return 'Résumé:\n' + blurb; } else { - final bookTitles = metadataFromIsbn.map((entry) => _bookFormatTitleAndAuthor(entry.value)).join('\n'); + final bookTitles = metadataFromIsbn + .map((entry) => _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors)) + .join('\n'); final blurbs = metadataFromIsbn - .map((entry) => _bookFormatTitleAndAuthor(entry.value) + ':\n' + entry.value.blurb!) + .map((entry) => + _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors) + ':\n' + entry.value.blurb!) .join('\n'); final description = bookTitles + '\n\nRésumés:\n' + blurbs; return description; diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 1f2b8df..bf214fa 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -2,10 +2,11 @@ // Generated by `flutter_rust_bridge`@ 1.68.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const -import 'dart:convert'; import 'dart:async'; -import 'package:meta/meta.dart'; +import 'dart:convert'; + import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:meta/meta.dart'; abstract class Native { Future getMetadataFromProvider( @@ -13,8 +14,7 @@ abstract class Native { FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; - Future publishAd( - {required Ad ad, required LbcCredential credential, dynamic hint}); + Future publishAd({required Ad ad, required LbcCredential credential, dynamic hint}); FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta; } @@ -50,7 +50,7 @@ class BookMetaDataFromProvider { List keywords; Float32List marketPrice; - const BookMetaDataFromProvider({ + BookMetaDataFromProvider({ this.title, required this.authors, this.blurb, diff --git a/lib/common.dart b/lib/common.dart index f67ab09..c9e504f 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:typed_data'; import 'package:flutter/material.dart'; @@ -61,6 +60,23 @@ extension DoubleExt on double { double multiply(double other) => this * other; } +class BookMetaDataManual { + String? title; + List authors; + String? blurb; + List keywords; + int? priceCent; + + BookMetaDataManual({ + this.title, + required this.authors, + this.blurb, + required this.keywords, + required this.priceCent, + }); +} + +/* extension BookMetadataExt on BookMetaData { BookMetaData deepCopy() => BookMetaData( title: '$title', @@ -68,4 +84,4 @@ extension BookMetadataExt on BookMetaData { blurb: '$blurb', keywords: List.from(keywords), marketPrice: Float32List.fromList(marketPrice)); -} +}*/ diff --git a/lib/main.dart b/lib/main.dart index 23bfee3..df589b5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/common.dart'; import 'ad_editing.dart'; import 'drag_and_drop.dart' as drag_and_drop; -import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'isbn_decoding.dart'; import 'metadata_collecting.dart'; @@ -27,7 +27,7 @@ class MetadataCollectingStep implements BookyStep { class AdEditingStep implements BookyStep { List imgsPaths = []; - Map metadata = {}; + Map metadata = {}; AdEditingStep({required this.imgsPaths, required this.metadata}); } @@ -40,8 +40,8 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BookyStep step = ImageSelectionStep(); - /* AdEditingStep(imgsPaths: [ + BookyStep step = //ImageSelectionStep(); + /* AdEditingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg' ], metadata: { 'myisbn': BookMetaData( @@ -49,7 +49,7 @@ class _MyAppState extends State { authors: [Author(firstName: 'Mock firstname', lastName: 'mock lastname')], keywords: ['mock kw']) });*/ - /* MetadataCollectingStep(imgsPaths: [ + MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', @@ -57,7 +57,7 @@ class _MyAppState extends State { ], isbns: { '9782253029854', '9782277223634', - });*/ + }); @override Widget build(BuildContext context) { return MaterialApp( diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 00a7f80..e7b1b86 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -35,8 +35,8 @@ class MetadataCollectingWidget extends StatefulWidget { } class Metadatas { - final Map> mdFromProviders; - BookMetaData manual; + final Map> mdFromProviders; + BookMetaDataManual manual; Metadatas({required this.mdFromProviders, required this.manual}); } @@ -69,7 +69,7 @@ class _MetadataCollectingWidgetState extends State { metadata.putIfAbsent( isbn, () => Metadatas( - manual: BookMetaData(title: '', authors: [], blurb: '', keywords: [], price), + manual: BookMetaDataManual(title: '', authors: [], blurb: '', keywords: [], priceCent: null), mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); md.then((value) { From 624eca73e2562b17e753db875e0b96ae05185604 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 4 Apr 2023 21:49:27 +0200 Subject: [PATCH 055/150] BooksPrice: remove assert, add wait_until --- lib/metadata_collecting.dart | 1 + native/src/booksprice/request.rs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index e7b1b86..d5865bb 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -112,6 +112,7 @@ class _MetadataCollectingWidgetState extends State { Text('Manual'), Text('Babelio'), Text('GoogleBooks'), + Text('BooksPrice'), ]), TableRow(children: [ FutureWidget( diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index 1846f28..62cbee3 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -2,6 +2,8 @@ //! //! chromedriver --port=9515 +use std::time::Duration; + use thirtyfour::prelude::*; use tokio; @@ -24,10 +26,13 @@ pub async fn extract_price_from_isbn( async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, WebDriverError> { c.goto(&url).await?; + + c.query(By::XPath("//*[@id='chart']")) + .wait(Duration::from_secs(10), Duration::from_secs(1)); + let entries = c .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) .await?; - assert_eq!(entries.len(), 6); let prices = futures::future::try_join_all(entries.iter().map(|e| async { let price_text = e From 47190316bfa206b580117304101f96ec466ced86 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 10 Apr 2023 16:57:47 +0200 Subject: [PATCH 056/150] Price works rust: wait for chart to appear. flutter: show price and use it in ad_editing --- lib/ad_editing.dart | 15 ++- lib/main.dart | 10 +- lib/metadata_collecting.dart | 175 ++++++++++++++++++------------- native/src/booksprice/request.rs | 7 +- 4 files changed, 122 insertions(+), 85 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index cc33a8d..2016f59 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; @@ -47,7 +48,9 @@ class _AdEditingWidgetState extends State { description += '\n\nMots-clés:\n' + keywords; } - ad = Ad(title: title, description: description, priceCent: 1000, imgsPath: widget.step.imgsPaths); + final totalPrice = metadataFromIsbn.map((e) => e.value.priceCent ?? 0).sum; + + ad = Ad(title: title, description: description, priceCent: totalPrice, imgsPath: widget.step.imgsPaths); credential = Credential.loadFromFile(); print('credential ${credential.lbcToken} ${credential.dataDomeCookie}'); @@ -130,9 +133,13 @@ class _AdEditingWidgetState extends State { style: const TextStyle(fontSize: 20), autovalidateMode: AutovalidateMode.always, validator: (token) { - final remainingDuration = JwtDecoder.getRemainingTime(token!); - if (remainingDuration.isNegative) { - return 'Token expired'; + try { + final remainingDuration = JwtDecoder.getRemainingTime(token!); + if (remainingDuration.isNegative) { + return 'Token expired'; + } + } on FormatException catch (e) { + return 'Not a JWT token'; } return null; }, diff --git a/lib/main.dart b/lib/main.dart index df589b5..f1ff9e1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -40,8 +40,8 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BookyStep step = //ImageSelectionStep(); - /* AdEditingStep(imgsPaths: [ + BookyStep step = ImageSelectionStep(); + /* AdEditingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg' ], metadata: { 'myisbn': BookMetaData( @@ -49,15 +49,15 @@ class _MyAppState extends State { authors: [Author(firstName: 'Mock firstname', lastName: 'mock lastname')], keywords: ['mock kw']) });*/ - MetadataCollectingStep(imgsPaths: [ + /* MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194758.jpg' ], isbns: { '9782253029854', - '9782277223634', - }); + // '9782277223634', + });*/ @override Widget build(BuildContext context) { return MaterialApp( diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index d5865bb..7f9aae2 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_rust_bridge_template/common.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; @@ -29,6 +30,7 @@ class MetadataCollectingWidget extends StatefulWidget { final blurbTextFieldController = TextEditingController(); final titleTextFieldController = TextEditingController(); + final priceTextFieldController = TextEditingController(); @override State createState() => _MetadataCollectingWidgetState(); @@ -98,89 +100,114 @@ class _MetadataCollectingWidgetState extends State { children: [ ...widget.step.isbns.map((isbn) { final manual = metadata[isbn]!.manual; + const columnHeaderStyle = TextStyle(fontSize: 20, fontWeight: FontWeight.bold); return Card( margin: const EdgeInsets.all(10), child: Padding( padding: const EdgeInsets.all(8.0), - child: Row( + child: Column( children: [ - SelectableText('ISBN: $isbn'), - Expanded( - child: Table( - children: [ - const TableRow(children: [ - Text('Manual'), - Text('Babelio'), - Text('GoogleBooks'), - Text('BooksPrice'), - ]), - TableRow(children: [ - FutureWidget( - future: metadata[isbn]!.mdFromProviders.entries.first.value, - builder: (data) => TextFormField( - controller: widget.titleTextFieldController, - onChanged: (newText) => setState(() => manual.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', - ), - )), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) => data == null ? noneText : SelectableText(data.title ?? ''))), - ]), - TableRow(children: [ - FutureWidget( + SelectableText('ISBN: $isbn', style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), + Table( + children: [ + TableRow( + children: [ + const Text('Manual', style: columnHeaderStyle), + const Text('Babelio', style: columnHeaderStyle), + const Text('GoogleBooks', style: columnHeaderStyle), + const Text('BooksPrice', style: columnHeaderStyle), + ].map((e) => Center(child: e)).toList()), + TableRow(children: [ + FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data?.authors.toText(), - onChanged: (newText) => setState(() => manual.authors = newText - .split('\n') - .map((line) => Author(firstName: '', lastName: line)) - .toList()), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Authors', - ), + controller: widget.titleTextFieldController, + onChanged: (newText) => setState(() => manual.title = newText), + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + )), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) => data == null ? noneText : SelectableText(data.title ?? ''))), + ]), + TableRow(children: [ + FutureWidget( + future: metadata[isbn]!.mdFromProviders.entries.first.value, + builder: (data) => TextFormField( + initialValue: data?.authors.toText(), + onChanged: (newText) => setState(() => manual.authors = + newText.split('\n').map((line) => Author(firstName: '', lastName: line)).toList()), + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Authors', ), ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final authors = data?.authors; - if (authors == null || authors.isEmpty) { - return noneText; - } - return SelectableText(authors.toText()); - })), - ]), - TableRow(children: [ - FutureWidget( - future: metadata[isbn]!.mdFromProviders.entries.first.value, - builder: (data) => TextFormField( - controller: widget.blurbTextFieldController, - onChanged: (newText) => setState(() => metadata[isbn]!.manual.blurb = newText), - maxLines: null, - decoration: const InputDecoration( - icon: Icon(Icons.description), - labelText: 'Book blurb', - ), - )), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final blurb = data?.blurb; - if (blurb == null) { - return noneText; - } - return SelectableTextAndUse( - blurb, - onUse: (b) => _updateManualBlurb(isbn, b), - ); - })), - ]), - ], - ), + ), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final authors = data?.authors; + if (authors == null || authors.isEmpty) { + return noneText; + } + return SelectableText(authors.toText()); + })), + ]), + TableRow(children: [ + FutureWidget( + future: metadata[isbn]!.mdFromProviders.entries.first.value, + builder: (data) => TextFormField( + controller: widget.blurbTextFieldController, + onChanged: (newText) => setState(() => metadata[isbn]!.manual.blurb = newText), + maxLines: null, + decoration: const InputDecoration( + icon: Icon(Icons.description), + labelText: 'Book blurb', + ), + )), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final blurb = data?.blurb; + if (blurb == null) { + return noneText; + } + return SelectableTextAndUse( + blurb, + onUse: (b) => _updateManualBlurb(isbn, b), + ); + })), + ]), + TableRow(children: [ + FutureWidget( + future: metadata[isbn]!.mdFromProviders.entries.first.value, + builder: (data) => TextFormField( + controller: widget.priceTextFieldController, + onChanged: (newText) => setState(() => metadata[isbn]!.manual.priceCent = + double.parse(newText).multiply(100).round()), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9]+[,.]{0,1}[0-9]*')), + ], + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', + ), + )), + ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final marketPrices = data?.marketPrice.toList()?..sort(); + if (marketPrices == null || marketPrices.isEmpty) { + return noneText; + } + return SelectableText( + '${marketPrices.first.toStringAsFixed(2)} - ${marketPrices.last.toStringAsFixed(2)}', + ); + })), + ]), + ], ), ], ), diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index 62cbee3..ee1a12d 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -27,8 +27,11 @@ pub async fn extract_price_from_isbn( async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, WebDriverError> { c.goto(&url).await?; - c.query(By::XPath("//*[@id='chart']")) - .wait(Duration::from_secs(10), Duration::from_secs(1)); + let wait_res = c + .query(By::XPath("//*[@id='chart']")) + .wait(Duration::from_secs(10), Duration::from_secs(1)) + .exists() + .await; let entries = c .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) From 6a8a8b07f635b1f3920271ed557261dcf4041abc Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 12 Apr 2023 22:36:10 +0200 Subject: [PATCH 057/150] Convert html encoded text to String --- native/Cargo.toml | 1 + native/src/babelio.rs | 11 +++++------ native/src/babelio/parser.rs | 14 ++++++++++++-- .../test/get_book_blurb_see_more_179245.html | 4 ++++ 4 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 native/src/babelio/test/get_book_blurb_see_more_179245.html diff --git a/native/Cargo.toml b/native/Cargo.toml index 1f31af6..c38db3c 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -26,3 +26,4 @@ tokio = { version = "1.20", features = ["fs", "macros", "rt-multi-thread", "io-u color-eyre = "0.6.2" hyper = { version = "0.14", features = ["server", "tcp"] } futures = "0.3.28" +html2text = "0.5.1" diff --git a/native/src/babelio.rs b/native/src/babelio.rs index fe7782b..4f40ce4 100644 --- a/native/src/babelio.rs +++ b/native/src/babelio.rs @@ -21,7 +21,7 @@ impl common::Provider for Babelio { request::get_book_blurb_see_more(&cached_client, &id_obj) } }; - res.blurb = parser::parse_blurb(&raw_blurb); + res.blurb = Some(parser::parse_blurb(&raw_blurb)); } Some(res) @@ -41,7 +41,7 @@ mod tests { assert_eq!(md, Some(BookMetaDataFromProvider { title: Some("Le nom de la bête".to_string()), authors: vec![Author{first_name:"Daniel".to_string(), last_name: "Easterman".to_string()}], - blurb: Some("Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède\n".to_string()), + blurb: Some("Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède".to_string()), keywords: [ "roman", @@ -78,10 +78,9 @@ mod tests { assert_eq!(md, Some(BookMetaDataFromProvider { title: Some("À la croisée des mondes, tome 2 : La tour des anges".to_string()), authors: vec![Author{first_name:"Philip".to_string(), last_name: "Pullman".to_string()}], - blurb: Some(r#"Le jeune Will, à la recherche de son père disparu depuis de longues années, est persuadé d’avoir tué un homme. Dans sa fuite, il franchit une brèche presque invisible qui lui permet de passer dans un monde parallèle. -Là, à Cittàgazze, la ville au-delà de l’Aurore, il rencontre Lyra, l’héroïne des "Royaumes du Nord". Elle aussi cherche à rejoindre son père, elle aussi est investie d’une mission dont elle ne connaît pas encore toute l’importance. -Ensemble, les deux enfants devront lutter contre les forces obscures du mal et, pour accomplir leur quête, pénétrer dans la mystérieuse tour des Anges… -"#.to_string()), + blurb: Some(r#"Le jeune Will, à la recherche de son père disparu depuis de longues années, est persuadé d’avoir tué un homme. Dans sa fuite, il franchit une brèche presque invisible qui lui permet de passer dans un monde parallèle. +Là, à Cittàgazze, la ville au-delà de l’Aurore, il rencontre Lyra, l’héroïne des "Royaumes du Nord". Elle aussi cherche à rejoindre son père, elle aussi est investie d’une mission dont elle ne connaît pas encore toute l’importance. +Ensemble, les deux enfants devront lutter contre les forces obscures du mal et, pour accomplir leur quête, pénétrer dans la mystérieuse tour des Anges…"#.to_string()), keywords: [ "aventure", "saga", "roman", "fantasy", "fantastique", "littérature jeunesse", "jeunesse", "steampunk", "littérature pour adolescents", "enfants", "magie", "amitié", "enfance", "science-fiction", "univers parallèles", "religion", "adolescence", "littérature anglaise", "littérature britannique", "20ème siècle", diff --git a/native/src/babelio/parser.rs b/native/src/babelio/parser.rs index 620c731..4544d81 100644 --- a/native/src/babelio/parser.rs +++ b/native/src/babelio/parser.rs @@ -154,8 +154,9 @@ pub fn extract_title_author_keywords(html: &str) -> Option Option { - Some(raw_blurb.trim().replace("
", "\n")) +pub fn parse_blurb(raw_blurb: &str) -> String { + let text = html2text::from_read(raw_blurb.as_bytes(), usize::MAX); + text.trim().to_string() } #[cfg(test)] @@ -168,6 +169,15 @@ mod tests { let id_obj = extract_blurb(&html); assert_eq!(id_obj, Some(BlurbRes::BigBlurb("827593".to_string()))); } + + #[test] + fn test_parse_blurb_with_special_charset() { + let html = std::fs::read_to_string("src/babelio/test/get_book_blurb_see_more_179245.html") + .unwrap(); + let text = parse_blurb(&html); + assert_eq!(text, "La ville entière est sous le choc. Adam, un jeune autiste de neuf ans, a été retrouvé dans les bois à côté du corps sans vie d'une camarade d'école sauvagement poignardée. Quelques heures auparavant, les deux enfants avaient échappé à la vigilance des adultes pendant la récréation et s'étaient évanouis dans la nature. Tous les espoirs d'identifier le coupable reposent désormais sur le témoignage d'Adam. Mais, replié sur lui-même, il ne réagit pas et refuse de communiquer. Commence alors pour Cara, sa mère, un subtil exercice d'interprétation : saura-t-elle déchiffrer les silences de son fils et aider les enquêteurs à débusquer le meurtrier ? Thriller psychologique, Au fond des yeux raconte avec pudeur et justesse le courageux combat d'une mère contre les préjugés et l'isolement."); + } + #[test] pub fn extract_title_author_keywords_from_file() { let html = std::fs::read_to_string("src/babelio/test/get_book_minimal.html").unwrap(); diff --git a/native/src/babelio/test/get_book_blurb_see_more_179245.html b/native/src/babelio/test/get_book_blurb_see_more_179245.html new file mode 100644 index 0000000..c7c5ee5 --- /dev/null +++ b/native/src/babelio/test/get_book_blurb_see_more_179245.html @@ -0,0 +1,4 @@ +
+ La ville entière est sous le choc. Adam, un jeune autiste de neuf ans, a été retrouvé dans les bois à côté du corps sans vie d'une camarade d'école sauvagement poignardée. Quelques heures auparavant, les deux enfants avaient échappé à la vigilance des adultes pendant la récréation et s'étaient évanouis dans la nature. Tous les espoirs d'identifier le coupable reposent désormais sur le témoignage d'Adam. Mais, replié sur lui-même, il ne réagit pas et refuse de communiquer. Commence alors pour Cara, sa mère, un subtil exercice d'interprétation : saura-t-elle déchiffrer les silences de son fils et aider les enquêteurs à débusquer le meurtrier ? Thriller psychologique, Au fond des yeux raconte avec pudeur et justesse le courageux combat d'une mère contre les préjugés et l'isolement.
+
+
\ No newline at end of file From f7c39e151073dcf4be285b6e6c047d5d8fd04df8 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 12 Apr 2023 22:49:09 +0200 Subject: [PATCH 058/150] ISBNDeconding: use Wrap --- lib/isbn_decoding.dart | 62 +++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 78ef669..6adc1f5 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -41,35 +41,41 @@ class _ISBNDecodingWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Row( + body: Column( children: [ - ...widget.step.imgsPaths - .map((imgPath) => Column( - children: [ - ImageWidget(imgPath), - FutureBuilder( - future: isbns[imgPath]!, - builder: (context, snap) { - if (snap.hasData == false) { - return const CircularProgressIndicator(); - } - return Column(children: snap.data!.map((isbn) => Text(isbn)).toList()); - }) - ], - )) - .toList(), - const Spacer(), - FutureBuilder( - future: Future.wait(isbns.values), - builder: (context, snap) { - return ElevatedButton( - onPressed: () { - final isbnSet = snap.data!.expand((e) => e).toSet(); - print('isbnSet = $isbnSet'); - widget.onSubmit(MetadataCollectingStep(imgsPaths: widget.step.imgsPaths, isbns: isbnSet)); - }, - child: const Text('Validate ISBNs')); - }) + Wrap( + children: [ + ...widget.step.imgsPaths + .map((imgPath) => Column( + children: [ + ImageWidget(imgPath), + FutureBuilder( + future: isbns[imgPath]!, + builder: (context, snap) { + if (snap.hasData == false) { + return const CircularProgressIndicator(); + } + return Column(children: snap.data!.map((isbn) => Text(isbn)).toList()); + }) + ], + )) + .toList(), + ], + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: FutureBuilder( + future: Future.wait(isbns.values), + builder: (context, snap) { + return ElevatedButton( + onPressed: () { + final isbnSet = snap.data!.expand((e) => e).toSet(); + print('isbnSet = $isbnSet'); + widget.onSubmit(MetadataCollectingStep(imgsPaths: widget.step.imgsPaths, isbns: isbnSet)); + }, + child: const Text('Validate ISBNs')); + }), + ) ], ), ); From c3869d029f966b7cb4484bcf318740b12127e468 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 18 Apr 2023 21:32:46 +0200 Subject: [PATCH 059/150] Select bundle from phone --- lib/ad_editing.dart | 12 +++- lib/bundle.dart | 20 +++++++ lib/bundle_selection.dart | 64 ++++++++++++++++++++ lib/common.dart | 109 ++++++++++------------------------- lib/common.g.dart | 24 ++++++++ lib/helpers.dart | 87 ++++++++++++++++++++++++++++ lib/isbn_decoding.dart | 12 ++-- lib/main.dart | 29 +++++----- lib/metadata_collecting.dart | 4 +- pubspec.yaml | 1 + 10 files changed, 256 insertions(+), 106 deletions(-) create mode 100644 lib/bundle.dart create mode 100644 lib/bundle_selection.dart create mode 100644 lib/common.g.dart create mode 100644 lib/helpers.dart diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 2016f59..a982246 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -1,12 +1,14 @@ +import 'dart:io'; + import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; import 'package:jwt_decoder/jwt_decoder.dart'; -import 'common.dart'; import 'credential.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; +import 'helpers.dart'; class AdEditingWidget extends StatefulWidget { const AdEditingWidget({required this.step, required this.onSubmit}); @@ -50,7 +52,11 @@ class _AdEditingWidgetState extends State { final totalPrice = metadataFromIsbn.map((e) => e.value.priceCent ?? 0).sum; - ad = Ad(title: title, description: description, priceCent: totalPrice, imgsPath: widget.step.imgsPaths); + ad = Ad( + title: title, + description: description, + priceCent: totalPrice, + imgsPath: widget.step.bundle.images.map((e) => e.path).toList()); credential = Credential.loadFromFile(); print('credential ${credential.lbcToken} ${credential.dataDomeCookie}'); @@ -120,7 +126,7 @@ class _AdEditingWidgetState extends State { color: Colors.grey, ), const SizedBox(width: 16), - ...ad.imgsPath.map((imgPath) => ImageWidget(imgPath)).toList(), + ...ad.imgsPath.map((img) => ImageWidget(File(img))).toList(), ]), ), TextFormField( diff --git a/lib/bundle.dart b/lib/bundle.dart new file mode 100644 index 0000000..0c954e7 --- /dev/null +++ b/lib/bundle.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter_rust_bridge_template/common.dart'; +import 'package:path/path.dart' as path; + +class Bundle { + Bundle(this.directory); + + final Directory directory; + + Iterable get images { + return directory.listSync().whereType().where((file) => path.extension(file.path) == '.jpg'); + } + + Metadata get metadata { + final metadataFile = File(path.join(directory.path, 'metadata.json')); + return Metadata.fromJson(jsonDecode(metadataFile.readAsStringSync()) as Map); + } +} diff --git a/lib/bundle_selection.dart b/lib/bundle_selection.dart new file mode 100644 index 0000000..c8bd3cf --- /dev/null +++ b/lib/bundle_selection.dart @@ -0,0 +1,64 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/main.dart'; +import 'package:path/path.dart' as path; + +import 'bundle.dart'; + +class BundleSelection extends StatelessWidget { + const BundleSelection({required this.onSubmit}); + + final void Function(ISBNDecodingStep newStep) onSubmit; + + @override + Widget build(BuildContext context) { + final bundleDirs = + Directory('/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/booky/') + .listSync() + .whereType(); + + return Scaffold( + appBar: AppBar(title: const Text('Bundle Section')), + body: Wrap( + children: bundleDirs + .map((d) => Padding( + padding: const EdgeInsets.all(8.0), + child: GestureDetector( + child: BundleWidget(d), + onTap: () => onSubmit(ISBNDecodingStep(bundle: Bundle(d))), + ), + )) + .toList(), + ), + ); + } +} + +class BundleWidget extends StatelessWidget { + const BundleWidget(this.directory); + + final Directory directory; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + decoration: const BoxDecoration(color: Colors.blue), + child: Wrap( + children: directory + .listSync() + .whereType() + .where((f) => path.extension(f.path) == '.jpg') + .map((f) => Padding( + padding: const EdgeInsets.all(8.0), + child: Image.file(f, height: 150), + )) + .toList(), + )), + Text(path.basename(directory.path)) + ], + ); + } +} diff --git a/lib/common.dart b/lib/common.dart index c9e504f..cb56f1b 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -1,87 +1,36 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; - -import 'bridge_definitions.dart'; - -class ImageWidget extends StatelessWidget { - const ImageWidget(this.imgPath); - final String imgPath; - - @override - Widget build(BuildContext context) { - return Image.file( - File(imgPath), - height: 200, - isAntiAlias: true, - filterQuality: FilterQuality.medium, - ); - } -} - -class FutureWidget extends StatelessWidget { - const FutureWidget({required this.future, required this.builder}); - final Future future; - final Widget Function(T) builder; - - @override - Widget build(BuildContext context) { - return FutureBuilder(future: future, builder: (context, snap) => AsyncSnapshotWidget(snap: snap, builder: builder)); - } -} - -class AsyncSnapshotWidget extends StatelessWidget { - const AsyncSnapshotWidget({required this.snap, required this.builder}); - final AsyncSnapshot snap; - final Widget Function(T data) builder; - - @override - Widget build(BuildContext context) { - switch (snap.connectionState) { - case ConnectionState.waiting: - return const CircularProgressIndicator(); - case ConnectionState.done: - return builder(snap.data as T); - default: - return const Text('???'); +// TODO: merge with same file in camera_app + +import 'package:json_annotation/json_annotation.dart'; + +part 'common.g.dart'; + +enum ItemState { + brandNew, + veryGood, + good, + medium; + + String get loc { + switch (this) { + case ItemState.brandNew: + return 'Brand New'; + case ItemState.veryGood: + return 'Very Good'; + case ItemState.good: + return 'Good'; + case ItemState.medium: + return 'Medium'; } } } -extension AuthorsExt on List { - String toText() => map((a) => '${a.firstName} ${a.lastName}').join('\n'); -} +@JsonSerializable() +class Metadata { + Metadata({this.weightGrams, this.itemState}); + int? weightGrams; + ItemState? itemState; -extension IntExt on int { - int divide(int other) => this ~/ other; -} + factory Metadata.fromJson(Map json) => _$MetadataFromJson(json); -extension DoubleExt on double { - double multiply(double other) => this * other; + Map toJson() => _$MetadataToJson(this); } - -class BookMetaDataManual { - String? title; - List authors; - String? blurb; - List keywords; - int? priceCent; - - BookMetaDataManual({ - this.title, - required this.authors, - this.blurb, - required this.keywords, - required this.priceCent, - }); -} - -/* -extension BookMetadataExt on BookMetaData { - BookMetaData deepCopy() => BookMetaData( - title: '$title', - authors: List.from(authors), - blurb: '$blurb', - keywords: List.from(keywords), - marketPrice: Float32List.fromList(marketPrice)); -}*/ diff --git a/lib/common.g.dart b/lib/common.g.dart new file mode 100644 index 0000000..915ee66 --- /dev/null +++ b/lib/common.g.dart @@ -0,0 +1,24 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'common.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Metadata _$MetadataFromJson(Map json) => Metadata( + weightGrams: json['weightGrams'] as int?, + itemState: $enumDecodeNullable(_$ItemStateEnumMap, json['itemState']), + ); + +Map _$MetadataToJson(Metadata instance) => { + 'weightGrams': instance.weightGrams, + 'itemState': _$ItemStateEnumMap[instance.itemState], + }; + +const _$ItemStateEnumMap = { + ItemState.brandNew: 'brandNew', + ItemState.veryGood: 'veryGood', + ItemState.good: 'good', + ItemState.medium: 'medium', +}; diff --git a/lib/helpers.dart b/lib/helpers.dart new file mode 100644 index 0000000..42809f7 --- /dev/null +++ b/lib/helpers.dart @@ -0,0 +1,87 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +import 'bridge_definitions.dart'; + +class ImageWidget extends StatelessWidget { + const ImageWidget(this.image); + final File image; + + @override + Widget build(BuildContext context) { + return Image.file( + image, + height: 200, + isAntiAlias: true, + filterQuality: FilterQuality.medium, + ); + } +} + +class FutureWidget extends StatelessWidget { + const FutureWidget({required this.future, required this.builder}); + final Future future; + final Widget Function(T) builder; + + @override + Widget build(BuildContext context) { + return FutureBuilder(future: future, builder: (context, snap) => AsyncSnapshotWidget(snap: snap, builder: builder)); + } +} + +class AsyncSnapshotWidget extends StatelessWidget { + const AsyncSnapshotWidget({required this.snap, required this.builder}); + final AsyncSnapshot snap; + final Widget Function(T data) builder; + + @override + Widget build(BuildContext context) { + switch (snap.connectionState) { + case ConnectionState.waiting: + return const CircularProgressIndicator(); + case ConnectionState.done: + return builder(snap.data as T); + default: + return const Text('???'); + } + } +} + +extension AuthorsExt on List { + String toText() => map((a) => '${a.firstName} ${a.lastName}').join('\n'); +} + +extension IntExt on int { + int divide(int other) => this ~/ other; +} + +extension DoubleExt on double { + double multiply(double other) => this * other; +} + +class BookMetaDataManual { + String? title; + List authors; + String? blurb; + List keywords; + int? priceCent; + + BookMetaDataManual({ + this.title, + required this.authors, + this.blurb, + required this.keywords, + required this.priceCent, + }); +} + +/* +extension BookMetadataExt on BookMetaData { + BookMetaData deepCopy() => BookMetaData( + title: '$title', + authors: List.from(authors), + blurb: '$blurb', + keywords: List.from(keywords), + marketPrice: Float32List.fromList(marketPrice)); +}*/ diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 6adc1f5..c4efacf 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; -import 'common.dart'; +import 'helpers.dart'; class ISBNDecodingWidget extends StatefulWidget { const ISBNDecodingWidget({required this.step, required this.onSubmit}); @@ -15,14 +15,14 @@ class ISBNDecodingWidget extends StatefulWidget { } class _ISBNDecodingWidgetState extends State { + // TODO: Don't use Map because operator[] accept Object as parameter instead of o Key type Map>> isbns = {}; @override void initState() { - // TODO: implement initState super.initState(); - print('initState'); - widget.step.imgsPaths.forEach((imgPath) { + widget.step.bundle.images.forEach((image) { + final imgPath = image.path; isbns[imgPath] = Future(() async { final decoderProcess = await Process.run( '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', @@ -45,7 +45,7 @@ class _ISBNDecodingWidgetState extends State { children: [ Wrap( children: [ - ...widget.step.imgsPaths + ...widget.step.bundle.images .map((imgPath) => Column( children: [ ImageWidget(imgPath), @@ -71,7 +71,7 @@ class _ISBNDecodingWidgetState extends State { onPressed: () { final isbnSet = snap.data!.expand((e) => e).toSet(); print('isbnSet = $isbnSet'); - widget.onSubmit(MetadataCollectingStep(imgsPaths: widget.step.imgsPaths, isbns: isbnSet)); + widget.onSubmit(MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)); }, child: const Text('Validate ISBNs')); }), diff --git a/lib/main.dart b/lib/main.dart index f1ff9e1..60ed4e4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/common.dart'; +import 'package:flutter_rust_bridge_template/helpers.dart'; import 'ad_editing.dart'; -import 'drag_and_drop.dart' as drag_and_drop; +import 'bundle.dart'; +import 'bundle_selection.dart'; import 'isbn_decoding.dart'; import 'metadata_collecting.dart'; @@ -12,24 +13,25 @@ void main() { sealed class BookyStep {} -class ImageSelectionStep implements BookyStep {} +class BundleSelectionStep implements BookyStep {} class ISBNDecodingStep implements BookyStep { - List imgsPaths = []; - ISBNDecodingStep({required this.imgsPaths}); + Bundle bundle; + ISBNDecodingStep({required this.bundle}); } class MetadataCollectingStep implements BookyStep { - List imgsPaths = []; + Bundle bundle; Set isbns = {}; - MetadataCollectingStep({required this.imgsPaths, required this.isbns}); + MetadataCollectingStep({required this.bundle, required this.isbns}); } class AdEditingStep implements BookyStep { - List imgsPaths = []; + Bundle bundle; + Map metadata = {}; - AdEditingStep({required this.imgsPaths, required this.metadata}); + AdEditingStep({required this.bundle, required this.metadata}); } class MyApp extends StatefulWidget { @@ -40,7 +42,7 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BookyStep step = ImageSelectionStep(); + BookyStep step = BundleSelectionStep(); /* AdEditingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg' ], metadata: { @@ -64,11 +66,8 @@ class _MyAppState extends State { title: 'BookAdPublisher', theme: ThemeData(primarySwatch: Colors.blue), home: switch (step) { - ImageSelectionStep() => drag_and_drop.SelectImages(onSelect: (List paths) { - setState(() { - step = ISBNDecodingStep(imgsPaths: paths); - }); - }), + BundleSelectionStep() => + BundleSelection(onSubmit: (ISBNDecodingStep newStep) => setState(() => step = newStep)), ISBNDecodingStep() => ISBNDecodingWidget( step: step as ISBNDecodingStep, onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep)), diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index 7f9aae2..ae29840 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_rust_bridge_template/common.dart'; +import 'package:flutter_rust_bridge_template/helpers.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'main.dart'; @@ -219,7 +219,7 @@ class _MetadataCollectingWidgetState extends State { child: ElevatedButton( onPressed: () { widget.onSubmit(AdEditingStep( - imgsPaths: widget.step.imgsPaths, + bundle: widget.step.bundle, metadata: metadata.map((key, value) => MapEntry(key, value.manual)))); }, child: const Text('Validate Metadatas')), diff --git a/pubspec.yaml b/pubspec.yaml index dd525bb..b6937f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: super_clipboard: ^0.2.3+1 json_annotation: ^4.8.0 jwt_decoder: ^2.0.1 + path: ^1.8.3 dev_dependencies: flutter_test: From 5975106514ed87935401c8aa9a74a15098001cd4 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 18 Apr 2023 22:27:45 +0200 Subject: [PATCH 060/150] Add drag and drop images --- lib/ad_editing.dart | 4 ++- lib/draggable_files_widget.dart | 51 +++++++++++++++++++++++++++++++++ lib/main.dart | 24 ++++++++++------ pubspec.yaml | 9 +++--- 4 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 lib/draggable_files_widget.dart diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index a982246..6ab87d6 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -7,6 +7,7 @@ import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_inf import 'package:jwt_decoder/jwt_decoder.dart'; import 'credential.dart'; +import 'draggable_files_widget.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'helpers.dart'; @@ -122,10 +123,11 @@ class _AdEditingWidgetState extends State { padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row(children: [ const Icon( - Icons.image, + Icons.collections, color: Colors.grey, ), const SizedBox(width: 16), + DraggableFilesWidget(uris: ad.imgsPath.map((path) => Uri.file(path))), ...ad.imgsPath.map((img) => ImageWidget(File(img))).toList(), ]), ), diff --git a/lib/draggable_files_widget.dart b/lib/draggable_files_widget.dart new file mode 100644 index 0000000..9a35d1c --- /dev/null +++ b/lib/draggable_files_widget.dart @@ -0,0 +1,51 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:super_drag_and_drop/super_drag_and_drop.dart'; +import 'package:super_native_extensions/raw_drag_drop.dart' as raw; +import 'package:super_native_extensions/widgets.dart'; + +class DraggableFilesWidget extends StatelessWidget { + const DraggableFilesWidget({required this.uris}); + + final Iterable uris; + + @override + Widget build(BuildContext context) => FallbackSnapshotWidget( + child: Builder( + builder: (context) => BaseDraggableWidget( + hitTestBehavior: HitTestBehavior.deferToChild, + child: const Text('Drag and drop images'), + dragConfiguration: (location, session) async { + Future getSnapshot(Offset location) async { + final snapshotter = Snapshotter.of(context)!; + final dragSnapshot = await snapshotter.getSnapshot(location, SnapshotType.drag); + + raw.TargetedImage? liftSnapshot; + if (defaultTargetPlatform == TargetPlatform.iOS) { + liftSnapshot = await snapshotter.getSnapshot(location, SnapshotType.lift); + } + + final snapshot = dragSnapshot ?? liftSnapshot ?? await snapshotter.getSnapshot(location, null); + + if (snapshot == null) { + return null; + } + + return DragImage(image: snapshot, liftImage: liftSnapshot); + } + + final dragImage = (await getSnapshot(const Offset(0, 0)))!; + // final r = dragImage!.image.rect; + // print('r = $r'); + + return DragConfiguration( + items: uris + .map((uri) => DragConfigurationItem( + item: DragItem()..add(Formats.uri(NamedUri(uri))), image: dragImage)) + .toList(), + allowedOperations: [DropOperation.copy], + ); + }, + )), + ); +} diff --git a/lib/main.dart b/lib/main.dart index 60ed4e4..a0fd790 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,10 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/helpers.dart'; import 'ad_editing.dart'; +import 'bridge_definitions.dart'; import 'bundle.dart'; import 'bundle_selection.dart'; import 'isbn_decoding.dart'; @@ -42,15 +45,18 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BookyStep step = BundleSelectionStep(); - /* AdEditingStep(imgsPaths: [ - '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg' - ], metadata: { - 'myisbn': BookMetaData( - title: 'Mock title', - authors: [Author(firstName: 'Mock firstname', lastName: 'mock lastname')], - keywords: ['mock kw']) - });*/ + BookyStep step = //BundleSelectionStep(); + AdEditingStep( + bundle: Bundle( + Directory('/home/julien/Perso/LeBonCoin/chain_automatisation/open_cv_test/test_images/booky_example/normal')), + metadata: { + 'myisbn': BookMetaDataManual( + title: 'Mock title', + authors: [const Author(firstName: 'Mock firstname', lastName: 'mock lastname')], + keywords: ['mock kw'], + priceCent: 1234) + }, + ); /* MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', diff --git a/pubspec.yaml b/pubspec.yaml index b6937f2..fe09f42 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,15 +17,16 @@ environment: dependencies: flutter: sdk: flutter + collection: ^1.17.1 ffi: ^2.0.1 flutter_rust_bridge: ^1.45.0 - meta: ^1.8.0 - super_drag_and_drop: ^0.2.3 - collection: ^1.17.1 - super_clipboard: ^0.2.3+1 json_annotation: ^4.8.0 jwt_decoder: ^2.0.1 + meta: ^1.8.0 path: ^1.8.3 + super_clipboard: ^0.3.0+2 + super_drag_and_drop: ^0.3.0+2 + super_native_extensions: ^0.3.0+2 dev_dependencies: flutter_test: From f32ebfcfd5ec2c79c4dd3f67b6629ed102dec158 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 18 Apr 2023 22:31:51 +0200 Subject: [PATCH 061/150] Remove LBC tokenm cookie and publish button --- lib/ad_editing.dart | 54 --------------------------------------------- 1 file changed, 54 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 6ab87d6..370591d 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -4,7 +4,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; -import 'package:jwt_decoder/jwt_decoder.dart'; import 'credential.dart'; import 'draggable_files_widget.dart'; @@ -131,59 +130,6 @@ class _AdEditingWidgetState extends State { ...ad.imgsPath.map((img) => ImageWidget(File(img))).toList(), ]), ), - TextFormField( - initialValue: credential.lbcToken, - onChanged: (newText) => setState(() => credential.lbcToken = newText), - decoration: const InputDecoration( - icon: Icon(Icons.key), - labelText: 'LBC Bearer token', - ), - style: const TextStyle(fontSize: 20), - autovalidateMode: AutovalidateMode.always, - validator: (token) { - try { - final remainingDuration = JwtDecoder.getRemainingTime(token!); - if (remainingDuration.isNegative) { - return 'Token expired'; - } - } on FormatException catch (e) { - return 'Not a JWT token'; - } - return null; - }, - ), - TextFormField( - initialValue: credential.dataDomeCookie, - onChanged: (newText) => setState(() => credential.dataDomeCookie = newText), - decoration: const InputDecoration( - icon: Icon(Icons.cookie), - labelText: 'datadome cookie', - ), - style: const TextStyle(fontSize: 20), - ), - ElevatedButton( - onPressed: (ad.title.length < 2 || - ad.description.length < 15 || - ad.description.length > 4000 || - ad.priceCent == null) - ? null - : () async { - print('Try to publish...'); - - final res = await api.publishAd( - ad: ad, - credential: LbcCredential( - lbcToken: credential.lbcToken, datadomeCookie: credential.dataDomeCookie)); - - if (!context.mounted) return; - if (res) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Success'))); - credential.saveToFile(); - } else { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Failure'))); - } - }, - child: const Text('Publish')) ], ), ), From b8340b2a9d2b55a90b933c3c6897c5f875d7af71 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 18 Apr 2023 22:50:40 +0200 Subject: [PATCH 062/150] Add state and weight in AdEditing --- lib/ad_editing.dart | 17 +++++++++++++++++ lib/main.dart | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 370591d..5c473c1 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -82,6 +82,7 @@ class _AdEditingWidgetState extends State { @override Widget build(BuildContext context) { + final metadata = widget.step.bundle.metadata; return Scaffold( appBar: AppBar(title: const Text('Ad editing')), body: Padding( @@ -98,6 +99,14 @@ class _AdEditingWidgetState extends State { ), style: const TextStyle(fontSize: 30), ), + TextFormField( + initialValue: metadata.itemState?.loc, + decoration: const InputDecoration( + icon: Icon(Icons.diamond), + labelText: 'State', + ), + style: const TextStyle(fontSize: 20), + ), TextFormField( initialValue: ad.description, maxLines: null, @@ -118,6 +127,14 @@ class _AdEditingWidgetState extends State { ), style: const TextStyle(fontSize: 20), ), + TextFormField( + initialValue: metadata.weightGrams?.toString(), + decoration: const InputDecoration( + icon: Icon(Icons.scale), + labelText: 'Weight (grams)', + ), + style: const TextStyle(fontSize: 20), + ), Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), child: Row(children: [ diff --git a/lib/main.dart b/lib/main.dart index a0fd790..c5527ce 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -53,7 +53,8 @@ class _MyAppState extends State { 'myisbn': BookMetaDataManual( title: 'Mock title', authors: [const Author(firstName: 'Mock firstname', lastName: 'mock lastname')], - keywords: ['mock kw'], + blurb: 'This is a mock blurb', + keywords: ['kw1', 'kw2', 'kw3'], priceCent: 1234) }, ); From ec4cc4e9c5054d1632d6762baeadd3b18694c4af Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 18 Apr 2023 23:38:52 +0200 Subject: [PATCH 063/150] Add copy button for text field --- lib/ad_editing.dart | 19 ++++++++++--------- lib/copiable_text_field.dart | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 lib/copiable_text_field.dart diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 5c473c1..8009aaf 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; +import 'copiable_text_field.dart'; import 'credential.dart'; import 'draggable_files_widget.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; @@ -90,15 +91,15 @@ class _AdEditingWidgetState extends State { child: SingleChildScrollView( child: Column( children: [ - TextFormField( - initialValue: ad.title, + CopiableTextField(TextFormField( + controller: TextEditingController(text: ad.title), onChanged: (newText) => setState(() => ad.title = newText), decoration: const InputDecoration( icon: Icon(Icons.title), labelText: 'Ad title', ), style: const TextStyle(fontSize: 30), - ), + )), TextFormField( initialValue: metadata.itemState?.loc, decoration: const InputDecoration( @@ -107,8 +108,8 @@ class _AdEditingWidgetState extends State { ), style: const TextStyle(fontSize: 20), ), - TextFormField( - initialValue: ad.description, + CopiableTextField(TextFormField( + controller: TextEditingController(text: ad.description), maxLines: null, scrollPhysics: const NeverScrollableScrollPhysics(), onChanged: (newText) => setState(() => ad.description = newText), @@ -116,9 +117,9 @@ class _AdEditingWidgetState extends State { icon: Icon(Icons.text_snippet), labelText: 'Ad description', ), - ), - TextFormField( - initialValue: ad.priceCent /*?*/ .divide(100).toString(), + )), + CopiableTextField(TextFormField( + controller: TextEditingController(text: ad.priceCent.divide(100).toString()), onChanged: (newText) => setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), decoration: const InputDecoration( @@ -126,7 +127,7 @@ class _AdEditingWidgetState extends State { labelText: 'Price', ), style: const TextStyle(fontSize: 20), - ), + )), TextFormField( initialValue: metadata.weightGrams?.toString(), decoration: const InputDecoration( diff --git a/lib/copiable_text_field.dart b/lib/copiable_text_field.dart new file mode 100644 index 0000000..10c164d --- /dev/null +++ b/lib/copiable_text_field.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:super_clipboard/super_clipboard.dart'; + +class CopiableTextField extends StatelessWidget { + const CopiableTextField(this.textFormField); + final TextFormField textFormField; + + @override + Widget build(BuildContext context) => Row( + children: [ + IconButton( + onPressed: () async { + final item = DataWriterItem(); + item.add(Formats.plainText(textFormField.controller!.text)); + await ClipboardWriter.instance.write([item]); + }, + icon: const Icon(Icons.copy)), + Expanded(child: textFormField), + ], + ); +} From 1cf3257d6a180c7e148474d0a084cd6e43c5039a Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 18 Apr 2023 23:40:48 +0200 Subject: [PATCH 064/150] Remove unused credentials --- lib/ad_editing.dart | 5 ----- lib/credential.dart | 29 ----------------------------- lib/credential.g.dart | 18 ------------------ 3 files changed, 52 deletions(-) delete mode 100644 lib/credential.dart delete mode 100644 lib/credential.g.dart diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 8009aaf..f5853b0 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -6,7 +6,6 @@ import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; import 'copiable_text_field.dart'; -import 'credential.dart'; import 'draggable_files_widget.dart'; import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'helpers.dart'; @@ -34,7 +33,6 @@ String _bookFormatTitleAndAuthor(String title, Iterable authors) { class _AdEditingWidgetState extends State { late Ad ad; - late Credential credential; @override void initState() { @@ -58,9 +56,6 @@ class _AdEditingWidgetState extends State { description: description, priceCent: totalPrice, imgsPath: widget.step.bundle.images.map((e) => e.path).toList()); - - credential = Credential.loadFromFile(); - print('credential ${credential.lbcToken} ${credential.dataDomeCookie}'); } String _getDescription(Iterable> metadataFromIsbn) { diff --git a/lib/credential.dart b/lib/credential.dart deleted file mode 100644 index b7790ac..0000000 --- a/lib/credential.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:json_annotation/json_annotation.dart'; - -part 'credential.g.dart'; - -@JsonSerializable() -class Credential { - String lbcToken; - String dataDomeCookie; - - Credential({required this.lbcToken, required this.dataDomeCookie}); - - static final _file = File('credential.json'); - - void saveToFile() { - _file.writeAsStringSync(jsonEncode(toJson())); - } - - factory Credential.loadFromFile() { - final json = _file.readAsStringSync(); - return Credential.fromJson(jsonDecode(json) as Map); - } - - factory Credential.fromJson(Map json) => _$CredentialFromJson(json); - - Map toJson() => _$CredentialToJson(this); -} diff --git a/lib/credential.g.dart b/lib/credential.g.dart deleted file mode 100644 index 8a8e092..0000000 --- a/lib/credential.g.dart +++ /dev/null @@ -1,18 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'credential.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -Credential _$CredentialFromJson(Map json) => Credential( - lbcToken: json['lbcToken'] as String, - dataDomeCookie: json['dataDomeCookie'] as String, - ); - -Map _$CredentialToJson(Credential instance) => - { - 'lbcToken': instance.lbcToken, - 'dataDomeCookie': instance.dataDomeCookie, - }; From a6bd4ab848b723ab2042c0c511879402cc169090 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 19 Apr 2023 00:30:27 +0200 Subject: [PATCH 065/150] Fix Authors not taken into account in AdEditing step. Better hanle Author.toText() with empty first name. Use KtMap --- lib/ad_editing.dart | 8 ++++++-- lib/helpers.dart | 6 +++++- lib/isbn_decoding.dart | 8 ++++---- lib/main.dart | 9 +++------ lib/metadata_collecting.dart | 18 ++++++++++++++++-- pubspec.yaml | 1 + 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index f5853b0..60abf67 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -28,7 +28,7 @@ String vecFmt(Iterable it) { } String _bookFormatTitleAndAuthor(String title, Iterable authors) { - return '"$title" ${vecFmt(authors.map((a) => '${a.firstName} ${a.lastName}'))}'; + return '"$title" ${vecFmt(authors.map((a) => a.toText()))}'; } class _AdEditingWidgetState extends State { @@ -39,7 +39,11 @@ class _AdEditingWidgetState extends State { super.initState(); final metadataFromIsbn = widget.step.metadata.entries; - final title = metadataFromIsbn.length == 1 ? (metadataFromIsbn.first.value.title ?? '') : ''; + var title = ''; + if (metadataFromIsbn.length == 1) { + final onlyMetadata = metadataFromIsbn.single.value; + title = _bookFormatTitleAndAuthor(onlyMetadata.title!, onlyMetadata.authors); + } var description = _getDescription(metadataFromIsbn); description += '\n\n' + personal_info.customMessage; diff --git a/lib/helpers.dart b/lib/helpers.dart index 42809f7..e8e20c1 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -48,8 +48,12 @@ class AsyncSnapshotWidget extends StatelessWidget { } } +extension AuthorExt on Author { + String toText() => [firstName, lastName].where((s) => s.isNotEmpty).join(' '); +} + extension AuthorsExt on List { - String toText() => map((a) => '${a.firstName} ${a.lastName}').join('\n'); + String toText() => map((a) => a.toText()).join('\n'); } extension IntExt on int { diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index c4efacf..3a5daee 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; +import 'package:kt_dart/collection.dart'; import 'helpers.dart'; @@ -15,8 +16,7 @@ class ISBNDecodingWidget extends StatefulWidget { } class _ISBNDecodingWidgetState extends State { - // TODO: Don't use Map because operator[] accept Object as parameter instead of o Key type - Map>> isbns = {}; + KtMutableMap>> isbns = KtMutableMap.empty(); @override void initState() { @@ -50,7 +50,7 @@ class _ISBNDecodingWidgetState extends State { children: [ ImageWidget(imgPath), FutureBuilder( - future: isbns[imgPath]!, + future: isbns[imgPath.path]!, builder: (context, snap) { if (snap.hasData == false) { return const CircularProgressIndicator(); @@ -65,7 +65,7 @@ class _ISBNDecodingWidgetState extends State { Padding( padding: const EdgeInsets.all(8.0), child: FutureBuilder( - future: Future.wait(isbns.values), + future: Future.wait(isbns.values.iter), builder: (context, snap) { return ElevatedButton( onPressed: () { diff --git a/lib/main.dart b/lib/main.dart index c5527ce..293609e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,10 +1,7 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/helpers.dart'; import 'ad_editing.dart'; -import 'bridge_definitions.dart'; import 'bundle.dart'; import 'bundle_selection.dart'; import 'isbn_decoding.dart'; @@ -45,8 +42,8 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - BookyStep step = //BundleSelectionStep(); - AdEditingStep( + BookyStep step = BundleSelectionStep(); + /*AdEditingStep( bundle: Bundle( Directory('/home/julien/Perso/LeBonCoin/chain_automatisation/open_cv_test/test_images/booky_example/normal')), metadata: { @@ -57,7 +54,7 @@ class _MyAppState extends State { keywords: ['kw1', 'kw2', 'kw3'], priceCent: 1234) }, - ); + );*/ /* MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', diff --git a/lib/metadata_collecting.dart b/lib/metadata_collecting.dart index ae29840..e1620e1 100644 --- a/lib/metadata_collecting.dart +++ b/lib/metadata_collecting.dart @@ -28,8 +28,9 @@ class MetadataCollectingWidget extends StatefulWidget { final MetadataCollectingStep step; final void Function(AdEditingStep newStep) onSubmit; - final blurbTextFieldController = TextEditingController(); final titleTextFieldController = TextEditingController(); + final authorsTextFieldController = TextEditingController(); + final blurbTextFieldController = TextEditingController(); final priceTextFieldController = TextEditingController(); @override @@ -57,6 +58,13 @@ class _MetadataCollectingWidgetState extends State { }); } + void _updateManualAuthors(String isbn, String newAuthor) { + setState(() { + metadata[isbn]!.manual.authors = [Author(firstName: '', lastName: newAuthor)]; + widget.authorsTextFieldController.text = newAuthor; + }); + } + void _updateManualBlurb(String isbn, String newBlurb) { setState(() { metadata[isbn]!.manual.blurb = newBlurb; @@ -79,6 +87,12 @@ class _MetadataCollectingWidgetState extends State { replaceIfBetterString(value.title, metadata[isbn]!.manual.title!, () { _updateManualTitle(isbn, value.title!); }); + + // TODO: handle list of authors + final joinedAuthors = value.authors.toText(); + replaceIfBetterString(joinedAuthors, metadata[isbn]!.manual.authors.toText(), () { + _updateManualAuthors(isbn, joinedAuthors); + }); replaceIfBetterString(value.blurb, metadata[isbn]!.manual.blurb!, () { _updateManualBlurb(isbn, value.blurb!); }); @@ -136,7 +150,7 @@ class _MetadataCollectingWidgetState extends State { FutureWidget( future: metadata[isbn]!.mdFromProviders.entries.first.value, builder: (data) => TextFormField( - initialValue: data?.authors.toText(), + controller: widget.authorsTextFieldController, onChanged: (newText) => setState(() => manual.authors = newText.split('\n').map((line) => Author(firstName: '', lastName: line)).toList()), decoration: const InputDecoration( diff --git a/pubspec.yaml b/pubspec.yaml index fe09f42..d68f81f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: super_clipboard: ^0.3.0+2 super_drag_and_drop: ^0.3.0+2 super_native_extensions: ^0.3.0+2 + kt_dart: ^1.1.0 dev_dependencies: flutter_test: From cf4be4a006c0b001863ae091314d36645b78a204 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 19 Apr 2023 21:29:13 +0200 Subject: [PATCH 066/150] BundleSelection: sort images by date, antialiase --- lib/bundle_selection.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/bundle_selection.dart b/lib/bundle_selection.dart index c8bd3cf..0e9ca2e 100644 --- a/lib/bundle_selection.dart +++ b/lib/bundle_selection.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:path/path.dart' as path; @@ -51,9 +52,14 @@ class BundleWidget extends StatelessWidget { .listSync() .whereType() .where((f) => path.extension(f.path) == '.jpg') + .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())) .map((f) => Padding( padding: const EdgeInsets.all(8.0), - child: Image.file(f, height: 150), + child: Image.file( + f, + height: 150, + filterQuality: FilterQuality.medium, + ), )) .toList(), )), From 35010bed88eef019a6a32378e0439d1cc8d3bb79 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 19 Apr 2023 22:42:12 +0200 Subject: [PATCH 067/150] AdEditing: images are draggable, add "Mark as Publish" button --- lib/ad_editing.dart | 25 +++++++++++++++++++++++-- lib/bundle.dart | 7 ++++++- lib/bundle_selection.dart | 7 ++----- lib/draggable_files_widget.dart | 5 +++-- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index 60abf67..e5bd61d 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -4,6 +4,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; +import 'package:path/path.dart' as path; import 'copiable_text_field.dart'; import 'draggable_files_widget.dart'; @@ -143,10 +144,30 @@ class _AdEditingWidgetState extends State { color: Colors.grey, ), const SizedBox(width: 16), - DraggableFilesWidget(uris: ad.imgsPath.map((path) => Uri.file(path))), - ...ad.imgsPath.map((img) => ImageWidget(File(img))).toList(), + DraggableFilesWidget( + uris: ad.imgsPath.map((path) => Uri.file(path)), + child: Column( + children: [ + Row( + children: ad.imgsPath.map((img) => ImageWidget(File(img))).toList(), + ), + const Text('Drag and drop images') + ], + ), + ), ]), ), + ElevatedButton( + onPressed: () { + final d = widget.step.bundle.directory; + final segments = path.split(d.path); + segments[segments.length - 2] = 'booky_done'; + d.renameSync(path.joinAll(segments)); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Moved'), + )); + }, + child: const Text('Mark as published')) ], ), ), diff --git a/lib/bundle.dart b/lib/bundle.dart index 0c954e7..02fff96 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:collection/collection.dart'; import 'package:flutter_rust_bridge_template/common.dart'; import 'package:path/path.dart' as path; @@ -10,7 +11,11 @@ class Bundle { final Directory directory; Iterable get images { - return directory.listSync().whereType().where((file) => path.extension(file.path) == '.jpg'); + return directory + .listSync() + .whereType() + .where((file) => path.extension(file.path) == '.jpg') + .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())); } Metadata get metadata { diff --git a/lib/bundle_selection.dart b/lib/bundle_selection.dart index 0e9ca2e..00b6f2f 100644 --- a/lib/bundle_selection.dart +++ b/lib/bundle_selection.dart @@ -6,6 +6,7 @@ import 'package:flutter_rust_bridge_template/main.dart'; import 'package:path/path.dart' as path; import 'bundle.dart'; +import 'helpers.dart'; class BundleSelection extends StatelessWidget { const BundleSelection({required this.onSubmit}); @@ -55,11 +56,7 @@ class BundleWidget extends StatelessWidget { .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())) .map((f) => Padding( padding: const EdgeInsets.all(8.0), - child: Image.file( - f, - height: 150, - filterQuality: FilterQuality.medium, - ), + child: ImageWidget(f), )) .toList(), )), diff --git a/lib/draggable_files_widget.dart b/lib/draggable_files_widget.dart index 9a35d1c..8cb6c77 100644 --- a/lib/draggable_files_widget.dart +++ b/lib/draggable_files_widget.dart @@ -5,16 +5,17 @@ import 'package:super_native_extensions/raw_drag_drop.dart' as raw; import 'package:super_native_extensions/widgets.dart'; class DraggableFilesWidget extends StatelessWidget { - const DraggableFilesWidget({required this.uris}); + const DraggableFilesWidget({required this.uris, required this.child}); final Iterable uris; + final Widget child; @override Widget build(BuildContext context) => FallbackSnapshotWidget( child: Builder( builder: (context) => BaseDraggableWidget( hitTestBehavior: HitTestBehavior.deferToChild, - child: const Text('Drag and drop images'), + child: child, dragConfiguration: (location, session) async { Future getSnapshot(Offset location) async { final snapshotter = Snapshotter.of(context)!; From 87513e4fb5aab507673ccecc1b9864cf16917088 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 19 Apr 2023 22:55:25 +0200 Subject: [PATCH 068/150] Come back to BundleSelection after publish --- lib/ad_editing.dart | 3 ++- lib/main.dart | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/ad_editing.dart b/lib/ad_editing.dart index e5bd61d..ffc81d8 100644 --- a/lib/ad_editing.dart +++ b/lib/ad_editing.dart @@ -14,7 +14,7 @@ import 'helpers.dart'; class AdEditingWidget extends StatefulWidget { const AdEditingWidget({required this.step, required this.onSubmit}); final AdEditingStep step; - final void Function(bool newStep) onSubmit; + final void Function() onSubmit; @override State createState() => _AdEditingWidgetState(); @@ -166,6 +166,7 @@ class _AdEditingWidgetState extends State { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Moved'), )); + widget.onSubmit(); }, child: const Text('Mark as published')) ], diff --git a/lib/main.dart b/lib/main.dart index 293609e..2aef8c9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -78,9 +78,8 @@ class _MyAppState extends State { MetadataCollectingStep() => MetadataCollectingWidget( step: step as MetadataCollectingStep, onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), - AdEditingStep() => AdEditingWidget( - step: step as AdEditingStep, - onSubmit: (bool publishSuccess) => print('onSubmit with bool = $publishSuccess')), + AdEditingStep() => + AdEditingWidget(step: step as AdEditingStep, onSubmit: () => setState(() => step = BundleSelectionStep())), BookyStep() => throw UnimplementedError('Not possible') }); } From 9861383599af684f113882118c5628ea775259f9 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 19 Apr 2023 23:29:16 +0200 Subject: [PATCH 069/150] Fix ISBN decoding split on new line --- lib/isbn_decoding.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/isbn_decoding.dart b/lib/isbn_decoding.dart index 3a5daee..158002e 100644 --- a/lib/isbn_decoding.dart +++ b/lib/isbn_decoding.dart @@ -33,7 +33,7 @@ class _ISBNDecodingWidgetState extends State { throw Exception('decoder status is ${decoderProcess.exitCode}'); } final s = decoderProcess.stdout as String; - return s.split(' ').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); + return s.split('\n').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); }); }); } From e2ca44804233be70968dee3453816dbe75f214ec Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 27 Apr 2023 21:22:29 +0200 Subject: [PATCH 070/150] Move 4 steps into enrichment/ --- lib/{ => enrichment}/ad_editing.dart | 10 +-- lib/{ => enrichment}/bundle_selection.dart | 6 +- lib/enrichment/enrichment.dart | 82 ++++++++++++++++++ lib/{ => enrichment}/isbn_decoding.dart | 4 +- lib/{ => enrichment}/metadata_collecting.dart | 4 +- lib/main.dart | 83 +------------------ 6 files changed, 96 insertions(+), 93 deletions(-) rename lib/{ => enrichment}/ad_editing.dart (96%) rename lib/{ => enrichment}/bundle_selection.dart (94%) create mode 100644 lib/enrichment/enrichment.dart rename lib/{ => enrichment}/isbn_decoding.dart (97%) rename lib/{ => enrichment}/metadata_collecting.dart (99%) diff --git a/lib/ad_editing.dart b/lib/enrichment/ad_editing.dart similarity index 96% rename from lib/ad_editing.dart rename to lib/enrichment/ad_editing.dart index ffc81d8..ed3875d 100644 --- a/lib/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -2,14 +2,14 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/main.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; import 'package:path/path.dart' as path; -import 'copiable_text_field.dart'; -import 'draggable_files_widget.dart'; -import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; -import 'helpers.dart'; +import '../copiable_text_field.dart'; +import '../draggable_files_widget.dart'; +import '../ffi.dart' if (dart.library.html) 'ffi_web.dart'; +import '../helpers.dart'; +import 'enrichment.dart'; class AdEditingWidget extends StatefulWidget { const AdEditingWidget({required this.step, required this.onSubmit}); diff --git a/lib/bundle_selection.dart b/lib/enrichment/bundle_selection.dart similarity index 94% rename from lib/bundle_selection.dart rename to lib/enrichment/bundle_selection.dart index 00b6f2f..87bc253 100644 --- a/lib/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -2,11 +2,11 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/main.dart'; import 'package:path/path.dart' as path; -import 'bundle.dart'; -import 'helpers.dart'; +import '../bundle.dart'; +import '../helpers.dart'; +import 'enrichment.dart'; class BundleSelection extends StatelessWidget { const BundleSelection({required this.onSubmit}); diff --git a/lib/enrichment/enrichment.dart b/lib/enrichment/enrichment.dart new file mode 100644 index 0000000..3e49741 --- /dev/null +++ b/lib/enrichment/enrichment.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/helpers.dart'; + +import '../bundle.dart'; +import 'ad_editing.dart'; +import 'bundle_selection.dart'; +import 'isbn_decoding.dart'; +import 'metadata_collecting.dart'; + +sealed class BookyStep {} + +class BundleSelectionStep implements BookyStep {} + +class ISBNDecodingStep implements BookyStep { + Bundle bundle; + ISBNDecodingStep({required this.bundle}); +} + +class MetadataCollectingStep implements BookyStep { + Bundle bundle; + Set isbns = {}; + MetadataCollectingStep({required this.bundle, required this.isbns}); +} + +class AdEditingStep implements BookyStep { + Bundle bundle; + + Map metadata = {}; + + AdEditingStep({required this.bundle, required this.metadata}); +} + +class EnrichmentApp extends StatefulWidget { + const EnrichmentApp({Key? key}) : super(key: key); + + @override + State createState() => _EnrichmentAppState(); +} + +class _EnrichmentAppState extends State { + BookyStep step = BundleSelectionStep(); + /*AdEditingStep( + bundle: Bundle( + Directory('/home/julien/Perso/LeBonCoin/chain_automatisation/open_cv_test/test_images/booky_example/normal')), + metadata: { + 'myisbn': BookMetaDataManual( + title: 'Mock title', + authors: [const Author(firstName: 'Mock firstname', lastName: 'mock lastname')], + blurb: 'This is a mock blurb', + keywords: ['kw1', 'kw2', 'kw3'], + priceCent: 1234) + }, + );*/ + /* MetadataCollectingStep(imgsPaths: [ + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', + '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194758.jpg' + ], isbns: { + '9782253029854', + // '9782277223634', + });*/ + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'BookAdPublisher', + theme: ThemeData(primarySwatch: Colors.blue), + home: switch (step) { + BundleSelectionStep() => + BundleSelection(onSubmit: (ISBNDecodingStep newStep) => setState(() => step = newStep)), + ISBNDecodingStep() => ISBNDecodingWidget( + step: step as ISBNDecodingStep, + onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep)), + MetadataCollectingStep() => MetadataCollectingWidget( + step: step as MetadataCollectingStep, + onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), + AdEditingStep() => + AdEditingWidget(step: step as AdEditingStep, onSubmit: () => setState(() => step = BundleSelectionStep())), + BookyStep() => throw UnimplementedError('Not possible') + }); + } +} diff --git a/lib/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart similarity index 97% rename from lib/isbn_decoding.dart rename to lib/enrichment/isbn_decoding.dart index 158002e..6d52b04 100644 --- a/lib/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/main.dart'; import 'package:kt_dart/collection.dart'; -import 'helpers.dart'; +import '../helpers.dart'; +import 'enrichment.dart'; class ISBNDecodingWidget extends StatefulWidget { const ISBNDecodingWidget({required this.step, required this.onSubmit}); diff --git a/lib/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart similarity index 99% rename from lib/metadata_collecting.dart rename to lib/enrichment/metadata_collecting.dart index e1620e1..f1182b7 100644 --- a/lib/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_rust_bridge_template/helpers.dart'; -import 'ffi.dart' if (dart.library.html) 'ffi_web.dart'; -import 'main.dart'; +import '../ffi.dart' if (dart.library.html) 'ffi_web.dart'; +import 'enrichment.dart'; const noneText = Text('None', style: TextStyle(fontStyle: FontStyle.italic)); diff --git a/lib/main.dart b/lib/main.dart index 2aef8c9..b29c5a8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,86 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/helpers.dart'; -import 'ad_editing.dart'; -import 'bundle.dart'; -import 'bundle_selection.dart'; -import 'isbn_decoding.dart'; -import 'metadata_collecting.dart'; +import 'enrichment/enrichment.dart'; void main() { - runApp(const MyApp()); -} - -sealed class BookyStep {} - -class BundleSelectionStep implements BookyStep {} - -class ISBNDecodingStep implements BookyStep { - Bundle bundle; - ISBNDecodingStep({required this.bundle}); -} - -class MetadataCollectingStep implements BookyStep { - Bundle bundle; - Set isbns = {}; - MetadataCollectingStep({required this.bundle, required this.isbns}); -} - -class AdEditingStep implements BookyStep { - Bundle bundle; - - Map metadata = {}; - - AdEditingStep({required this.bundle, required this.metadata}); -} - -class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); - - @override - State createState() => _MyAppState(); -} - -class _MyAppState extends State { - BookyStep step = BundleSelectionStep(); - /*AdEditingStep( - bundle: Bundle( - Directory('/home/julien/Perso/LeBonCoin/chain_automatisation/open_cv_test/test_images/booky_example/normal')), - metadata: { - 'myisbn': BookMetaDataManual( - title: 'Mock title', - authors: [const Author(firstName: 'Mock firstname', lastName: 'mock lastname')], - blurb: 'This is a mock blurb', - keywords: ['kw1', 'kw2', 'kw3'], - priceCent: 1234) - }, - );*/ - /* MetadataCollectingStep(imgsPaths: [ - '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', - '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', - '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', - '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194758.jpg' - ], isbns: { - '9782253029854', - // '9782277223634', - });*/ - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'BookAdPublisher', - theme: ThemeData(primarySwatch: Colors.blue), - home: switch (step) { - BundleSelectionStep() => - BundleSelection(onSubmit: (ISBNDecodingStep newStep) => setState(() => step = newStep)), - ISBNDecodingStep() => ISBNDecodingWidget( - step: step as ISBNDecodingStep, - onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep)), - MetadataCollectingStep() => MetadataCollectingWidget( - step: step as MetadataCollectingStep, - onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), - AdEditingStep() => - AdEditingWidget(step: step as AdEditingStep, onSubmit: () => setState(() => step = BundleSelectionStep())), - BookyStep() => throw UnimplementedError('Not possible') - }); - } + runApp(const EnrichmentApp()); } From 08a0e307ff842edbf5cdfcc973e525741c1caad0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 28 Apr 2023 00:02:24 +0200 Subject: [PATCH 071/150] Merge Camera_app. Compile on Android --- android/app/build.gradle | 4 +- android/gradle.properties | 2 +- lib/camera/camera.dart | 464 +++++++++++++++++++++++++++++++ lib/camera/draggable_widget.dart | 44 +++ lib/common.dart | 4 +- lib/main.dart | 31 ++- native/Cargo.toml | 2 +- pubspec.yaml | 4 + 8 files changed, 549 insertions(+), 6 deletions(-) create mode 100644 lib/camera/camera.dart create mode 100644 lib/camera/draggable_widget.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index c1b36a9..9430ef7 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -27,7 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + ndkVersion "25.2.9519653" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -47,7 +47,7 @@ android { applicationId "com.example.flutter_rust_bridge_template" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion + minSdkVersion 23 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/gradle.properties b/android/gradle.properties index 5c76181..ddbbffb 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -ANDROID_NDK=/home/julien/Android/Sdk/ndk-bundle/ +ANDROID_NDK=/home/julien/Android/Sdk/ndk/ diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart new file mode 100644 index 0000000..9fd6f04 --- /dev/null +++ b/lib/camera/camera.dart @@ -0,0 +1,464 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:camera/camera.dart'; +import 'package:flutter/material.dart'; +import 'package:gallery_saver/gallery_saver.dart'; +import 'package:path/path.dart' as path; +import 'package:permission_handler/permission_handler.dart'; + +import '../common.dart' as common; +import 'draggable_widget.dart'; + +/// Camera example home widget. +class CameraExampleHome extends StatefulWidget { + /// Default Constructor + const CameraExampleHome({Key? key}) : super(key: key); + + @override + State createState() { + return _CameraExampleHomeState(); + } +} + +/// Returns a suitable camera icon for [direction]. +IconData getCameraLensIcon(CameraLensDirection direction) { + switch (direction) { + case CameraLensDirection.back: + return Icons.camera_rear; + case CameraLensDirection.front: + return Icons.camera_front; + case CameraLensDirection.external: + return Icons.camera; + } + // This enum is from a different package, so a new value could be added at + // any time. The example should keep working if that happens. + // ignore: dead_code + return Icons.camera; +} + +void _logError(String code, String? message) { + // ignore: avoid_print + print('Error: $code${message == null ? '' : '\nError Message: $message'}'); +} + +class _CameraExampleHomeState extends State with WidgetsBindingObserver, TickerProviderStateMixin { + CameraController? controller; + XFile? imageFile; + late String bundleName; + + Directory get getBundleDir => Directory(path.join(common.bookyDir.path, bundleName)); + + void _generateNewFolderPath() { + bundleName = DateTime.now().toIso8601String().replaceAll(':', '_'); + } + + @override + void initState() { + super.initState(); + _generateNewFolderPath(); + WidgetsBinding.instance.addObserver(this); + + _onNewCameraSelected(_cameras.first); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + // #docregion AppLifecycle + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + + // App state changed before we got the chance to initialize. + if (cameraController == null || !cameraController.value.isInitialized) { + return; + } + + if (state == AppLifecycleState.inactive) { + cameraController.dispose(); + } else if (state == AppLifecycleState.resumed) { + _onNewCameraSelected(cameraController.description); + } + } + // #enddocregion AppLifecycle + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Booky Camera app'), actions: [ + PopupMenuButton( + itemBuilder: (_) => [ + PopupMenuItem( + child: const Text('Change camera'), + onTap: () async { + await Future.delayed(const Duration(seconds: 0), () async { + await showDialog( + context: context, + builder: (BuildContext _) => SimpleDialog( + title: const Text('Select camera'), + children: _cameras + .where((c) => c.lensDirection == CameraLensDirection.back) + .map((c) => SimpleDialogOption( + onPressed: () => _onNewCameraSelected(c), + child: Text('Camera ${c.name}'), + )) + .toList()), + ); + }); + }) + ], + ), + ]), + body: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(5.0), + child: BottomWidget( + directory: getBundleDir, + onSubmit: () { + setState(() { + _generateNewFolderPath(); + }); + Navigator.pop(context); + }), + ), + ], + ), + ); + } + + /// Display the preview from the camera (or a message if the preview is not available). + Widget _cameraPreviewWidget() { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { + return const Text( + 'Tap a camera', + style: TextStyle( + color: Colors.white, + fontSize: 24.0, + fontWeight: FontWeight.w900, + ), + ); + } else { + return CameraPreview( + controller!, + child: LayoutBuilder( + builder: (context, boxConstraints) => GestureDetector( + onTapDown: (TapDownDetails details) async { + _onViewFinderTap(details, boxConstraints); + // The auto focus is not instantaneous. We must wait a little while before taking the picture + // In release mode, if we + // wait 100 ms : blurry + // wait 300 ms : sharp + // The optimum delay shall lie between the bounds + await Future.delayed(const Duration(milliseconds: 300)); + _onTakePictureButtonPressed(); + }, + ), + ), + ); + } + } + + void showInSnackBar(String message) { + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); + } + + void _onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (controller == null) { + return; + } + + final CameraController cameraController = controller!; + + final Offset offset = Offset( + details.localPosition.dx / constraints.maxWidth, + details.localPosition.dy / constraints.maxHeight, + ); + cameraController.setExposurePoint(offset); + cameraController.setFocusPoint(offset); + } + + Future _onNewCameraSelected(CameraDescription cameraDescription) async { + final CameraController? oldController = controller; + if (oldController != null) { + // `controller` needs to be set to null before getting disposed, + // to avoid a race condition when we use the controller that is being + // disposed. This happens when camera permission dialog shows up, + // which triggers `didChangeAppLifecycleState`, which disposes and + // re-creates the controller. + controller = null; + await oldController.dispose(); + } + + final CameraController cameraController = CameraController( + cameraDescription, + ResolutionPreset.max, + imageFormatGroup: ImageFormatGroup.jpeg, + ); + + controller = cameraController; + + // If the controller is updated then update the UI. + cameraController.addListener(() { + if (mounted) { + setState(() {}); + } + if (cameraController.value.hasError) { + showInSnackBar('Camera error ${cameraController.value.errorDescription}'); + } + }); + + try { + await cameraController.initialize(); + } on CameraException catch (e) { + switch (e.code) { + case 'CameraAccessDenied': + showInSnackBar('You have denied camera access.'); + break; + case 'CameraAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable camera access.'); + break; + case 'CameraAccessRestricted': + // iOS only + showInSnackBar('Camera access is restricted.'); + break; + case 'AudioAccessDenied': + showInSnackBar('You have denied audio access.'); + break; + case 'AudioAccessDeniedWithoutPrompt': + // iOS only + showInSnackBar('Please go to Settings app to enable audio access.'); + break; + case 'AudioAccessRestricted': + // iOS only + showInSnackBar('Audio access is restricted.'); + break; + default: + _showCameraException(e); + break; + } + } + + if (mounted) { + setState(() {}); + } + } + + void _onTakePictureButtonPressed() { + takePicture().then((XFile? file) async { + if (mounted) { + if (file != null) { + GallerySaver.saveImage(file.path, albumName: 'booky/$bundleName', toDcim: true).then((bool? success) { + if (success != true) { + showInSnackBar('Error when saving image'); + } else { + setState(() { + imageFile = file; + }); + } + }); + } + } + }); + } + + Future onCaptureOrientationLockButtonPressed() async { + try { + if (controller != null) { + final CameraController cameraController = controller!; + if (cameraController.value.isCaptureOrientationLocked) { + await cameraController.unlockCaptureOrientation(); + showInSnackBar('Capture orientation unlocked'); + } else { + await cameraController.lockCaptureOrientation(); + showInSnackBar( + 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}'); + } + } + } on CameraException catch (e) { + _showCameraException(e); + } + } + + Future takePicture() async { + final CameraController? cameraController = controller; + if (cameraController == null || !cameraController.value.isInitialized) { + showInSnackBar('Error: select a camera first.'); + return null; + } + + if (cameraController.value.isTakingPicture) { + // A capture is already pending, do nothing. + return null; + } + + try { + final XFile file = await cameraController.takePicture(); + return file; + } on CameraException catch (e) { + _showCameraException(e); + return null; + } + } + + void _showCameraException(CameraException e) { + _logError(e.code, e.description); + showInSnackBar('Error: ${e.code}\n${e.description}'); + } +} + +class BottomWidget extends StatefulWidget { + const BottomWidget({required this.directory, required this.onSubmit}); + final Directory directory; + final void Function() onSubmit; + + @override + State createState() => _BottomWidgetState(); +} + +class _BottomWidgetState extends State { + @override + Widget build(BuildContext context) { + try { + final images = widget.directory.listSync().where((file) => path.extension(file.path) == '.jpg'); + return Row( + children: [ + _thumbnailWidget(images), + _addMetadataButton(context: context, directory: widget.directory, onSubmit: widget.onSubmit), + ], + ); + } on PathNotFoundException { + return const Text('Tap the screen to take a picture'); + } + } + + /// Display the thumbnail of the captured image or video. + Widget _thumbnailWidget(Iterable images) { + return Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.min, + children: images + .map((imgFile) => SizedBox( + width: 64, + height: 64, + child: DraggableWidget( + // Use a key otherwise if we delete an image, the image that will take its place will inherit the state of the deleted image + key: ValueKey(imgFile.path), + child: Image.file(File(imgFile.path)), + onVerticalDrag: () => setState(() { + imgFile.deleteSync(); + })), + )) + .toList(), + ), + ), + ); + } + + Widget _addMetadataButton( + {required BuildContext context, required Directory directory, required void Function() onSubmit}) => + IconButton( + icon: const Icon(Icons.keyboard_arrow_right), + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) => MetadataWidget(directory: directory, onSubmit: onSubmit))); +} + +class MetadataWidget extends StatefulWidget { + const MetadataWidget({ + required this.directory, + required this.onSubmit, + }); + final Directory directory; + final void Function() onSubmit; + + @override + State createState() => _MetadataWidgetState(); +} + +class _MetadataWidgetState extends State { + var metadata = common.Metadata(); + + @override + Widget build(BuildContext context) { + return SimpleDialog( + title: const Text('Add the final metadata'), + children: [ + TextFormField( + initialValue: '', + autofocus: true, + onChanged: (newText) => setState(() => metadata.weightGrams = int.parse(newText)), + keyboardType: TextInputType.number, + decoration: const InputDecoration( + icon: Icon(Icons.scale), + labelText: 'Weight in grams', + ), + style: const TextStyle(fontSize: 20), + ), + DropdownButton( + hint: const Text('Book state'), + value: metadata.itemState, + items: common.ItemState.values.map((s) => DropdownMenuItem(value: s, child: Text(s.loc))).toList(), + onChanged: (state) => setState(() { + metadata.itemState = state; + })), + IconButton( + icon: const Icon(Icons.save), + onPressed: () async { + final managePerm = await Permission.manageExternalStorage.request(); + print('managePerm = $managePerm'); + File(path.join(widget.directory.path, 'metadata.json')).writeAsStringSync(jsonEncode(metadata.toJson())); + widget.onSubmit(); + }) + ], + ); + } +} + +/// CameraApp is the Main Application. +class CameraApp extends StatelessWidget { + /// Default Constructor + const CameraApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: CameraExampleHome(), + ); + } +} + +List _cameras = []; +/* + +Future main() async { + runApp(const MaterialApp(home: Explorer())); + // Fetch the available cameras before initializing the app. + */ +/*try { + WidgetsFlutterBinding.ensureInitialized(); + _cameras = await availableCameras(); + } on CameraException catch (e) { + _logError(e.code, e.description); + } + runApp(const CameraApp());*/ /* + +} +*/ diff --git a/lib/camera/draggable_widget.dart b/lib/camera/draggable_widget.dart new file mode 100644 index 0000000..325996e --- /dev/null +++ b/lib/camera/draggable_widget.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +class DraggableWidget extends StatefulWidget { + const DraggableWidget({required super.key, required this.child, required this.onVerticalDrag}); + + final Widget child; + final void Function() onVerticalDrag; + + @override + State createState() => _DraggableWidgetState(); +} + +class _DraggableWidgetState extends State { + bool showDismiss = false; + Offset? startPosition; + @override + Widget build(BuildContext context) { + return GestureDetector( + child: showDismiss + ? Stack( + fit: StackFit.expand, + children: [widget.child, ColoredBox(color: Colors.white.withOpacity(0.8))], + ) + : widget.child, + onVerticalDragStart: (details) { + startPosition = details.globalPosition; + }, + onVerticalDragUpdate: (details) { + final dy = (startPosition! - details.globalPosition).dy; + const maxDy = 50; + if (dy > maxDy && !showDismiss) { + setState(() => showDismiss = true); + } else if (dy < maxDy && showDismiss) { + setState(() => showDismiss = false); + } + }, + onVerticalDragEnd: (details) { + if (showDismiss) { + widget.onVerticalDrag(); + } + }, + ); + } +} diff --git a/lib/common.dart b/lib/common.dart index cb56f1b..1e0f1c0 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -1,9 +1,11 @@ -// TODO: merge with same file in camera_app +import 'dart:io'; import 'package:json_annotation/json_annotation.dart'; part 'common.g.dart'; +final bookyDir = Directory('/storage/emulated/0/DCIM/booky/'); + enum ItemState { brandNew, veryGood, diff --git a/lib/main.dart b/lib/main.dart index b29c5a8..52c6d9a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,36 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/camera/camera.dart'; import 'enrichment/enrichment.dart'; void main() { - runApp(const EnrichmentApp()); + runApp(const BookyApp()); +} + +enum BookyAppActivity { + camera, + enrichment, +} + +class BookyApp extends StatefulWidget { + const BookyApp(); + + @override + State createState() => _BookyAppState(); +} + +class _BookyAppState extends State { + BookyAppActivity activity = Platform.isAndroid ? BookyAppActivity.camera : BookyAppActivity.enrichment; + + @override + Widget build(BuildContext context) { + switch (activity) { + case BookyAppActivity.camera: + return const CameraApp(); + case BookyAppActivity.enrichment: + return const EnrichmentApp(); + } + } } diff --git a/native/Cargo.toml b/native/Cargo.toml index c38db3c..bb2b159 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -14,7 +14,7 @@ flutter_rust_bridge = "1" base64 = "0.21.0" itertools = "0.10.5" regex = "1.7.1" -reqwest = { version = "0.11.14", features = ["blocking", "json", "multipart"] } +reqwest = { version = "0.11.14", default-features = false, features = ["blocking", "json", "rustls-tls", "multipart"] } scraper = "0.14.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.91" diff --git a/pubspec.yaml b/pubspec.yaml index d68f81f..dd9fdbc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,10 @@ dependencies: super_drag_and_drop: ^0.3.0+2 super_native_extensions: ^0.3.0+2 kt_dart: ^1.1.0 + camera: ^0.10.3+2 + gallery_saver: ^2.3.2 + permission_handler: ^10.2.0 + dev_dependencies: flutter_test: From 6751dcfa2dfad330e7593ee9047a62fd92ef1922 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 28 Apr 2023 00:09:51 +0200 Subject: [PATCH 072/150] Camera: initialize _camera list --- lib/camera/camera.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 9fd6f04..c5cccaa 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -59,7 +59,15 @@ class _CameraExampleHomeState extends State with WidgetsBindi _generateNewFolderPath(); WidgetsBinding.instance.addObserver(this); - _onNewCameraSelected(_cameras.first); + Future(() async { + try { + // WidgetsFlutterBinding.ensureInitialized(); + _cameras = await availableCameras(); + } on CameraException catch (e) { + _logError(e.code, e.description); + } + _onNewCameraSelected(_cameras.first); + }); } @override From 707373a46c6d759678948f84ae854a97c274123f Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 28 Apr 2023 23:45:03 +0200 Subject: [PATCH 073/150] Add storage permission to write metadata.json --- android/app/src/main/AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d555ba0..91be786 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ - + From f3429498901ded8165a9b77185cfc2f2824feea0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 28 Apr 2023 23:47:34 +0200 Subject: [PATCH 074/150] Better explorer, use Card --- lib/common.dart | 4 +- lib/enrichment/bundle_selection.dart | 58 ++++++++++++++++------------ lib/helpers.dart | 2 +- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/lib/common.dart b/lib/common.dart index 1e0f1c0..4ef9a58 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -4,7 +4,9 @@ import 'package:json_annotation/json_annotation.dart'; part 'common.g.dart'; -final bookyDir = Directory('/storage/emulated/0/DCIM/booky/'); +final bookyDir = Platform.isAndroid + ? Directory('/storage/emulated/0/DCIM/booky/') + : Directory('/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/booky/'); enum ItemState { brandNew, diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 87bc253..3b9559f 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:path/path.dart' as path; import '../bundle.dart'; +import '../common.dart' as common; import '../helpers.dart'; import 'enrichment.dart'; @@ -15,17 +16,16 @@ class BundleSelection extends StatelessWidget { @override Widget build(BuildContext context) { - final bundleDirs = - Directory('/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/booky/') - .listSync() - .whereType(); + final bundleDirs = common.bookyDir.listSync().whereType().sorted((d1, d2) => d1.path.compareTo(d2.path)); return Scaffold( appBar: AppBar(title: const Text('Bundle Section')), - body: Wrap( + body: GridView.extent( + maxCrossAxisExtent: 500, + childAspectRatio: 2, children: bundleDirs .map((d) => Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(2.0), child: GestureDetector( child: BundleWidget(d), onTap: () => onSubmit(ISBNDecodingStep(bundle: Bundle(d))), @@ -44,24 +44,34 @@ class BundleWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - children: [ - Container( - decoration: const BoxDecoration(color: Colors.blue), - child: Wrap( - children: directory - .listSync() - .whereType() - .where((f) => path.extension(f.path) == '.jpg') - .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())) - .map((f) => Padding( - padding: const EdgeInsets.all(8.0), - child: ImageWidget(f), - )) - .toList(), - )), - Text(path.basename(directory.path)) - ], + return Card( + // decoration: const BoxDecoration(color: Colors.blue), + child: Column( + children: [ + Text(path.basename(directory.path)), + Expanded( + child: Row( + children: [ + ...directory + .listSync() + .whereType() + .where((f) => path.extension(f.path) == '.jpg') + .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())) + .map((f) => Padding( + padding: const EdgeInsets.all(8.0), + child: ImageWidget(f), + )) + .toList(), + const Expanded(child: SizedBox.expand()), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () {}, + ), + ], + ), + ), + ], + ), ); } } diff --git a/lib/helpers.dart b/lib/helpers.dart index e8e20c1..ecd8114 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -12,7 +12,7 @@ class ImageWidget extends StatelessWidget { Widget build(BuildContext context) { return Image.file( image, - height: 200, + fit: BoxFit.fitHeight, isAntiAlias: true, filterQuality: FilterQuality.medium, ); From c2a009a3964e1b86a8200c462d5ea0e28ad2f490 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 28 Apr 2023 23:57:02 +0200 Subject: [PATCH 075/150] Better ISBNDecoding ui --- lib/enrichment/isbn_decoding.dart | 37 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index 6d52b04..1a8c4fc 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -44,23 +44,26 @@ class _ISBNDecodingWidgetState extends State { body: Column( children: [ Wrap( - children: [ - ...widget.step.bundle.images - .map((imgPath) => Column( - children: [ - ImageWidget(imgPath), - FutureBuilder( - future: isbns[imgPath.path]!, - builder: (context, snap) { - if (snap.hasData == false) { - return const CircularProgressIndicator(); - } - return Column(children: snap.data!.map((isbn) => Text(isbn)).toList()); - }) - ], - )) - .toList(), - ], + children: widget.step.bundle.images + .map((imgPath) => Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + SizedBox(height: 300, child: ImageWidget(imgPath)), + FutureBuilder( + future: isbns[imgPath.path]!, + builder: (context, snap) { + if (snap.hasData == false) { + return const CircularProgressIndicator(); + } + return Column(children: snap.data!.map((isbn) => Text(isbn)).toList()); + }) + ], + ), + ), + )) + .toList(), ), Padding( padding: const EdgeInsets.all(8.0), From 7fd9a64739843be69b91e1e17bde24200b53d140 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 29 Apr 2023 00:11:54 +0200 Subject: [PATCH 076/150] BundleSelection: delete button move to booky_deleted --- lib/enrichment/bundle_selection.dart | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 3b9559f..7b573ff 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -9,11 +9,16 @@ import '../common.dart' as common; import '../helpers.dart'; import 'enrichment.dart'; -class BundleSelection extends StatelessWidget { +class BundleSelection extends StatefulWidget { const BundleSelection({required this.onSubmit}); final void Function(ISBNDecodingStep newStep) onSubmit; + @override + State createState() => _BundleSelectionState(); +} + +class _BundleSelectionState extends State { @override Widget build(BuildContext context) { final bundleDirs = common.bookyDir.listSync().whereType().sorted((d1, d2) => d1.path.compareTo(d2.path)); @@ -27,8 +32,10 @@ class BundleSelection extends StatelessWidget { .map((d) => Padding( padding: const EdgeInsets.all(2.0), child: GestureDetector( - child: BundleWidget(d), - onTap: () => onSubmit(ISBNDecodingStep(bundle: Bundle(d))), + child: BundleWidget(d, onDelete: () { + setState(() {}); + }), + onTap: () => widget.onSubmit(ISBNDecodingStep(bundle: Bundle(d))), ), )) .toList(), @@ -38,9 +45,10 @@ class BundleSelection extends StatelessWidget { } class BundleWidget extends StatelessWidget { - const BundleWidget(this.directory); + const BundleWidget(this.directory, {required this.onDelete}); final Directory directory; + final void Function() onDelete; @override Widget build(BuildContext context) { @@ -65,7 +73,15 @@ class BundleWidget extends StatelessWidget { const Expanded(child: SizedBox.expand()), IconButton( icon: const Icon(Icons.delete), - onPressed: () {}, + onPressed: () { + final segments = path.split(directory.path); + segments[segments.length - 2] = 'booky_deleted'; + directory.renameSync(path.joinAll(segments)); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Deleted'), + )); + onDelete(); + }, ), ], ), From bb1fd7fa93ad225428e80d1d18134436cde2ec1f Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 29 Apr 2023 00:43:50 +0200 Subject: [PATCH 077/150] Bundle Selection: Better message if no device connected --- lib/enrichment/bundle_selection.dart | 34 +++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 7b573ff..86910a3 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -21,11 +21,15 @@ class BundleSelection extends StatefulWidget { class _BundleSelectionState extends State { @override Widget build(BuildContext context) { - final bundleDirs = common.bookyDir.listSync().whereType().sorted((d1, d2) => d1.path.compareTo(d2.path)); + return Scaffold(appBar: AppBar(title: const Text('Bundle Section')), body: _getBody()); + } + + Widget _getBody() { + try { + final bundleDirs = + common.bookyDir.listSync().whereType().sorted((d1, d2) => d1.path.compareTo(d2.path)); - return Scaffold( - appBar: AppBar(title: const Text('Bundle Section')), - body: GridView.extent( + return GridView.extent( maxCrossAxisExtent: 500, childAspectRatio: 2, children: bundleDirs @@ -39,8 +43,26 @@ class _BundleSelectionState extends State { ), )) .toList(), - ), - ); + ); + } on PathNotFoundException { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Device not connected', + style: TextStyle(fontSize: 30), + ), + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + setState(() {}); + }, + ), + ], + ), + ); + } } } From 7defe21120abdac6d5a85ac057d1984930ff8187 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 29 Apr 2023 01:04:52 +0200 Subject: [PATCH 078/150] ISBNDecding: Add AppBar and back button --- lib/enrichment/enrichment.dart | 3 ++- lib/enrichment/isbn_decoding.dart | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/enrichment/enrichment.dart b/lib/enrichment/enrichment.dart index 3e49741..a98f98b 100644 --- a/lib/enrichment/enrichment.dart +++ b/lib/enrichment/enrichment.dart @@ -70,7 +70,8 @@ class _EnrichmentAppState extends State { BundleSelection(onSubmit: (ISBNDecodingStep newStep) => setState(() => step = newStep)), ISBNDecodingStep() => ISBNDecodingWidget( step: step as ISBNDecodingStep, - onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep)), + onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep), + onBack: () => setState(() => step = BundleSelectionStep())), MetadataCollectingStep() => MetadataCollectingWidget( step: step as MetadataCollectingStep, onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index 1a8c4fc..8365e70 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -7,9 +7,10 @@ import '../helpers.dart'; import 'enrichment.dart'; class ISBNDecodingWidget extends StatefulWidget { - const ISBNDecodingWidget({required this.step, required this.onSubmit}); + const ISBNDecodingWidget({required this.step, required this.onSubmit, required this.onBack}); final ISBNDecodingStep step; final void Function(MetadataCollectingStep newStep) onSubmit; + final void Function() onBack; @override State createState() => _ISBNDecodingWidgetState(); @@ -41,6 +42,12 @@ class _ISBNDecodingWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: widget.onBack, + ), + title: const Text('ISBN decoding')), body: Column( children: [ Wrap( From a992c39b4db782a48def71b7443408683b378a2c Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 30 Apr 2023 18:15:00 +0200 Subject: [PATCH 079/150] Add README --- README.md | 136 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index fd95d2a..cc5dea4 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,97 @@ -# flutter_rust_bridge_template +# Booky -This repository serves as a template for Flutter projects calling into native Rust -libraries via `flutter_rust_bridge`. +Booky is an application to help publish second-hand book. +It enable taking multiple picture of the book(s). Add books state (brand new, worn out), add the weight (for shipping), then extract the ISBN from the barcode in the pictures to have additional metadata by scrapping some website. -## Getting Started +Metadata include: +- Title +- Author +- Blurb. A book blurb is a short promotional description, whereas a synopsis summarizes the twists, turns, and conclusion of the story. +- Keywords or genres -To begin, ensure that you have a working installation of the following items: -- [Flutter SDK](https://docs.flutter.dev/get-started/install) -- [Rust language](https://rustup.rs/) -- `flutter_rust_bridge_codegen` [cargo package](https://cjycode.com/flutter_rust_bridge/integrate/deps.html#build-time-dependencies) -- Appropriate [Rust targets](https://rust-lang.github.io/rustup/cross-compilation.html) for cross-compiling to your device -- For Android targets: - - Install [cargo-ndk](https://github.com/bbqsrc/cargo-ndk#installing) - - Install [Android NDK 22](https://github.com/android/ndk/wiki/Unsupported-Downloads#r22b), then put its path in one of the `gradle.properties`, e.g.: +## Enrichment +### Example using Babelio as source +#### Input +```rust +let isbn = 9782266071529; ``` -echo "ANDROID_NDK=.." >> ~/.gradle/gradle.properties -``` - -- For iOS targets: - - Install [cargo-xcode](https://gitlab.com/kornelski/cargo-xcode#installation) -- [Web dependencies](http://cjycode.com/flutter_rust_bridge/template/setup_web.html) for the Web - -Then go ahead and run `flutter run` (for web, run `dart run flutter_rust_bridge:serve` instead). When you're ready, refer to our documentation -[here](https://fzyzcjy.github.io/flutter_rust_bridge/index.html) to learn how to write and use binding code. - -Once you have edited `api.rs` to incorporate your own Rust code, the bridge files `bridge_definitions.dart` and `bridge_generated.dart` are generated using the following command (note: append ` --wasm` to add web support): -### Windows -``` -flutter_rust_bridge_codegen --rust-input native\src\api.rs --dart-output .\lib\bridge_generated.dart --dart-decl-output .\lib\bridge_definitions.dart +#### Output +```rust +BookMetaData { + title: "Le nom de la bête", + author: { + surname: "Daniel", + name: "Easterman", + }, + blurb: "Janvier 1999. Peu à peu, les pays arabes ont sombré dans l'intégrisme. Les attentats terroristes se multiplient en Europe attisant la haine et le racisme. Au Caire, un coup d'état fomenté par les fondamentalistes permet à leur chef Al-Kourtoubi de s'installer au pouvoir et d'instaurer la terreur. Le réseau des agents secrets britanniques en Égypte ayant été anéanti, Michael Hunt est obligé de reprendre du service pour enquêter sur place. Aidé par son frère Paul, prêtre catholique et agent du Vatican, il apprend que le Pape doit se rendre à Jérusalem pour participer à une conférence œcuménique. Au courant de ce projet, le chef des fondamentalistes a prévu d'enlever le saint père.Dans ce récit efficace et à l'action soutenue, le héros lutte presque seul contre des groupes fanatiques puissants et sans grand espoir de réussir. Comme dans tous ses autres livres, Daniel Easterman, spécialiste de l'islam, part du constat que le Mal est puissant et il dénonce l'intolérance et les nationalismes qui engendrent violence et chaos.--Claude Mesplède
\t\t", + key_words: [ + "roman", "fantastique", "policier historique", "romans policiers et polars", "thriller", "terreur", "action", "démocratie", "mystique", "islam", "intégrisme religieux", "catholicisme", "religion", "terrorisme", "extrémisme", "egypte", "médias", "thriller religieux", "littérature irlandaise", "irlande" + ], +} ``` -### Linux/MacOS/any other Unix -``` -flutter_rust_bridge_codegen --rust-input native/src/api.rs --dart-output ./lib/bridge_generated.dart --dart-decl-output ./lib/bridge_definitions.dart +### Sources + +| Source | Metadata (in addition to title and authors) | Notes | +|-------------------------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Babelio](https://www.babelio.com/) | blurb, keyword | No API available. No plan to build one.
Babelio seem to block the IP if it detect this bot is doing some scrapping | +| [Decitre](https://www.decitre.fr/) | blurb, keywords in commentaries | | +| [GoodReads](https://www.goodreads.com/) | blurb, genres in english | An API was available, but GoodRead does not create new developer key. [See this](https://help.goodreads.com/s/article/Does-Goodreads-support-the-use-of-APIs) | +| [Google Books](https://www.google.fr/books/) | blurb, genres | [A real API](https://developers.google.com/books/docs/overview) is available to look up a book by ISBN
Some book can't be search by ISBN, even though a search by title can find them, and they display the right ISBN | +| [ISBSearcher](https://www.isbnsearcher.com/) | blurb, main category in english | | +| [Label Emmaus](https://www.label-emmaus.co/) | blurb, genres | | +| [OpenLibrary](https://openlibrary.org/) | blurb are not translated | Its is based on physical books, it is not really a book database | +| [Chasse Aux Livre](https://www.chasse-aux-livres.fr/) | price only | it is not possible to parse with Selenium | +| [AbeBooks](https://www.abebooks.fr/) | Seems to have good french blurb | | + +#### GoogleBooks +GoogleBooks has some inconsistencies: +https://www.googleapis.com/books/v1/volumes?q=isbn:9782744170812 +says te publishedDate is 2004. +But https://www.googleapis.com/books/v1/volumes/DQUFSQAACAAJ +says the publishedDate is 2005. + +In the first response, we don't have a publisher, in the second we have. +In the first response, the title use a big C for "Cité", but in the second, it use a small 'c' + +## Contributing +### Build the barcode detector binary +Clone the 3 OpenCV repo: +- https://github.com/opencv/opencv.git (main repo) +- https://github.com/opencv/opencv_contrib.git (contain the barcode contrib module) +- https://github.com/opencv/opencv_extra.git (optionnal, contain the test data to test OpenCV) + +```shell +$ cd / +$ mkdir build +$ cd build/ +build/ $ cmake -DOPENCV_EXTRA_MODULES_PATH=/modules .. ``` -## Scaffolding in existing projects - -If you would like to generate boilerplate for using `flutter_rust_bridge` in your existing projects, -check out the [`flutter_rust_bridge` brick](https://brickhub.dev/bricks/flutter_rust_bridge/) -for more details. - -## Disclaimer - -This template is not affiliated with flutter_rust_bridge. Please file issues and PRs related to the template here, -not flutter_rust_bridge. - -## License - -Copyright 2022 Viet Dinh. - -This template is licensed under either of -- [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) ([LICENSE-APACHE](LICENSE-APACHE)) -- [MIT license](https://opensource.org/licenses/MIT) ([LICENSE-MIT](LICENSE-MIT)) - -at your option. +You can test the barcode module with: +```shell +build/ $ make opencv_test_barcode +build/ $ OPENCV_TEST_DATA_PATH=/testdata/ bin/opencv_test_barcode +``` -The [SPDX](https://spdx.dev/) license identifier for this project is `MIT OR Apache-2.0`. +### Install the rust/android toolchain +#### flutter_rust_bridge_template +Follow the instruction of flutter_rust_bridge_template. Here is an extract + +> To begin, ensure that you have a working installation of the following items: +> - [Flutter SDK](https://docs.flutter.dev/get-started/install) +> - [Rust language](https://rustup.rs/) +> - `flutter_rust_bridge_codegen` [cargo package](https://cjycode.com/flutter_rust_bridge/integrate/deps.html#build-time-dependencies) +> - Appropriate [Rust targets](https://rust-lang.github.io/rustup/cross-compilation.html) for cross-compiling to your device +> - For Android targets: +> - Install [cargo-ndk](https://github.com/bbqsrc/cargo-ndk#installing) +> - Install [Android NDK 22](https://github.com/android/ndk/wiki/Unsupported-Downloads#r22b), then put its path in one of the `gradle.properties`, e.g.: +> +> ``` +> echo "ANDROID_NDK=.." >> ~/.gradle/gradle.properties +> ``` + +#### super_native_extension +Follow this tutorial: https://pub.dev/packages/super_clipboard \ No newline at end of file From b1501df7ada7c825eab922d27e960a096624280f Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 30 Apr 2023 18:23:53 +0200 Subject: [PATCH 080/150] Add TODO --- TODO.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..fc356db --- /dev/null +++ b/TODO.md @@ -0,0 +1,7 @@ +# TODO + +* [ ] Grab the ISBN in real-time with ML Kit +* [ ] Search with Selenium in headless mode +* [ ] Price auto fill +* [ ] Compress the images to upload them quicker +* [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) From f30b8f30925ca3f30871c562ed8be4fa51d0bf82 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 1 May 2023 15:21:07 +0200 Subject: [PATCH 081/150] build.gradle: Remove uneeded android target --- android/app/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 9430ef7..48a4c02 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -87,12 +87,12 @@ dependencies { environment ANDROID_NDK_HOME: "$ANDROID_NDK" commandLine 'cargo', 'ndk', // the 2 ABIs below are used by real Android devices - '-t', 'armeabi-v7a', +// '-t', 'armeabi-v7a', '-t', 'arm64-v8a', // the below 2 ABIs are usually used for Android simulators, // add or remove these ABIs as needed. - '-t', 'x86', - '-t', 'x86_64', +// '-t', 'x86', +// '-t', 'x86_64', '-o', '../android/app/src/main/jniLibs', 'build' if (profileMode != null) { args profileMode From f4b65314c2b94642cfc72ed480bb5ebb2386034f Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 1 May 2023 17:18:09 +0200 Subject: [PATCH 082/150] BundleSelection: catch SystemFileException as well --- lib/enrichment/bundle_selection.dart | 39 +++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 86910a3..e689e76 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -44,24 +44,27 @@ class _BundleSelectionState extends State { )) .toList(), ); - } on PathNotFoundException { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'Device not connected', - style: TextStyle(fontSize: 30), - ), - IconButton( - icon: const Icon(Icons.refresh), - onPressed: () { - setState(() {}); - }, - ), - ], - ), - ); + } catch (e) { + if (e is PathNotFoundException || e is FileSystemException) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Device not connected', + style: TextStyle(fontSize: 30), + ), + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + setState(() {}); + }, + ), + ], + ), + ); + } + rethrow; } } } From ae718d0fccf175bcf0fcfecefc63d932a6b4ee67 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 1 May 2023 17:19:09 +0200 Subject: [PATCH 083/150] Booksprice: save file to cache --- native/src/booksprice/request.rs | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index ee1a12d..ab65fdf 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -14,17 +14,26 @@ pub async fn extract_price_from_isbn( let caps = DesiredCapabilities::chrome(); let driver = WebDriver::new("http://localhost:9515", caps).await?; - extract_price_from_url( - driver, - &format!( - "https://www.booksprice.com/comparePrice.do?l=y&searchType=compare&inputData={}", - isbn - ), - ) - .await + let url = format!( + "https://www.booksprice.com/comparePrice.do?l=y&searchType=compare&inputData={}", + isbn + ); + + let cache_file_path = format!("{}/booksprice/{}.html", crate::config::CACHE_PATH, isbn); + if std::path::Path::new(&cache_file_path).exists() { + println!("Using cache"); + extract_price_from_url(driver, &format!("file://{}", cache_file_path), None).await + } else { + println!("using online selenium"); + extract_price_from_url(driver, &url, Some(cache_file_path)).await + } } -async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, WebDriverError> { +async fn extract_price_from_url( + c: WebDriver, + url: &str, + cache_file_path: Option, +) -> Result, WebDriverError> { c.goto(&url).await?; let wait_res = c @@ -32,6 +41,12 @@ async fn extract_price_from_url(c: WebDriver, url: &str) -> Result, Web .wait(Duration::from_secs(10), Duration::from_secs(1)) .exists() .await; + let source_file = c.source().await.unwrap(); + + if let Some(cache_file_path) = cache_file_path { + let write_res = std::fs::write(&cache_file_path, &source_file); + write_res.expect(format!("Can't write to file {}", cache_file_path).as_str()); + } let entries = c .find_all(By::XPath("//*[@id='chart']/tbody/tr[position()>1]")) @@ -80,6 +95,7 @@ mod tests { let prices = extract_price_from_url( c, &selenium_common::url_from_path(port, "9782884747974.html"), + None, ) .await .unwrap(); @@ -92,4 +108,13 @@ mod tests { fn test_selenium() { local_tester!(parse_booksprices_from_9782884747974, "chrome"); } + + /* #[test] + + fn test_selenium_real_test() -> Result<(), WebDriverError> { + let prices = extract_price_from_isbn("9782884747974").unwrap(); + + assert_eq!(prices, vec![16.55, 21.85, 23.75, 27.17, 28.15, 43.20]); + Ok(()) + } */ } From 58f3ac47c973199f5e85e73dbd3048253f1fc1b8 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 1 May 2023 19:56:15 +0200 Subject: [PATCH 084/150] Use Bundle functions --- lib/camera/camera.dart | 14 ++++++------- lib/enrichment/ad_editing.dart | 4 +++- lib/enrichment/bundle_selection.dart | 31 ++++++++++++++-------------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index c5cccaa..63b7422 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -7,6 +7,7 @@ import 'package:gallery_saver/gallery_saver.dart'; import 'package:path/path.dart' as path; import 'package:permission_handler/permission_handler.dart'; +import '../bundle.dart'; import '../common.dart' as common; import 'draggable_widget.dart'; @@ -47,7 +48,7 @@ class _CameraExampleHomeState extends State with WidgetsBindi XFile? imageFile; late String bundleName; - Directory get getBundleDir => Directory(path.join(common.bookyDir.path, bundleName)); + Bundle get getBundle => Bundle(Directory(path.join(common.bookyDir.path, bundleName))); void _generateNewFolderPath() { bundleName = DateTime.now().toIso8601String().replaceAll(':', '_'); @@ -134,7 +135,7 @@ class _CameraExampleHomeState extends State with WidgetsBindi Padding( padding: const EdgeInsets.all(5.0), child: BottomWidget( - directory: getBundleDir, + bundle: getBundle, onSubmit: () { setState(() { _generateNewFolderPath(); @@ -331,8 +332,8 @@ class _CameraExampleHomeState extends State with WidgetsBindi } class BottomWidget extends StatefulWidget { - const BottomWidget({required this.directory, required this.onSubmit}); - final Directory directory; + const BottomWidget({required this.bundle, required this.onSubmit}); + final Bundle bundle; final void Function() onSubmit; @override @@ -343,11 +344,10 @@ class _BottomWidgetState extends State { @override Widget build(BuildContext context) { try { - final images = widget.directory.listSync().where((file) => path.extension(file.path) == '.jpg'); return Row( children: [ - _thumbnailWidget(images), - _addMetadataButton(context: context, directory: widget.directory, onSubmit: widget.onSubmit), + _thumbnailWidget(widget.bundle.images), + _addMetadataButton(context: context, directory: widget.bundle.directory, onSubmit: widget.onSubmit), ], ); } on PathNotFoundException { diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index ed3875d..21f4681 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -149,7 +149,9 @@ class _AdEditingWidgetState extends State { child: Column( children: [ Row( - children: ad.imgsPath.map((img) => ImageWidget(File(img))).toList(), + children: ad.imgsPath.map((img) => SizedBox( + height: 200, + child: ImageWidget(File(img)))).toList(), ), const Text('Drag and drop images') ], diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index e689e76..514633c 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -26,20 +26,23 @@ class _BundleSelectionState extends State { Widget _getBody() { try { - final bundleDirs = - common.bookyDir.listSync().whereType().sorted((d1, d2) => d1.path.compareTo(d2.path)); + final bundles = common.bookyDir + .listSync() + .whereType() + .sorted((d1, d2) => d1.path.compareTo(d2.path)) + .map((d) => Bundle(d)); return GridView.extent( maxCrossAxisExtent: 500, childAspectRatio: 2, - children: bundleDirs - .map((d) => Padding( + children: bundles + .map((bundle) => Padding( padding: const EdgeInsets.all(2.0), child: GestureDetector( - child: BundleWidget(d, onDelete: () { + child: BundleWidget(bundle, onDelete: () { setState(() {}); }), - onTap: () => widget.onSubmit(ISBNDecodingStep(bundle: Bundle(d))), + onTap: () => widget.onSubmit(ISBNDecodingStep(bundle: bundle)), ), )) .toList(), @@ -70,9 +73,9 @@ class _BundleSelectionState extends State { } class BundleWidget extends StatelessWidget { - const BundleWidget(this.directory, {required this.onDelete}); + const BundleWidget(this.bundle, {required this.onDelete}); - final Directory directory; + final Bundle bundle; final void Function() onDelete; @override @@ -81,15 +84,11 @@ class BundleWidget extends StatelessWidget { // decoration: const BoxDecoration(color: Colors.blue), child: Column( children: [ - Text(path.basename(directory.path)), + Text(path.basename(bundle.directory.path)), Expanded( child: Row( children: [ - ...directory - .listSync() - .whereType() - .where((f) => path.extension(f.path) == '.jpg') - .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())) + ...bundle.images .map((f) => Padding( padding: const EdgeInsets.all(8.0), child: ImageWidget(f), @@ -99,9 +98,9 @@ class BundleWidget extends StatelessWidget { IconButton( icon: const Icon(Icons.delete), onPressed: () { - final segments = path.split(directory.path); + final segments = path.split(bundle.directory.path); segments[segments.length - 2] = 'booky_deleted'; - directory.renameSync(path.joinAll(segments)); + bundle.directory.renameSync(path.joinAll(segments)); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Deleted'), )); From df7c7fd41972a65ae612e3f8e6552cacdf913185 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 1 May 2023 21:54:48 +0200 Subject: [PATCH 085/150] Compressed image when opening BundleSelection on Android, and use compressedImage for display --- lib/bundle.dart | 10 ++++ lib/enrichment/bundle_selection.dart | 84 ++++++++++++++++++++++------ pubspec.yaml | 1 + 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/lib/bundle.dart b/lib/bundle.dart index 02fff96..c5db3c7 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -18,6 +18,16 @@ class Bundle { .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())); } + Directory get compressedImagesDir => Directory(path.join(directory.path, 'compressed')); + + Iterable get compressedImages { + return compressedImagesDir + .listSync() + .whereType() + .where((file) => path.extension(file.path) == '.jpg') + .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())); + } + Metadata get metadata { final metadataFile = File(path.join(directory.path, 'metadata.json')); return Metadata.fromJson(jsonDecode(metadataFile.readAsStringSync()) as Map); diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 514633c..9a9e6ca 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:path/path.dart' as path; import '../bundle.dart'; @@ -32,21 +33,7 @@ class _BundleSelectionState extends State { .sorted((d1, d2) => d1.path.compareTo(d2.path)) .map((d) => Bundle(d)); - return GridView.extent( - maxCrossAxisExtent: 500, - childAspectRatio: 2, - children: bundles - .map((bundle) => Padding( - padding: const EdgeInsets.all(2.0), - child: GestureDetector( - child: BundleWidget(bundle, onDelete: () { - setState(() {}); - }), - onTap: () => widget.onSubmit(ISBNDecodingStep(bundle: bundle)), - ), - )) - .toList(), - ); + return BundleList(bundles, onSubmit: widget.onSubmit); } catch (e) { if (e is PathNotFoundException || e is FileSystemException) { return Center( @@ -72,6 +59,71 @@ class _BundleSelectionState extends State { } } +class BundleList extends StatefulWidget { + const BundleList(this.bundles, {required this.onSubmit}); + final Iterable bundles; + + final void Function(ISBNDecodingStep newStep) onSubmit; + + @override + State createState() => _BundleListState(); +} + +class _BundleListState extends State { + @override + void initState() { + super.initState(); + for (final bundle in widget.bundles) { + for (final image in bundle.images) { + final segments = path.split(image.path); + segments.insert(segments.length - 1, 'compressed'); + print('new path ${path.joinAll(segments)}'); + final targetPath = path.joinAll(segments); + if (!File(targetPath).existsSync()) { + _testCompressAndGetFile(image, targetPath); + } + } + } + } + + Future _testCompressAndGetFile(File file, String targetPath) async { + await Directory(path.dirname(targetPath)).create(); + var result = await FlutterImageCompress.compressAndGetFile( + file.absolute.path, + targetPath, + minHeight: 800, + minWidth: 800, + quality: 70, + ); + + print(file.lengthSync()); + print(result?.lengthSync()); + + return result; + } + + @override + Widget build(BuildContext context) { + return GridView.extent( + maxCrossAxisExtent: 500, + childAspectRatio: 2, + children: widget.bundles + .map((bundle) => Padding( + padding: const EdgeInsets.all(2.0), + child: GestureDetector( + child: BundleWidget(bundle, onDelete: () { + setState(() {}); + }), + onTap: () { + widget.onSubmit(ISBNDecodingStep(bundle: bundle)); + }, + ), + )) + .toList(), + ); + } +} + class BundleWidget extends StatelessWidget { const BundleWidget(this.bundle, {required this.onDelete}); @@ -88,7 +140,7 @@ class BundleWidget extends StatelessWidget { Expanded( child: Row( children: [ - ...bundle.images + ...bundle.compressedImages .map((f) => Padding( padding: const EdgeInsets.all(8.0), child: ImageWidget(f), diff --git a/pubspec.yaml b/pubspec.yaml index dd9fdbc..cb7ac29 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: camera: ^0.10.3+2 gallery_saver: ^2.3.2 permission_handler: ^10.2.0 + flutter_image_compress: ^1.1.3 dev_dependencies: From b26c75792c9a588d4cf24463517bfaf8617afb84 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 1 May 2023 23:21:18 +0200 Subject: [PATCH 086/150] Stop using GallerySaver and use XFile.saveTo to specify the name of the file s that file and their compressed version are in the right order --- lib/bundle.dart | 8 ++------ lib/camera/camera.dart | 19 +++++++------------ lib/enrichment/ad_editing.dart | 7 +++---- pubspec.yaml | 1 - 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/lib/bundle.dart b/lib/bundle.dart index c5db3c7..564e901 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -11,11 +11,7 @@ class Bundle { final Directory directory; Iterable get images { - return directory - .listSync() - .whereType() - .where((file) => path.extension(file.path) == '.jpg') - .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())); + return directory.listSync().whereType().where((file) => path.extension(file.path) == '.jpg'); } Directory get compressedImagesDir => Directory(path.join(directory.path, 'compressed')); @@ -25,7 +21,7 @@ class Bundle { .listSync() .whereType() .where((file) => path.extension(file.path) == '.jpg') - .sorted((f1, f2) => f1.lastModifiedSync().compareTo(f2.lastModifiedSync())); + .sorted((f1, f2) => path.basename(f1.path).compareTo(path.basename(f2.path))); } Metadata get metadata { diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 63b7422..90df578 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; -import 'package:gallery_saver/gallery_saver.dart'; import 'package:path/path.dart' as path; import 'package:permission_handler/permission_handler.dart'; @@ -45,10 +44,10 @@ void _logError(String code, String? message) { class _CameraExampleHomeState extends State with WidgetsBindingObserver, TickerProviderStateMixin { CameraController? controller; - XFile? imageFile; late String bundleName; - Bundle get getBundle => Bundle(Directory(path.join(common.bookyDir.path, bundleName))); + Directory get getBundleDir => Directory(path.join(common.bookyDir.path, bundleName)); + Bundle get getBundle => Bundle(getBundleDir); void _generateNewFolderPath() { bundleName = DateTime.now().toIso8601String().replaceAll(':', '_'); @@ -272,20 +271,16 @@ class _CameraExampleHomeState extends State with WidgetsBindi takePicture().then((XFile? file) async { if (mounted) { if (file != null) { - GallerySaver.saveImage(file.path, albumName: 'booky/$bundleName', toDcim: true).then((bool? success) { - if (success != true) { - showInSnackBar('Error when saving image'); - } else { - setState(() { - imageFile = file; - }); - } - }); + await getBundleDir.create(); + file.saveTo(_getFirstUnusedName(getBundleDir)); } } }); } + String _getFirstUnusedName(Directory dir) => + List.generate(20, (index) => path.join(dir.path, '$index.jpg')).firstWhere((path) => !File(path).existsSync()); + Future onCaptureOrientationLockButtonPressed() async { try { if (controller != null) { diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 21f4681..b00f9c7 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -60,7 +60,7 @@ class _AdEditingWidgetState extends State { title: title, description: description, priceCent: totalPrice, - imgsPath: widget.step.bundle.images.map((e) => e.path).toList()); + imgsPath: widget.step.bundle.compressedImages.map((e) => e.path).toList()); } String _getDescription(Iterable> metadataFromIsbn) { @@ -149,9 +149,8 @@ class _AdEditingWidgetState extends State { child: Column( children: [ Row( - children: ad.imgsPath.map((img) => SizedBox( - height: 200, - child: ImageWidget(File(img)))).toList(), + children: + ad.imgsPath.map((img) => SizedBox(height: 200, child: ImageWidget(File(img)))).toList(), ), const Text('Drag and drop images') ], diff --git a/pubspec.yaml b/pubspec.yaml index cb7ac29..39734b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,7 +29,6 @@ dependencies: super_native_extensions: ^0.3.0+2 kt_dart: ^1.1.0 camera: ^0.10.3+2 - gallery_saver: ^2.3.2 permission_handler: ^10.2.0 flutter_image_compress: ^1.1.3 From 45be6df18e955801965c216b1e8e7b3d62f832f2 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 2 May 2023 21:55:19 +0200 Subject: [PATCH 087/150] TODO: Update --- TODO.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index fc356db..09d89a2 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,13 @@ # TODO -* [ ] Grab the ISBN in real-time with ML Kit -* [ ] Search with Selenium in headless mode +* [ ] Navigate between enrichment and camera +* [ ] MetadataCollecting: Create multiple textFieldController when multiple ISBN +* [ ] Add back the keywords * [ ] Price auto fill -* [ ] Compress the images to upload them quicker * [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) +* [ ] Grab the ISBN in real-time with ML Kit (add a minimum repetition of about 20 to avoid false ISBN detection) +* [ ] Search with Selenium in headless mode + +# DONE + +* [x] Compress the images to upload them quicker From 6d9b16ef250b902ad7b4da222c543779e544d8d0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 2 May 2023 22:07:50 +0200 Subject: [PATCH 088/150] Ignore compressed/ dir in .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c9e953d..13840ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +compressed/ + # Miscellaneous *.class *.log From fb6386330224325e4bb46be8c729b28477539c61 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 2 May 2023 22:49:47 +0200 Subject: [PATCH 089/150] Use Navigator instead of callback. Can go on every screen on linux and android --- TODO.md | 2 +- lib/camera/camera.dart | 39 ++++-------------------- lib/enrichment/ad_editing.dart | 6 ++-- lib/enrichment/bundle_selection.dart | 27 +++++++++++------ lib/enrichment/enrichment.dart | 40 ++----------------------- lib/enrichment/isbn_decoding.dart | 18 +++++------ lib/enrichment/metadata_collecting.dart | 14 +++++---- lib/main.dart | 31 ++++--------------- 8 files changed, 52 insertions(+), 125 deletions(-) diff --git a/TODO.md b/TODO.md index 09d89a2..39d1426 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ # TODO -* [ ] Navigate between enrichment and camera * [ ] MetadataCollecting: Create multiple textFieldController when multiple ISBN * [ ] Add back the keywords * [ ] Price auto fill @@ -11,3 +10,4 @@ # DONE * [x] Compress the images to upload them quicker +* [x] Navigate between enrichment and camera \ No newline at end of file diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 90df578..93d749e 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -11,38 +11,22 @@ import '../common.dart' as common; import 'draggable_widget.dart'; /// Camera example home widget. -class CameraExampleHome extends StatefulWidget { +class CameraWidget extends StatefulWidget { /// Default Constructor - const CameraExampleHome({Key? key}) : super(key: key); + const CameraWidget({Key? key}) : super(key: key); @override - State createState() { - return _CameraExampleHomeState(); + State createState() { + return _CameraWidgetState(); } } -/// Returns a suitable camera icon for [direction]. -IconData getCameraLensIcon(CameraLensDirection direction) { - switch (direction) { - case CameraLensDirection.back: - return Icons.camera_rear; - case CameraLensDirection.front: - return Icons.camera_front; - case CameraLensDirection.external: - return Icons.camera; - } - // This enum is from a different package, so a new value could be added at - // any time. The example should keep working if that happens. - // ignore: dead_code - return Icons.camera; -} - void _logError(String code, String? message) { // ignore: avoid_print print('Error: $code${message == null ? '' : '\nError Message: $message'}'); } -class _CameraExampleHomeState extends State with WidgetsBindingObserver, TickerProviderStateMixin { +class _CameraWidgetState extends State with WidgetsBindingObserver, TickerProviderStateMixin { CameraController? controller; late String bundleName; @@ -435,19 +419,6 @@ class _MetadataWidgetState extends State { } } -/// CameraApp is the Main Application. -class CameraApp extends StatelessWidget { - /// Default Constructor - const CameraApp({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - home: CameraExampleHome(), - ); - } -} - List _cameras = []; /* diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index b00f9c7..391059c 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -9,12 +9,12 @@ import '../copiable_text_field.dart'; import '../draggable_files_widget.dart'; import '../ffi.dart' if (dart.library.html) 'ffi_web.dart'; import '../helpers.dart'; +import 'bundle_selection.dart'; import 'enrichment.dart'; class AdEditingWidget extends StatefulWidget { - const AdEditingWidget({required this.step, required this.onSubmit}); + const AdEditingWidget({required this.step}); final AdEditingStep step; - final void Function() onSubmit; @override State createState() => _AdEditingWidgetState(); @@ -167,7 +167,7 @@ class _AdEditingWidgetState extends State { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Moved'), )); - widget.onSubmit(); + Navigator.push(context, MaterialPageRoute(builder: (context) => const BundleSelection())); }, child: const Text('Mark as published')) ], diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 9a9e6ca..a1ac6e8 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -3,17 +3,17 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; +import 'package:flutter_rust_bridge_template/camera/camera.dart'; import 'package:path/path.dart' as path; import '../bundle.dart'; import '../common.dart' as common; import '../helpers.dart'; import 'enrichment.dart'; +import 'isbn_decoding.dart'; class BundleSelection extends StatefulWidget { - const BundleSelection({required this.onSubmit}); - - final void Function(ISBNDecodingStep newStep) onSubmit; + const BundleSelection(); @override State createState() => _BundleSelectionState(); @@ -22,7 +22,13 @@ class BundleSelection extends StatefulWidget { class _BundleSelectionState extends State { @override Widget build(BuildContext context) { - return Scaffold(appBar: AppBar(title: const Text('Bundle Section')), body: _getBody()); + return Scaffold( + appBar: AppBar(title: const Text('Bundle Section')), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.camera), + onPressed: () => + Navigator.push(context, MaterialPageRoute(builder: (context) => const CameraWidget()))), + body: _getBody()); } Widget _getBody() { @@ -33,7 +39,7 @@ class _BundleSelectionState extends State { .sorted((d1, d2) => d1.path.compareTo(d2.path)) .map((d) => Bundle(d)); - return BundleList(bundles, onSubmit: widget.onSubmit); + return BundleList(bundles); } catch (e) { if (e is PathNotFoundException || e is FileSystemException) { return Center( @@ -60,11 +66,9 @@ class _BundleSelectionState extends State { } class BundleList extends StatefulWidget { - const BundleList(this.bundles, {required this.onSubmit}); + const BundleList(this.bundles); final Iterable bundles; - final void Function(ISBNDecodingStep newStep) onSubmit; - @override State createState() => _BundleListState(); } @@ -115,7 +119,12 @@ class _BundleListState extends State { setState(() {}); }), onTap: () { - widget.onSubmit(ISBNDecodingStep(bundle: bundle)); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ISBNDecodingWidget( + step: ISBNDecodingStep(bundle: bundle), + ))); }, ), )) diff --git a/lib/enrichment/enrichment.dart b/lib/enrichment/enrichment.dart index a98f98b..c5fbc3f 100644 --- a/lib/enrichment/enrichment.dart +++ b/lib/enrichment/enrichment.dart @@ -1,11 +1,6 @@ -import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/helpers.dart'; import '../bundle.dart'; -import 'ad_editing.dart'; -import 'bundle_selection.dart'; -import 'isbn_decoding.dart'; -import 'metadata_collecting.dart'; sealed class BookyStep {} @@ -30,16 +25,8 @@ class AdEditingStep implements BookyStep { AdEditingStep({required this.bundle, required this.metadata}); } -class EnrichmentApp extends StatefulWidget { - const EnrichmentApp({Key? key}) : super(key: key); - - @override - State createState() => _EnrichmentAppState(); -} - -class _EnrichmentAppState extends State { - BookyStep step = BundleSelectionStep(); - /*AdEditingStep( +// Example State +/*AdEditingStep( bundle: Bundle( Directory('/home/julien/Perso/LeBonCoin/chain_automatisation/open_cv_test/test_images/booky_example/normal')), metadata: { @@ -51,7 +38,7 @@ class _EnrichmentAppState extends State { priceCent: 1234) }, );*/ - /* MetadataCollectingStep(imgsPaths: [ +/* MetadataCollectingStep(imgsPaths: [ '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194742.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194746.jpg', '/home/julien/Perso/LeBonCoin/chain_automatisation/test_images/20230204_194753.jpg', @@ -60,24 +47,3 @@ class _EnrichmentAppState extends State { '9782253029854', // '9782277223634', });*/ - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'BookAdPublisher', - theme: ThemeData(primarySwatch: Colors.blue), - home: switch (step) { - BundleSelectionStep() => - BundleSelection(onSubmit: (ISBNDecodingStep newStep) => setState(() => step = newStep)), - ISBNDecodingStep() => ISBNDecodingWidget( - step: step as ISBNDecodingStep, - onSubmit: (MetadataCollectingStep newStep) => setState(() => step = newStep), - onBack: () => setState(() => step = BundleSelectionStep())), - MetadataCollectingStep() => MetadataCollectingWidget( - step: step as MetadataCollectingStep, - onSubmit: (AdEditingStep newStep) => setState(() => step = newStep)), - AdEditingStep() => - AdEditingWidget(step: step as AdEditingStep, onSubmit: () => setState(() => step = BundleSelectionStep())), - BookyStep() => throw UnimplementedError('Not possible') - }); - } -} diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index 8365e70..fa0d3cf 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -1,16 +1,15 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/enrichment/metadata_collecting.dart'; import 'package:kt_dart/collection.dart'; import '../helpers.dart'; import 'enrichment.dart'; class ISBNDecodingWidget extends StatefulWidget { - const ISBNDecodingWidget({required this.step, required this.onSubmit, required this.onBack}); + const ISBNDecodingWidget({required this.step}); final ISBNDecodingStep step; - final void Function(MetadataCollectingStep newStep) onSubmit; - final void Function() onBack; @override State createState() => _ISBNDecodingWidgetState(); @@ -42,12 +41,7 @@ class _ISBNDecodingWidgetState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: widget.onBack, - ), - title: const Text('ISBN decoding')), + appBar: AppBar(title: const Text('ISBN decoding')), body: Column( children: [ Wrap( @@ -81,7 +75,11 @@ class _ISBNDecodingWidgetState extends State { onPressed: () { final isbnSet = snap.data!.expand((e) => e).toSet(); print('isbnSet = $isbnSet'); - widget.onSubmit(MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MetadataCollectingWidget( + step: MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)))); }, child: const Text('Validate ISBNs')); }), diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index f1182b7..9b5f6b2 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_rust_bridge_template/enrichment/ad_editing.dart'; import 'package:flutter_rust_bridge_template/helpers.dart'; import '../ffi.dart' if (dart.library.html) 'ffi_web.dart'; @@ -24,9 +25,8 @@ class SelectableTextAndUse extends StatelessWidget { } class MetadataCollectingWidget extends StatefulWidget { - MetadataCollectingWidget({required this.step, required this.onSubmit}); + MetadataCollectingWidget({required this.step}); final MetadataCollectingStep step; - final void Function(AdEditingStep newStep) onSubmit; final titleTextFieldController = TextEditingController(); final authorsTextFieldController = TextEditingController(); @@ -232,9 +232,13 @@ class _MetadataCollectingWidgetState extends State { padding: const EdgeInsets.all(8.0), child: ElevatedButton( onPressed: () { - widget.onSubmit(AdEditingStep( - bundle: widget.step.bundle, - metadata: metadata.map((key, value) => MapEntry(key, value.manual)))); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AdEditingWidget( + step: AdEditingStep( + bundle: widget.step.bundle, + metadata: metadata.map((key, value) => MapEntry(key, value.manual)))))); }, child: const Text('Validate Metadatas')), ) diff --git a/lib/main.dart b/lib/main.dart index 52c6d9a..63dd156 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,36 +1,15 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/camera/camera.dart'; - -import 'enrichment/enrichment.dart'; +import 'package:flutter_rust_bridge_template/enrichment/bundle_selection.dart'; void main() { runApp(const BookyApp()); } -enum BookyAppActivity { - camera, - enrichment, -} - -class BookyApp extends StatefulWidget { +class BookyApp extends StatelessWidget { const BookyApp(); @override - State createState() => _BookyAppState(); -} - -class _BookyAppState extends State { - BookyAppActivity activity = Platform.isAndroid ? BookyAppActivity.camera : BookyAppActivity.enrichment; - - @override - Widget build(BuildContext context) { - switch (activity) { - case BookyAppActivity.camera: - return const CameraApp(); - case BookyAppActivity.enrichment: - return const EnrichmentApp(); - } - } + Widget build(BuildContext context) => const MaterialApp( + home: BundleSelection(), + ); } From 65fd6a237e6d4d6536af586bcc6d69a1059f0609 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 21:30:55 +0200 Subject: [PATCH 090/150] MetadataCollecting: Create multiple textFieldController when multiple ISBN --- TODO.md | 6 +- lib/enrichment/isbn_decoding.dart | 4 +- lib/enrichment/metadata_collecting.dart | 420 ++++++++++++------------ 3 files changed, 222 insertions(+), 208 deletions(-) diff --git a/TODO.md b/TODO.md index 39d1426..da1b946 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ # TODO -* [ ] MetadataCollecting: Create multiple textFieldController when multiple ISBN * [ ] Add back the keywords * [ ] Price auto fill * [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) @@ -9,5 +8,6 @@ # DONE -* [x] Compress the images to upload them quicker -* [x] Navigate between enrichment and camera \ No newline at end of file +* [x] MetadataCollecting: Create multiple textFieldController when multiple ISBN +* [x] Navigate between enrichment and camera +* [x] Compress the images to upload them quicker \ No newline at end of file diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index fa0d3cf..399b8d8 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -1,11 +1,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_rust_bridge_template/enrichment/metadata_collecting.dart'; import 'package:kt_dart/collection.dart'; import '../helpers.dart'; import 'enrichment.dart'; +import 'metadata_collecting.dart'; class ISBNDecodingWidget extends StatefulWidget { const ISBNDecodingWidget({required this.step}); @@ -78,7 +78,7 @@ class _ISBNDecodingWidgetState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => MetadataCollectingWidget( + builder: (context) => BooksMetadataCollectingWidget( step: MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)))); }, child: const Text('Validate ISBNs')); diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 9b5f6b2..76fdd45 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -6,101 +6,21 @@ import 'package:flutter_rust_bridge_template/helpers.dart'; import '../ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'enrichment.dart'; -const noneText = Text('None', style: TextStyle(fontStyle: FontStyle.italic)); +class BooksMetadataCollectingWidget extends StatefulWidget { + const BooksMetadataCollectingWidget({required this.step}); -class SelectableTextAndUse extends StatelessWidget { - const SelectableTextAndUse(this.s, {required this.onUse}); - final String s; - final void Function(String) onUse; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - TextButton(onPressed: () => onUse(s), child: const Text('Use')), - SelectableText(s), - ], - ); - } -} - -class MetadataCollectingWidget extends StatefulWidget { - MetadataCollectingWidget({required this.step}); final MetadataCollectingStep step; - - final titleTextFieldController = TextEditingController(); - final authorsTextFieldController = TextEditingController(); - final blurbTextFieldController = TextEditingController(); - final priceTextFieldController = TextEditingController(); - @override - State createState() => _MetadataCollectingWidgetState(); + State createState() => _BooksMetadataCollectingWidgetState(); } -class Metadatas { - final Map> mdFromProviders; - BookMetaDataManual manual; - Metadatas({required this.mdFromProviders, required this.manual}); -} - -class _MetadataCollectingWidgetState extends State { - Map metadata = {}; - - void replaceIfBetterString(String? providerStr, String manualStr, void Function() onReplace) { - if (providerStr == null || manualStr.length > providerStr.length) return; - onReplace(); - } - - void _updateManualTitle(String isbn, String newTitle) { - setState(() { - metadata[isbn]!.manual.title = newTitle; - widget.titleTextFieldController.text = newTitle; - }); - } - - void _updateManualAuthors(String isbn, String newAuthor) { - setState(() { - metadata[isbn]!.manual.authors = [Author(firstName: '', lastName: newAuthor)]; - widget.authorsTextFieldController.text = newAuthor; - }); - } - - void _updateManualBlurb(String isbn, String newBlurb) { - setState(() { - metadata[isbn]!.manual.blurb = newBlurb; - widget.blurbTextFieldController.text = newBlurb; - }); - } +class _BooksMetadataCollectingWidgetState extends State { + late Map controllers; @override void initState() { super.initState(); - widget.step.isbns.forEach((isbn) { - metadata.putIfAbsent( - isbn, - () => Metadatas( - manual: BookMetaDataManual(title: '', authors: [], blurb: '', keywords: [], priceCent: null), - mdFromProviders: Map.fromEntries(ProviderEnum.values.map((provider) { - final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); - md.then((value) { - if (value != null) { - replaceIfBetterString(value.title, metadata[isbn]!.manual.title!, () { - _updateManualTitle(isbn, value.title!); - }); - - // TODO: handle list of authors - final joinedAuthors = value.authors.toText(); - replaceIfBetterString(joinedAuthors, metadata[isbn]!.manual.authors.toText(), () { - _updateManualAuthors(isbn, joinedAuthors); - }); - replaceIfBetterString(value.blurb, metadata[isbn]!.manual.blurb!, () { - _updateManualBlurb(isbn, value.blurb!); - }); - } - }); - return MapEntry(provider, md); - })))); - }); + controllers = Map.fromEntries(widget.step.isbns.map((isbn) => MapEntry(isbn, _BookControllerSet()))); } @override @@ -112,122 +32,12 @@ class _MetadataCollectingWidgetState extends State { body: SingleChildScrollView( child: Column( children: [ - ...widget.step.isbns.map((isbn) { - final manual = metadata[isbn]!.manual; - const columnHeaderStyle = TextStyle(fontSize: 20, fontWeight: FontWeight.bold); - return Card( - margin: const EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - SelectableText('ISBN: $isbn', style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), - Table( - children: [ - TableRow( - children: [ - const Text('Manual', style: columnHeaderStyle), - const Text('Babelio', style: columnHeaderStyle), - const Text('GoogleBooks', style: columnHeaderStyle), - const Text('BooksPrice', style: columnHeaderStyle), - ].map((e) => Center(child: e)).toList()), - TableRow(children: [ - FutureWidget( - future: metadata[isbn]!.mdFromProviders.entries.first.value, - builder: (data) => TextFormField( - controller: widget.titleTextFieldController, - onChanged: (newText) => setState(() => manual.title = newText), - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', - ), - )), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) => data == null ? noneText : SelectableText(data.title ?? ''))), - ]), - TableRow(children: [ - FutureWidget( - future: metadata[isbn]!.mdFromProviders.entries.first.value, - builder: (data) => TextFormField( - controller: widget.authorsTextFieldController, - onChanged: (newText) => setState(() => manual.authors = - newText.split('\n').map((line) => Author(firstName: '', lastName: line)).toList()), - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Authors', - ), - ), - ), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final authors = data?.authors; - if (authors == null || authors.isEmpty) { - return noneText; - } - return SelectableText(authors.toText()); - })), - ]), - TableRow(children: [ - FutureWidget( - future: metadata[isbn]!.mdFromProviders.entries.first.value, - builder: (data) => TextFormField( - controller: widget.blurbTextFieldController, - onChanged: (newText) => setState(() => metadata[isbn]!.manual.blurb = newText), - maxLines: null, - decoration: const InputDecoration( - icon: Icon(Icons.description), - labelText: 'Book blurb', - ), - )), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final blurb = data?.blurb; - if (blurb == null) { - return noneText; - } - return SelectableTextAndUse( - blurb, - onUse: (b) => _updateManualBlurb(isbn, b), - ); - })), - ]), - TableRow(children: [ - FutureWidget( - future: metadata[isbn]!.mdFromProviders.entries.first.value, - builder: (data) => TextFormField( - controller: widget.priceTextFieldController, - onChanged: (newText) => setState(() => metadata[isbn]!.manual.priceCent = - double.parse(newText).multiply(100).round()), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[0-9]+[,.]{0,1}[0-9]*')), - ], - decoration: const InputDecoration( - icon: Icon(Icons.euro), - labelText: 'Price', - ), - )), - ...metadata[isbn]!.mdFromProviders.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final marketPrices = data?.marketPrice.toList()?..sort(); - if (marketPrices == null || marketPrices.isEmpty) { - return noneText; - } - return SelectableText( - '${marketPrices.first.toStringAsFixed(2)} - ${marketPrices.last.toStringAsFixed(2)}', - ); - })), - ]), - ], - ), - ], - ), - ), - ); - }).toList(), + ...controllers.entries + .map((entry) => _BookMetadataCollectingWidget( + isbn: entry.key, + controllers: entry.value, + )) + .toList(), Padding( padding: const EdgeInsets.all(8.0), child: ElevatedButton( @@ -238,7 +48,19 @@ class _MetadataCollectingWidgetState extends State { builder: (context) => AdEditingWidget( step: AdEditingStep( bundle: widget.step.bundle, - metadata: metadata.map((key, value) => MapEntry(key, value.manual)))))); + metadata: controllers.map((key, value) => MapEntry( + key, + BookMetaDataManual( + title: value.titleTextFieldController.text, + authors: value.authorsTextFieldController.text + .split('\n') + .map((line) => Author(firstName: '', lastName: line)) + .toList(), + blurb: value.blurbTextFieldController.text, + keywords: [], + priceCent: + double.parse(value.priceTextFieldController.text).multiply(100).round(), + ))))))); }, child: const Text('Validate Metadatas')), ) @@ -248,3 +70,195 @@ class _MetadataCollectingWidgetState extends State { ); } } + +class _BookControllerSet { + final TextEditingController titleTextFieldController = TextEditingController(); + final TextEditingController authorsTextFieldController = TextEditingController(); + final TextEditingController blurbTextFieldController = TextEditingController(); + final TextEditingController priceTextFieldController = TextEditingController(); +} + +class _BookMetadataCollectingWidget extends StatefulWidget { + const _BookMetadataCollectingWidget({required this.isbn, required this.controllers}); + + final String isbn; + final _BookControllerSet controllers; + + @override + State<_BookMetadataCollectingWidget> createState() => _BookMetadataCollectingWidgetState(); +} + +class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWidget> { + late Map> metadata; + + @override + void initState() { + super.initState(); + final isbn = widget.isbn; + metadata = Map.fromEntries(ProviderEnum.values.map((provider) { + final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); + md.then((value) { + if (value != null) { + _replaceIfBetterString(value.title, widget.controllers.titleTextFieldController.text, () { + _updateManualTitle(value.title!); + }); + + final joinedAuthors = value.authors.toText(); + _replaceIfBetterString(joinedAuthors, widget.controllers.authorsTextFieldController.text, () { + _updateManualAuthors(joinedAuthors); + }); + _replaceIfBetterString(value.blurb, widget.controllers.blurbTextFieldController.text, () { + _updateManualBlurb(value.blurb!); + }); + // TODO: handle keywords + } + }); + return MapEntry(provider, md); + })); + } + + void _replaceIfBetterString(String? providerStr, String manualStr, void Function() onReplace) { + if (providerStr == null || manualStr.length > providerStr.length) return; + onReplace(); + } + + void _updateManualTitle(String newTitle) { + setState(() => widget.controllers.titleTextFieldController.text = newTitle); + } + + void _updateManualAuthors(String newAuthor) { + setState(() => widget.controllers.authorsTextFieldController.text = newAuthor); + } + + void _updateManualBlurb(String newBlurb) { + setState(() => widget.controllers.blurbTextFieldController.text = newBlurb); + } + + @override + Widget build(BuildContext context) { + const columnHeaderStyle = TextStyle(fontSize: 20, fontWeight: FontWeight.bold); + return Card( + margin: const EdgeInsets.all(10), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + SelectableText('ISBN: ${widget.isbn}', style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), + Table( + children: [ + TableRow( + children: [ + const Text('Manual', style: columnHeaderStyle), + const Text('Babelio', style: columnHeaderStyle), + const Text('GoogleBooks', style: columnHeaderStyle), + const Text('BooksPrice', style: columnHeaderStyle), + ].map((e) => Center(child: e)).toList()), + TableRow(children: [ + FutureWidget( + future: metadata.entries.first.value, + builder: (data) => TextFormField( + controller: widget.controllers.titleTextFieldController, + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + )), + ...metadata.entries.map((e) => FutureWidget( + future: e.value, builder: (data) => data == null ? _noneText : SelectableText(data.title ?? ''))), + ]), + TableRow(children: [ + FutureWidget( + future: metadata.entries.first.value, + builder: (data) => TextFormField( + controller: widget.controllers.authorsTextFieldController, + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Authors', + ), + ), + ), + ...metadata.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final authors = data?.authors; + if (authors == null || authors.isEmpty) { + return _noneText; + } + return SelectableText(authors.toText()); + })), + ]), + TableRow(children: [ + FutureWidget( + future: metadata.entries.first.value, + builder: (data) => TextFormField( + controller: widget.controllers.blurbTextFieldController, + maxLines: null, + decoration: const InputDecoration( + icon: Icon(Icons.description), + labelText: 'Book blurb', + ), + )), + ...metadata.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final blurb = data?.blurb; + if (blurb == null) { + return _noneText; + } + return _SelectableTextAndUse( + blurb, + onUse: (b) => _updateManualBlurb(b), + ); + })), + ]), + TableRow(children: [ + FutureWidget( + future: metadata.entries.first.value, + builder: (data) => TextFormField( + controller: widget.controllers.priceTextFieldController, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9]+[,.]{0,1}[0-9]*')), + ], + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', + ), + )), + ...metadata.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final marketPrices = data?.marketPrice.toList()?..sort(); + if (marketPrices == null || marketPrices.isEmpty) { + return _noneText; + } + return SelectableText( + '${marketPrices.first.toStringAsFixed(2)} - ${marketPrices.last.toStringAsFixed(2)}', + ); + })), + ]), + ], + ), + ], + ), + ), + ); + } +} + +const _noneText = Text('None', style: TextStyle(fontStyle: FontStyle.italic)); + +class _SelectableTextAndUse extends StatelessWidget { + const _SelectableTextAndUse(this.s, {required this.onUse}); + final String s; + final void Function(String) onUse; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + TextButton(onPressed: () => onUse(s), child: const Text('Use')), + SelectableText(s), + ], + ); + } +} From 6c1fc235d0610cfa94755a7c66f7199889b3fa77 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 22:02:45 +0200 Subject: [PATCH 091/150] MetadataCollecting: Add back the keywords --- TODO.md | 7 ++-- lib/enrichment/metadata_collecting.dart | 51 +++++++++++++++++++++---- lib/helpers.dart | 4 -- 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index da1b946..3b2fc71 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,14 @@ # TODO -* [ ] Add back the keywords -* [ ] Price auto fill +* [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty +* [ ] MetadataCollecting: Price auto fill * [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) -* [ ] Grab the ISBN in real-time with ML Kit (add a minimum repetition of about 20 to avoid false ISBN detection) +* [ ] Camera: Grab the ISBN in real-time with ML Kit (add a minimum repetition of about 20 to avoid false ISBN detection) * [ ] Search with Selenium in headless mode # DONE +* [x] MetadataCollecting: Add back the keywords * [x] MetadataCollecting: Create multiple textFieldController when multiple ISBN * [x] Navigate between enrichment and camera * [x] Compress the images to upload them quicker \ No newline at end of file diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 76fdd45..3ec3a02 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -52,12 +52,9 @@ class _BooksMetadataCollectingWidgetState extends State Author(firstName: '', lastName: line)) - .toList(), + authors: _stringToAuthors(value.authorsTextFieldController.text), blurb: value.blurbTextFieldController.text, - keywords: [], + keywords: _stringToKeywords(value.keywordsTextFieldController.text), priceCent: double.parse(value.priceTextFieldController.text).multiply(100).round(), ))))))); @@ -75,9 +72,16 @@ class _BookControllerSet { final TextEditingController titleTextFieldController = TextEditingController(); final TextEditingController authorsTextFieldController = TextEditingController(); final TextEditingController blurbTextFieldController = TextEditingController(); + final TextEditingController keywordsTextFieldController = TextEditingController(); final TextEditingController priceTextFieldController = TextEditingController(); } +String _keywordsToString(List keywords) => keywords.join(', '); +List _stringToKeywords(String s) => s.split(', ').toList(); + +String _authorsToString(List authors) => authors.map((a) => a.toText()).join('\n'); +List _stringToAuthors(String s) => s.split('\n').map((line) => Author(firstName: '', lastName: line)).toList(); + class _BookMetadataCollectingWidget extends StatefulWidget { const _BookMetadataCollectingWidget({required this.isbn, required this.controllers}); @@ -103,14 +107,17 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi _updateManualTitle(value.title!); }); - final joinedAuthors = value.authors.toText(); + final joinedAuthors = _authorsToString(value.authors); _replaceIfBetterString(joinedAuthors, widget.controllers.authorsTextFieldController.text, () { _updateManualAuthors(joinedAuthors); }); _replaceIfBetterString(value.blurb, widget.controllers.blurbTextFieldController.text, () { _updateManualBlurb(value.blurb!); }); - // TODO: handle keywords + final joinedKeywords = _keywordsToString(value.keywords); + _replaceIfBetterString(joinedAuthors, widget.controllers.keywordsTextFieldController.text, () { + _updateManualKeywords(joinedKeywords); + }); } }); return MapEntry(provider, md); @@ -134,6 +141,10 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi setState(() => widget.controllers.blurbTextFieldController.text = newBlurb); } + void _updateManualKeywords(String newKeywords) { + setState(() => widget.controllers.keywordsTextFieldController.text = newKeywords); + } + @override Widget build(BuildContext context) { const columnHeaderStyle = TextStyle(fontSize: 20, fontWeight: FontWeight.bold); @@ -184,7 +195,7 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi if (authors == null || authors.isEmpty) { return _noneText; } - return SelectableText(authors.toText()); + return SelectableText(_authorsToString(authors)); })), ]), TableRow(children: [ @@ -211,6 +222,30 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi ); })), ]), + TableRow(children: [ + FutureWidget( + future: metadata.entries.first.value, + builder: (data) => TextFormField( + controller: widget.controllers.keywordsTextFieldController, + maxLines: null, + decoration: const InputDecoration( + icon: Icon(Icons.manage_search), + labelText: 'Keywords', + ), + )), + ...metadata.entries.map((e) => FutureWidget( + future: e.value, + builder: (data) { + final keywords = data?.keywords; + if (keywords == null) { + return _noneText; + } + return _SelectableTextAndUse( + _keywordsToString(keywords), + onUse: (b) => _updateManualBlurb(b), + ); + })), + ]), TableRow(children: [ FutureWidget( future: metadata.entries.first.value, diff --git a/lib/helpers.dart b/lib/helpers.dart index ecd8114..aa39740 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -52,10 +52,6 @@ extension AuthorExt on Author { String toText() => [firstName, lastName].where((s) => s.isNotEmpty).join(' '); } -extension AuthorsExt on List { - String toText() => map((a) => a.toText()).join('\n'); -} - extension IntExt on int { int divide(int other) => this ~/ other; } From e155f05ad565be471866ca76b710e16f2eb35ce5 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 22:38:06 +0200 Subject: [PATCH 092/150] AdEditing: _getDescriptions does not write TitleAndAuthors if blurb is empty --- TODO.md | 3 +++ lib/enrichment/ad_editing.dart | 22 ++++++++++++++-------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index 3b2fc71..8fd3c30 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,10 @@ # TODO * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty +* [ ] AdEditing: Disable 'Mark as published' if the title is empty * [ ] MetadataCollecting: Price auto fill +* [ ] BundleSelection: Automatically update bundle list when a bundle is removed or added by the app +* [ ] BundleSelection: Allow manual refresh if the bundle list has been modified by external device * [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) * [ ] Camera: Grab the ISBN in real-time with ML Kit (add a minimum repetition of about 20 to avoid false ISBN detection) * [ ] Search with Selenium in headless mode diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 391059c..f9ca7e5 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -64,15 +64,21 @@ class _AdEditingWidgetState extends State { } String _getDescription(Iterable> metadataFromIsbn) { - if (metadataFromIsbn.length == 1) { - final blurb = metadataFromIsbn.single.value.blurb; - if (blurb == null) return ''; - return 'Résumé:\n' + blurb; + final booksWithBlurb = metadataFromIsbn.where((entry) => entry.value.blurb?.isNotEmpty == true); + if (booksWithBlurb.length == 0) { + return ''; + } else if (booksWithBlurb.length == 1) { + final onlyBookWithBlurb = booksWithBlurb.single.value; + String titleAndAuthor = ''; + // Even if only one book has a blurb, multiple book are in the same ad, so we need to specify which book this blurb is about + if (metadataFromIsbn.length > 1) { + titleAndAuthor = _bookFormatTitleAndAuthor(onlyBookWithBlurb.title!, onlyBookWithBlurb.authors) + '\n'; + } + return 'Résumé:\n' + titleAndAuthor + onlyBookWithBlurb.blurb!; } else { - final bookTitles = metadataFromIsbn - .map((entry) => _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors)) - .join('\n'); - final blurbs = metadataFromIsbn + final bookTitles = + booksWithBlurb.map((entry) => _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors)).join('\n '); + final blurbs = booksWithBlurb .map((entry) => _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors) + ':\n' + entry.value.blurb!) .join('\n'); From 97c2bc1e6f15bf25b7757d749c6bd0e1e090305c Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 22:45:37 +0200 Subject: [PATCH 093/150] MetadataCollecting: Price auto fill --- TODO.md | 2 +- lib/enrichment/metadata_collecting.dart | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 8fd3c30..99dd7a1 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty * [ ] AdEditing: Disable 'Mark as published' if the title is empty -* [ ] MetadataCollecting: Price auto fill * [ ] BundleSelection: Automatically update bundle list when a bundle is removed or added by the app * [ ] BundleSelection: Allow manual refresh if the bundle list has been modified by external device * [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) @@ -11,6 +10,7 @@ # DONE +* [x] MetadataCollecting: Price auto fill * [x] MetadataCollecting: Add back the keywords * [x] MetadataCollecting: Create multiple textFieldController when multiple ISBN * [x] Navigate between enrichment and camera diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 3ec3a02..5812568 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_rust_bridge_template/enrichment/ad_editing.dart'; @@ -118,6 +119,11 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi _replaceIfBetterString(joinedAuthors, widget.controllers.keywordsTextFieldController.text, () { _updateManualKeywords(joinedKeywords); }); + + if (value.marketPrice.isNotEmpty) { + // TODO: If multiple provider give a marketPrice, we should update the manual price by taking into account all the marketPrice received yet, not just the most recent + widget.controllers.priceTextFieldController.text = value.marketPrice.average.round().toString(); + } } }); return MapEntry(provider, md); From cd3f4a1ccc38ff2fa9f83eae73df2bcb1d601a60 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 22:48:45 +0200 Subject: [PATCH 094/150] MetadataCollecting: Remove "use" button for empty keyword list --- lib/enrichment/metadata_collecting.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 5812568..4099cf5 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -243,11 +243,11 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi future: e.value, builder: (data) { final keywords = data?.keywords; - if (keywords == null) { + if (keywords?.isEmpty ?? true) { return _noneText; } return _SelectableTextAndUse( - _keywordsToString(keywords), + _keywordsToString(keywords!), onUse: (b) => _updateManualBlurb(b), ); })), From 7e7a7f247814c7c14bb70173d3c371e85b00877e Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 22:59:18 +0200 Subject: [PATCH 095/150] ISBNDecoding: Sort image by name --- lib/bundle.dart | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/bundle.dart b/lib/bundle.dart index 564e901..fcdccae 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -10,22 +10,23 @@ class Bundle { final Directory directory; - Iterable get images { - return directory.listSync().whereType().where((file) => path.extension(file.path) == '.jpg'); - } + Iterable get images => directory.listImages(); Directory get compressedImagesDir => Directory(path.join(directory.path, 'compressed')); - Iterable get compressedImages { - return compressedImagesDir - .listSync() - .whereType() - .where((file) => path.extension(file.path) == '.jpg') - .sorted((f1, f2) => path.basename(f1.path).compareTo(path.basename(f2.path))); - } + Iterable get compressedImages => compressedImagesDir.listImages(); Metadata get metadata { final metadataFile = File(path.join(directory.path, 'metadata.json')); return Metadata.fromJson(jsonDecode(metadataFile.readAsStringSync()) as Map); } } + +extension _DirExt on Directory { + Iterable listImages() => + listSync().whereType().where((file) => path.extension(file.path) == '.jpg').sortByName(); +} + +extension _ListFileExt on Iterable { + List sortByName() => sorted((f1, f2) => path.basename(f1.path).compareTo(path.basename(f2.path))); +} From ce0be1953dc7cc70f0ad741219676ae09671b41d Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 23:08:34 +0200 Subject: [PATCH 096/150] ISBNDecoding: Only consider ISBN starting with 978 --- lib/enrichment/isbn_decoding.dart | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index 399b8d8..fa67c97 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -58,7 +58,15 @@ class _ISBNDecodingWidgetState extends State { if (snap.hasData == false) { return const CircularProgressIndicator(); } - return Column(children: snap.data!.map((isbn) => Text(isbn)).toList()); + return Column( + children: snap.data! + .map((isbn) => Text( + isbn, + style: TextStyle( + decoration: + isbn.startsWith('978') ? null : TextDecoration.lineThrough), + )) + .toList()); }) ], ), @@ -73,7 +81,7 @@ class _ISBNDecodingWidgetState extends State { builder: (context, snap) { return ElevatedButton( onPressed: () { - final isbnSet = snap.data!.expand((e) => e).toSet(); + final isbnSet = snap.data!.expand((e) => e).where((isbn) => isbn.startsWith('978')).toSet(); print('isbnSet = $isbnSet'); Navigator.push( context, From e7cb79b0cd337f6259edff8d69c290d01acddffe Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 23:28:56 +0200 Subject: [PATCH 097/150] MetadataCollecting: Authors field maxLine: null to have multiple lines available --- lib/enrichment/metadata_collecting.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 4099cf5..d7f5436 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -188,6 +188,7 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi future: metadata.entries.first.value, builder: (data) => TextFormField( controller: widget.controllers.authorsTextFieldController, + maxLines: null, decoration: const InputDecoration( icon: Icon(Icons.person), labelText: 'Authors', From 5ca9e403d6184c40ef53462e39701972e1e0f287 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 3 May 2023 23:58:13 +0200 Subject: [PATCH 098/150] BundleSelection: better UI if more than 3 images overflow --- lib/enrichment/bundle_selection.dart | 36 +++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index a1ac6e8..e3e36d6 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -141,6 +141,35 @@ class BundleWidget extends StatelessWidget { @override Widget build(BuildContext context) { + const maxImagesShown = 3; + final imagesShown = bundle.compressedImages + .take(maxImagesShown) + .mapIndexed((index, f) { + final thumbnail = ImageWidget(f); + if (index == maxImagesShown - 1) { + final nbImagesNotShown = bundle.compressedImages.length - maxImagesShown; + if (nbImagesNotShown > 0) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack( + alignment: Alignment.center, + children: [ + thumbnail, + Positioned.fill(child: ColoredBox(color: Colors.black.withOpacity(0.3))), + Text( + '+$nbImagesNotShown', + style: const TextStyle(color: Colors.white, fontSize: 30), + ), + ], + ); + }, + ); + } + } + return thumbnail; + }) + .map((w) => Padding(padding: const EdgeInsets.all(8.0), child: w)) + .toList(); return Card( // decoration: const BoxDecoration(color: Colors.blue), child: Column( @@ -149,12 +178,7 @@ class BundleWidget extends StatelessWidget { Expanded( child: Row( children: [ - ...bundle.compressedImages - .map((f) => Padding( - padding: const EdgeInsets.all(8.0), - child: ImageWidget(f), - )) - .toList(), + ...imagesShown, const Expanded(child: SizedBox.expand()), IconButton( icon: const Icon(Icons.delete), From 27c6ee141bdb78c5911f6c2523bd47bb506b14f0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 4 May 2023 00:02:56 +0200 Subject: [PATCH 099/150] ISBNDecoding: Increase image size. Add SingleChildScrollView --- lib/enrichment/isbn_decoding.dart | 102 +++++++++++++++--------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index fa67c97..db22b1b 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -42,57 +42,59 @@ class _ISBNDecodingWidgetState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('ISBN decoding')), - body: Column( - children: [ - Wrap( - children: widget.step.bundle.images - .map((imgPath) => Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - SizedBox(height: 300, child: ImageWidget(imgPath)), - FutureBuilder( - future: isbns[imgPath.path]!, - builder: (context, snap) { - if (snap.hasData == false) { - return const CircularProgressIndicator(); - } - return Column( - children: snap.data! - .map((isbn) => Text( - isbn, - style: TextStyle( - decoration: - isbn.startsWith('978') ? null : TextDecoration.lineThrough), - )) - .toList()); - }) - ], + body: SingleChildScrollView( + child: Column( + children: [ + Wrap( + children: widget.step.bundle.images + .map((imgPath) => Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + SizedBox(height: 600, child: ImageWidget(imgPath)), + FutureBuilder( + future: isbns[imgPath.path]!, + builder: (context, snap) { + if (snap.hasData == false) { + return const CircularProgressIndicator(); + } + return Column( + children: snap.data! + .map((isbn) => Text( + isbn, + style: TextStyle( + decoration: + isbn.startsWith('978') ? null : TextDecoration.lineThrough), + )) + .toList()); + }) + ], + ), ), - ), - )) - .toList(), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: FutureBuilder( - future: Future.wait(isbns.values.iter), - builder: (context, snap) { - return ElevatedButton( - onPressed: () { - final isbnSet = snap.data!.expand((e) => e).where((isbn) => isbn.startsWith('978')).toSet(); - print('isbnSet = $isbnSet'); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BooksMetadataCollectingWidget( - step: MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)))); - }, - child: const Text('Validate ISBNs')); - }), - ) - ], + )) + .toList(), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: FutureBuilder( + future: Future.wait(isbns.values.iter), + builder: (context, snap) { + return ElevatedButton( + onPressed: () { + final isbnSet = snap.data!.expand((e) => e).where((isbn) => isbn.startsWith('978')).toSet(); + print('isbnSet = $isbnSet'); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => BooksMetadataCollectingWidget( + step: MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)))); + }, + child: const Text('Validate ISBNs')); + }), + ) + ], + ), ), ); } From a387d1d2f233cdaf609bbfea0ddac5e7b8930cff Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 4 May 2023 22:00:47 +0200 Subject: [PATCH 100/150] README: Add new potential providers --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cc5dea4..9076af2 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ BookMetaData { | [OpenLibrary](https://openlibrary.org/) | blurb are not translated | Its is based on physical books, it is not really a book database | | [Chasse Aux Livre](https://www.chasse-aux-livres.fr/) | price only | it is not possible to parse with Selenium | | [AbeBooks](https://www.abebooks.fr/) | Seems to have good french blurb | | +| [Fnac](https://www.fnac.com/) | blurb, second-hand price | | +| [Librarie Kleber](https://www.librairie-kleber.com/) | blurb, price | | #### GoogleBooks GoogleBooks has some inconsistencies: From 7362d76d42e12b005d4d1f6828bb368dbb093bd7 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 14:06:46 +0200 Subject: [PATCH 101/150] Add Serialize/Deserialize providerMetadata and show then in the bundleSelection widget --- lib/bridge_definitions.dart | 38 ++++++++- lib/bridge_generated.dart | 121 +++++++++++++++++++++++++++ lib/bundle.dart | 20 +++++ lib/common.dart | 18 ++++ lib/enrichment/bundle_selection.dart | 85 +++++++++++++++---- lib/enrichment/isbn_decoding.dart | 17 +--- lib/helpers.dart | 14 ++++ native/Cargo.toml | 2 + native/src/api.rs | 92 +++++++++++++++++++- native/src/bridge_generated.io.rs | 14 ++++ native/src/bridge_generated.rs | 58 +++++++++++++ native/src/common.rs | 6 +- 12 files changed, 448 insertions(+), 37 deletions(-) diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index bf214fa..e886412 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -2,19 +2,29 @@ // Generated by `flutter_rust_bridge`@ 1.68.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const -import 'dart:async'; import 'dart:convert'; - -import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; abstract class Native { + Future getMetadataFromIsbns( + {required List isbns, required String path, dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kGetMetadataFromIsbnsConstMeta; + + Future> getAutoMetadataFromBundle( + {required String path, dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kGetAutoMetadataFromBundleConstMeta; + Future getMetadataFromProvider( {required ProviderEnum provider, required String isbn, dynamic hint}); FlutterRustBridgeTaskConstMeta get kGetMetadataFromProviderConstMeta; - Future publishAd({required Ad ad, required LbcCredential credential, dynamic hint}); + Future publishAd( + {required Ad ad, required LbcCredential credential, dynamic hint}); FlutterRustBridgeTaskConstMeta get kPublishAdConstMeta; } @@ -59,6 +69,16 @@ class BookMetaDataFromProvider { }); } +class ISBNMetadataPair { + final String isbn; + final List metadatas; + + const ISBNMetadataPair({ + required this.isbn, + required this.metadatas, + }); +} + class LbcCredential { String lbcToken; String datadomeCookie; @@ -74,3 +94,13 @@ enum ProviderEnum { GoogleBooks, BooksPrice, } + +class ProviderMetadataPair { + final ProviderEnum provider; + final BookMetaDataFromProvider? metadata; + + const ProviderMetadataPair({ + required this.provider, + this.metadata, + }); +} diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index c53f603..c02e979 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -24,6 +24,45 @@ class NativeImpl implements Native { factory NativeImpl.wasm(FutureOr module) => NativeImpl(module as ExternalLibrary); NativeImpl.raw(this._platform); + Future getMetadataFromIsbns( + {required List isbns, required String path, dynamic hint}) { + var arg0 = _platform.api2wire_StringList(isbns); + var arg1 = _platform.api2wire_String(path); + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => + _platform.inner.wire_get_metadata_from_isbns(port_, arg0, arg1), + parseSuccessData: _wire2api_unit, + constMeta: kGetMetadataFromIsbnsConstMeta, + argValues: [isbns, path], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kGetMetadataFromIsbnsConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "get_metadata_from_isbns", + argNames: ["isbns", "path"], + ); + + Future> getAutoMetadataFromBundle( + {required String path, dynamic hint}) { + var arg0 = _platform.api2wire_String(path); + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => + _platform.inner.wire_get_auto_metadata_from_bundle(port_, arg0), + parseSuccessData: _wire2api_list_isbn_metadata_pair, + constMeta: kGetAutoMetadataFromBundleConstMeta, + argValues: [path], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kGetAutoMetadataFromBundleConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "get_auto_metadata_from_bundle", + argNames: ["path"], + ); + Future getMetadataFromProvider( {required ProviderEnum provider, required String isbn, dynamic hint}) { var arg0 = api2wire_provider_enum(provider); @@ -116,10 +155,35 @@ class NativeImpl implements Native { return raw as Float32List; } + int _wire2api_i32(dynamic raw) { + return raw as int; + } + + ISBNMetadataPair _wire2api_isbn_metadata_pair(dynamic raw) { + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return ISBNMetadataPair( + isbn: _wire2api_String(arr[0]), + metadatas: _wire2api_list_provider_metadata_pair(arr[1]), + ); + } + List _wire2api_list_author(dynamic raw) { return (raw as List).map(_wire2api_author).toList(); } + List _wire2api_list_isbn_metadata_pair(dynamic raw) { + return (raw as List).map(_wire2api_isbn_metadata_pair).toList(); + } + + List _wire2api_list_provider_metadata_pair( + dynamic raw) { + return (raw as List) + .map(_wire2api_provider_metadata_pair) + .toList(); + } + String? _wire2api_opt_String(dynamic raw) { return raw == null ? null : _wire2api_String(raw); } @@ -131,6 +195,20 @@ class NativeImpl implements Native { : _wire2api_box_autoadd_book_meta_data_from_provider(raw); } + ProviderEnum _wire2api_provider_enum(dynamic raw) { + return ProviderEnum.values[raw]; + } + + ProviderMetadataPair _wire2api_provider_metadata_pair(dynamic raw) { + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return ProviderMetadataPair( + provider: _wire2api_provider_enum(arr[0]), + metadata: _wire2api_opt_box_autoadd_book_meta_data_from_provider(arr[1]), + ); + } + int _wire2api_u8(dynamic raw) { return raw as int; } @@ -138,6 +216,10 @@ class NativeImpl implements Native { Uint8List _wire2api_uint_8_list(dynamic raw) { return raw as Uint8List; } + + void _wire2api_unit(dynamic raw) { + return; + } } // Section: api2wire @@ -322,6 +404,45 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr .asFunction)>(); + void wire_get_metadata_from_isbns( + int port_, + ffi.Pointer isbns, + ffi.Pointer path, + ) { + return _wire_get_metadata_from_isbns( + port_, + isbns, + path, + ); + } + + late final _wire_get_metadata_from_isbnsPtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, ffi.Pointer, + ffi.Pointer)>>('wire_get_metadata_from_isbns'); + late final _wire_get_metadata_from_isbns = + _wire_get_metadata_from_isbnsPtr.asFunction< + void Function(int, ffi.Pointer, + ffi.Pointer)>(); + + void wire_get_auto_metadata_from_bundle( + int port_, + ffi.Pointer path, + ) { + return _wire_get_auto_metadata_from_bundle( + port_, + path, + ); + } + + late final _wire_get_auto_metadata_from_bundlePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, ffi.Pointer)>>( + 'wire_get_auto_metadata_from_bundle'); + late final _wire_get_auto_metadata_from_bundle = + _wire_get_auto_metadata_from_bundlePtr + .asFunction)>(); + void wire_get_metadata_from_provider( int port_, int provider, diff --git a/lib/bundle.dart b/lib/bundle.dart index fcdccae..894658a 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -1,10 +1,13 @@ import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:flutter_rust_bridge_template/common.dart'; import 'package:path/path.dart' as path; +import 'bridge_definitions.dart'; + class Bundle { Bundle(this.directory); @@ -20,6 +23,23 @@ class Bundle { final metadataFile = File(path.join(directory.path, 'metadata.json')); return Metadata.fromJson(jsonDecode(metadataFile.readAsStringSync()) as Map); } + + File get autoMetadataFile => File(path.join(directory.path, 'automatic_metadata.json')); +} + +extension ListProviderMetadataPairExt on List { + List getPrices() => + map((e) => e.metadata?.marketPrice.toList()).whereNotNull().expand((i) => i).toList()..sort(); + + BookMetaDataFromProvider mergeAllProvider() { + return BookMetaDataFromProvider( + title: map((e) => e.metadata?.title) + .whereNotNull() + .fold(null, (best, s) => s.length > (best?.length ?? 0) ? s : best), + authors: [], + keywords: [], + marketPrice: Float32List.fromList(getPrices())); + } } extension _DirExt on Directory { diff --git a/lib/common.dart b/lib/common.dart index 4ef9a58..a5936ff 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -4,6 +4,24 @@ import 'package:json_annotation/json_annotation.dart'; part 'common.g.dart'; +Future> extractIsbnsFromImage(File image) { + return Future(() async { + final decoderProcess = await Process.run( + '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', ['-in=' + image.path]); + if (decoderProcess.exitCode != 0) { + print('stdout is ${decoderProcess.stdout}'); + print('stderr is ${decoderProcess.stderr}'); + throw Exception('decoder status is ${decoderProcess.exitCode}'); + } + final s = decoderProcess.stdout as String; + return s.split('\n').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); + }); +} + +extension IterableFutureMapEntryExt on Iterable>> { + Future> toMap() async => Map.fromEntries(await Future.wait(this)); +} + final bookyDir = Platform.isAndroid ? Directory('/storage/emulated/0/DCIM/booky/') : Directory('/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/booky/'); diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index e3e36d6..6552bf9 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -8,6 +8,7 @@ import 'package:path/path.dart' as path; import '../bundle.dart'; import '../common.dart' as common; +import '../ffi.dart'; import '../helpers.dart'; import 'enrichment.dart'; import 'isbn_decoding.dart'; @@ -23,7 +24,27 @@ class _BundleSelectionState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text('Bundle Section')), + appBar: AppBar( + title: const Text('Bundle Section'), + actions: [ + IconButton( + icon: const Icon(Icons.cloud_download), + onPressed: () async { + _listBundles().forEach((bundle) async { + final isbnsList = await Future.wait(bundle.images.map((img) => common.extractIsbnsFromImage(img))); + Set isbns = isbnsList.expand((i) => i).toSet(); + await api.getMetadataFromIsbns( + isbns: isbns.toList(), + path: bundle.autoMetadataFile.path, + ); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Json wrote to file'))); + } + }); + }, + ) + ], + ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.camera), onPressed: () => @@ -33,11 +54,7 @@ class _BundleSelectionState extends State { Widget _getBody() { try { - final bundles = common.bookyDir - .listSync() - .whereType() - .sorted((d1, d2) => d1.path.compareTo(d2.path)) - .map((d) => Bundle(d)); + final bundles = _listBundles(); return BundleList(bundles); } catch (e) { @@ -63,6 +80,14 @@ class _BundleSelectionState extends State { rethrow; } } + + static Iterable _listBundles() { + return common.bookyDir + .listSync() + .whereType() + .sorted((d1, d2) => d1.path.compareTo(d2.path)) + .map((d) => Bundle(d)); + } } class BundleList extends StatefulWidget { @@ -133,21 +158,33 @@ class _BundleListState extends State { } } -class BundleWidget extends StatelessWidget { +class BundleWidget extends StatefulWidget { const BundleWidget(this.bundle, {required this.onDelete}); final Bundle bundle; final void Function() onDelete; + @override + State createState() => _BundleWidgetState(); +} + +class _BundleWidgetState extends State { + late Future> cachedAutoMetadata; + @override + void initState() { + super.initState(); + cachedAutoMetadata = api.getAutoMetadataFromBundle(path: widget.bundle.autoMetadataFile.path); + } + @override Widget build(BuildContext context) { const maxImagesShown = 3; - final imagesShown = bundle.compressedImages + final imagesShown = widget.bundle.compressedImages .take(maxImagesShown) .mapIndexed((index, f) { final thumbnail = ImageWidget(f); if (index == maxImagesShown - 1) { - final nbImagesNotShown = bundle.compressedImages.length - maxImagesShown; + final nbImagesNotShown = widget.bundle.compressedImages.length - maxImagesShown; if (nbImagesNotShown > 0) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { @@ -171,10 +208,30 @@ class BundleWidget extends StatelessWidget { .map((w) => Padding(padding: const EdgeInsets.all(8.0), child: w)) .toList(); return Card( - // decoration: const BoxDecoration(color: Colors.blue), child: Column( children: [ - Text(path.basename(bundle.directory.path)), + // Text(path.basename(widget.bundle.directory.path)), + FutureWidget( + future: cachedAutoMetadata, + builder: (autoMetadata) { + final firstBook = autoMetadata.firstOrNull; + if (firstBook == null) return const Text('No book identified'); + final md = firstBook.metadatas.mergeAllProvider(); + final priceRange = md.marketPrice.toList(); + return Row(children: [ + // Text(firstBook.isbn), + md.title.ifIs( + notnull: (t) => Text(t, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), + nul: () => const Text( + 'No title found', + style: TextStyle(fontStyle: FontStyle.italic), + )), + const Spacer(), + priceRange.isEmpty + ? const Text('?') + : Text('${priceRange.first.toInt()} - ${priceRange.last.toInt()} €'), + ]); + }), Expanded( child: Row( children: [ @@ -183,13 +240,13 @@ class BundleWidget extends StatelessWidget { IconButton( icon: const Icon(Icons.delete), onPressed: () { - final segments = path.split(bundle.directory.path); + final segments = path.split(widget.bundle.directory.path); segments[segments.length - 2] = 'booky_deleted'; - bundle.directory.renameSync(path.joinAll(segments)); + widget.bundle.directory.renameSync(path.joinAll(segments)); ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Deleted'), )); - onDelete(); + widget.onDelete(); }, ), ], diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index db22b1b..da83312 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -1,8 +1,7 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:kt_dart/collection.dart'; +import '../common.dart' as common; import '../helpers.dart'; import 'enrichment.dart'; import 'metadata_collecting.dart'; @@ -22,19 +21,7 @@ class _ISBNDecodingWidgetState extends State { void initState() { super.initState(); widget.step.bundle.images.forEach((image) { - final imgPath = image.path; - isbns[imgPath] = Future(() async { - final decoderProcess = await Process.run( - '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', - ['-in=' + imgPath]); - if (decoderProcess.exitCode != 0) { - print('stdout is ${decoderProcess.stdout}'); - print('stderr is ${decoderProcess.stderr}'); - throw Exception('decoder status is ${decoderProcess.exitCode}'); - } - final s = decoderProcess.stdout as String; - return s.split('\n').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); - }); + isbns[image.path] = common.extractIsbnsFromImage(image); }); } diff --git a/lib/helpers.dart b/lib/helpers.dart index aa39740..700c6e6 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -41,6 +41,9 @@ class AsyncSnapshotWidget extends StatelessWidget { case ConnectionState.waiting: return const CircularProgressIndicator(); case ConnectionState.done: + if (snap.hasError) { + return Text(snap.error.toString()); + } return builder(snap.data as T); default: return const Text('???'); @@ -60,6 +63,17 @@ extension DoubleExt on double { double multiply(double other) => this * other; } +extension IfNullExt on T? { + R ifIs({required R Function(T) notnull, required R Function() nul}) { + final t = this; + if (t == null) { + return nul(); + } else { + return notnull(t); + } + } +} + class BookMetaDataManual { String? title; List authors; diff --git a/native/Cargo.toml b/native/Cargo.toml index bb2b159..c756ca7 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -27,3 +27,5 @@ color-eyre = "0.6.2" hyper = { version = "0.14", features = ["server", "tcp"] } futures = "0.3.28" html2text = "0.5.1" +strum = { version = "0.24.1", features = ["strum_macros"] } +strum_macros = "0.24.3" diff --git a/native/src/api.rs b/native/src/api.rs index dfdb8da..0c9e826 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -1,19 +1,107 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::{Read, Write}; + use crate::cached_client::CachedClient; -use crate::common::{Ad, BookMetaDataFromProvider}; +use crate::common; +use crate::common::Ad; use crate::common::{LbcCredential, Provider}; use crate::publisher::Publisher; use crate::{babelio, booksprice, google_books, leboncoin}; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use strum::IntoEnumIterator; +use strum_macros::EnumIter; +#[derive(EnumIter, PartialEq, Eq, Hash, Debug, Deserialize, Serialize, Copy, Clone)] pub enum ProviderEnum { Babelio, GoogleBooks, BooksPrice, } +pub fn get_metadata_from_isbns(isbns: Vec, path: String) -> Result<(), anyhow::Error> { + let res = isbns.iter().map(|isbn| { + let mds: HashMap> = + ProviderEnum::iter() + .map(|provider| { + let md = get_metadata_from_provider(provider, isbn.clone()); + (provider, md) + }) + .collect(); + (isbn, mds) + }); + let hashmap: HashMap<&String, HashMap>> = + HashMap::from_iter(res); + + let tmp_path = "tmp.txt"; + let mut file = File::create(tmp_path)?; + file.write_all( + serde_json::to_string(&hashmap) + .expect("Unable to serialize data") + .as_bytes(), + )?; + // Writing to the phone does not work + // Instead a temporary file is created and immediatly move with 'gio move' + std::process::Command::new("gio") + .args(["move", tmp_path, &path]) + .output()?; + Ok(()) +} + +// FlutterRustBridge does not support returning HashMap, or template type (like MyPair) +// So a type for each pair is created +#[derive(Debug)] +pub struct ISBNMetadataPair { + pub isbn: String, + pub metadatas: Vec, +} +#[derive(Debug)] + +pub struct ProviderMetadataPair { + pub provider: ProviderEnum, + pub metadata: Option, +} + +pub fn get_auto_metadata_from_bundle(path: String) -> Result, anyhow::Error> { + let mut file = File::open(path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents).unwrap(); + + let raw_map: HashMap>> = + serde_json::from_str(&contents).unwrap(); + + let vec_of_vec = raw_map + .iter() + .collect_vec() + .iter() + .map(|entry| { + let v2: Vec = entry + .1 + .iter() + .collect::)>>() + .iter() + .map(|entry| { + let res = ProviderMetadataPair { + provider: entry.0.to_owned(), + metadata: entry.1.to_owned(), + }; + res + }) + .collect_vec(); + ISBNMetadataPair { + isbn: entry.0.to_owned(), + metadatas: v2, + } + }) + .collect_vec(); + Ok(vec_of_vec) +} + pub fn get_metadata_from_provider( provider: ProviderEnum, isbn: String, -) -> Option { +) -> Option { match provider { ProviderEnum::Babelio => babelio::Babelio {}.get_book_metadata_from_isbn(&isbn), ProviderEnum::GoogleBooks => google_books::GoogleBooks { diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index 628e8de..15bb1a0 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -1,6 +1,20 @@ use super::*; // Section: wire functions +#[no_mangle] +pub extern "C" fn wire_get_metadata_from_isbns( + port_: i64, + isbns: *mut wire_StringList, + path: *mut wire_uint_8_list, +) { + wire_get_metadata_from_isbns_impl(port_, isbns, path) +} + +#[no_mangle] +pub extern "C" fn wire_get_auto_metadata_from_bundle(port_: i64, path: *mut wire_uint_8_list) { + wire_get_auto_metadata_from_bundle_impl(port_, path) +} + #[no_mangle] pub extern "C" fn wire_get_metadata_from_provider( port_: i64, diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 719b492..3834c41 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -26,6 +26,40 @@ use crate::common::LbcCredential; // Section: wire functions +fn wire_get_metadata_from_isbns_impl( + port_: MessagePort, + isbns: impl Wire2Api> + UnwindSafe, + path: impl Wire2Api + UnwindSafe, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap( + WrapInfo { + debug_name: "get_metadata_from_isbns", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || { + let api_isbns = isbns.wire2api(); + let api_path = path.wire2api(); + move |task_callback| get_metadata_from_isbns(api_isbns, api_path) + }, + ) +} +fn wire_get_auto_metadata_from_bundle_impl( + port_: MessagePort, + path: impl Wire2Api + UnwindSafe, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap( + WrapInfo { + debug_name: "get_auto_metadata_from_bundle", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || { + let api_path = path.wire2api(); + move |task_callback| get_auto_metadata_from_bundle(api_path) + }, + ) +} fn wire_get_metadata_from_provider_impl( port_: MessagePort, provider: impl Wire2Api + UnwindSafe, @@ -130,6 +164,30 @@ impl support::IntoDart for BookMetaDataFromProvider { } impl support::IntoDartExceptPrimitive for BookMetaDataFromProvider {} +impl support::IntoDart for ISBNMetadataPair { + fn into_dart(self) -> support::DartAbi { + vec![self.isbn.into_dart(), self.metadatas.into_dart()].into_dart() + } +} +impl support::IntoDartExceptPrimitive for ISBNMetadataPair {} + +impl support::IntoDart for ProviderEnum { + fn into_dart(self) -> support::DartAbi { + match self { + Self::Babelio => 0, + Self::GoogleBooks => 1, + Self::BooksPrice => 2, + } + .into_dart() + } +} +impl support::IntoDart for ProviderMetadataPair { + fn into_dart(self) -> support::DartAbi { + vec![self.provider.into_dart(), self.metadata.into_dart()].into_dart() + } +} +impl support::IntoDartExceptPrimitive for ProviderMetadataPair {} + // Section: executor support::lazy_static! { diff --git a/native/src/common.rs b/native/src/common.rs index 28ad14e..aa37092 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -1,4 +1,6 @@ -#[derive(Default, Debug, PartialEq)] +use serde::{Serialize, Deserialize}; + +#[derive(Default, Debug, PartialEq, Deserialize, Serialize, Clone)] pub struct BookMetaDataFromProvider { pub title: Option, pub authors: Vec, @@ -10,7 +12,7 @@ pub struct BookMetaDataFromProvider { pub market_price: Vec, } -#[derive(Debug, PartialEq, Eq, Hash, Clone)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)] pub struct Author { pub first_name: String, pub last_name: String, From ca6dc6cdb9b92723c391487325684bbb2e60ce4a Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 14:37:43 +0200 Subject: [PATCH 102/150] BundleSelection: Better handle title overflow --- lib/enrichment/bundle_selection.dart | 118 +++++++++++++-------------- lib/helpers.dart | 15 ++++ 2 files changed, 74 insertions(+), 59 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 6552bf9..9bb02b2 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -137,21 +137,18 @@ class _BundleListState extends State { maxCrossAxisExtent: 500, childAspectRatio: 2, children: widget.bundles - .map((bundle) => Padding( - padding: const EdgeInsets.all(2.0), - child: GestureDetector( - child: BundleWidget(bundle, onDelete: () { - setState(() {}); - }), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ISBNDecodingWidget( - step: ISBNDecodingStep(bundle: bundle), - ))); - }, - ), + .map((bundle) => GestureDetector( + child: BundleWidget(bundle, onDelete: () { + setState(() {}); + }), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ISBNDecodingWidget( + step: ISBNDecodingStep(bundle: bundle), + ))); + }, )) .toList(), ); @@ -208,51 +205,54 @@ class _BundleWidgetState extends State { .map((w) => Padding(padding: const EdgeInsets.all(8.0), child: w)) .toList(); return Card( - child: Column( - children: [ - // Text(path.basename(widget.bundle.directory.path)), - FutureWidget( - future: cachedAutoMetadata, - builder: (autoMetadata) { - final firstBook = autoMetadata.firstOrNull; - if (firstBook == null) return const Text('No book identified'); - final md = firstBook.metadatas.mergeAllProvider(); - final priceRange = md.marketPrice.toList(); - return Row(children: [ - // Text(firstBook.isbn), - md.title.ifIs( - notnull: (t) => Text(t, style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), - nul: () => const Text( - 'No title found', - style: TextStyle(fontStyle: FontStyle.italic), - )), - const Spacer(), - priceRange.isEmpty - ? const Text('?') - : Text('${priceRange.first.toInt()} - ${priceRange.last.toInt()} €'), - ]); - }), - Expanded( - child: Row( - children: [ - ...imagesShown, - const Expanded(child: SizedBox.expand()), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - final segments = path.split(widget.bundle.directory.path); - segments[segments.length - 2] = 'booky_deleted'; - widget.bundle.directory.renameSync(path.joinAll(segments)); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text('Deleted'), - )); - widget.onDelete(); - }, - ), - ], + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + children: [ + // Text(path.basename(widget.bundle.directory.path)), + FutureWidget( + future: cachedAutoMetadata, + builder: (autoMetadata) { + final firstBook = autoMetadata.firstOrNull; + if (firstBook == null) return const Text('No book identified'); + final md = firstBook.metadatas.mergeAllProvider(); + final priceRange = md.marketPrice.toList(); + return Row(children: [ + // Text(firstBook.isbn), + Expanded( + child: md.title.ifIs( + notnull: (t) => TextWithTooltip(t), + nul: () => const Text( + 'No title found', + style: TextStyle(fontStyle: FontStyle.italic), + ))), + priceRange.isEmpty + ? const Text('?') + : Text('${priceRange.first.toInt()} - ${priceRange.last.toInt()} €'), + ]); + }), + Expanded( + child: Row( + children: [ + ...imagesShown, + const Expanded(child: SizedBox.expand()), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + final segments = path.split(widget.bundle.directory.path); + segments[segments.length - 2] = 'booky_deleted'; + widget.bundle.directory.renameSync(path.joinAll(segments)); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Deleted'), + )); + widget.onDelete(); + }, + ), + ], + ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/helpers.dart b/lib/helpers.dart index 700c6e6..2843a6c 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -19,6 +19,21 @@ class ImageWidget extends StatelessWidget { } } +class TextWithTooltip extends StatelessWidget { + const TextWithTooltip(this.text); + final String text; + + @override + Widget build(BuildContext context) { + return Tooltip( + message: text, + child: Text(text, + softWrap: false, + style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold, overflow: TextOverflow.fade)), + ); + } +} + class FutureWidget extends StatelessWidget { const FutureWidget({required this.future, required this.builder}); final Future future; From 6b1a67e51ce8cb4de2bf3b4bca41cdfad2d48679 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 14:56:01 +0200 Subject: [PATCH 103/150] bridge_generated.dart: Add explicit cast from dynamic to int --- lib/bridge_generated.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index c02e979..f23e032 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -196,7 +196,7 @@ class NativeImpl implements Native { } ProviderEnum _wire2api_provider_enum(dynamic raw) { - return ProviderEnum.values[raw]; + return ProviderEnum.values[raw as int]; } ProviderMetadataPair _wire2api_provider_metadata_pair(dynamic raw) { From 54ce2c122b1e30935cf9a10261893360b76fc1ea Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 19:42:00 +0200 Subject: [PATCH 104/150] Camera: Add live barcode detection while pressing button --- lib/camera/barcode_detector_painter.dart | 88 +++++++++++ lib/camera/camera.dart | 193 +++++++++++++++++------ lib/camera/coordinates_translator.dart | 36 +++++ pubspec.yaml | 2 + 4 files changed, 275 insertions(+), 44 deletions(-) create mode 100644 lib/camera/barcode_detector_painter.dart create mode 100644 lib/camera/coordinates_translator.dart diff --git a/lib/camera/barcode_detector_painter.dart b/lib/camera/barcode_detector_painter.dart new file mode 100644 index 0000000..a875a9e --- /dev/null +++ b/lib/camera/barcode_detector_painter.dart @@ -0,0 +1,88 @@ +import 'dart:math'; +import 'dart:ui'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart'; + +import 'coordinates_translator.dart'; + +class BarcodeDetectorPainter extends CustomPainter { + BarcodeDetectorPainter(this.barcodes, this.absoluteImageSize, this.rotation); + + final List barcodes; + final Size absoluteImageSize; + final InputImageRotation rotation; + + @override + void paint(Canvas canvas, Size size) { + final Paint paint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 3.0 + ..color = Colors.lightGreenAccent; + + final Paint background = Paint()..color = const Color(0x99000000); + + for (final Barcode barcode in barcodes) { + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(textAlign: TextAlign.left, fontSize: 16, textDirection: TextDirection.ltr), + ); + builder.pushStyle(ui.TextStyle(color: Colors.lightGreenAccent, background: background)); + builder.addText('${barcode.displayValue}'); + builder.pop(); + + // Store the points for the bounding box + double left = double.infinity; + double top = double.infinity; + double right = double.negativeInfinity; + double bottom = double.negativeInfinity; + + final cornerPoints = barcode.cornerPoints; + final boundingBox = barcode.boundingBox; + if (cornerPoints == null) { + if (boundingBox != null) { + left = translateX(boundingBox.left, rotation, size, absoluteImageSize); + top = translateY(boundingBox.top, rotation, size, absoluteImageSize); + right = translateX(boundingBox.right, rotation, size, absoluteImageSize); + bottom = translateY(boundingBox.bottom, rotation, size, absoluteImageSize); + + // Draw a bounding rectangle around the barcode + canvas.drawRect( + Rect.fromLTRB(left, top, right, bottom), + paint, + ); + } + } else { + final List offsetPoints = []; + for (final point in cornerPoints) { + final double x = translateX(point.x.toDouble(), rotation, size, absoluteImageSize); + final double y = translateY(point.y.toDouble(), rotation, size, absoluteImageSize); + + offsetPoints.add(Offset(x, y)); + + // Due to possible rotations we need to find the smallest and largest + top = min(top, y); + bottom = max(bottom, y); + left = min(left, x); + right = max(right, x); + } + // Add the first point to close the polygon + offsetPoints.add(offsetPoints.first); + canvas.drawPoints(PointMode.polygon, offsetPoints, paint); + } + + canvas.drawParagraph( + builder.build() + ..layout(ParagraphConstraints( + width: right - left, + )), + Offset(left, top), + ); + } + } + + @override + bool shouldRepaint(BarcodeDetectorPainter oldDelegate) { + return oldDelegate.absoluteImageSize != absoluteImageSize || oldDelegate.barcodes != barcodes; + } +} diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 93d749e..cff397a 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -2,12 +2,15 @@ import 'dart:convert'; import 'dart:io'; import 'package:camera/camera.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:google_mlkit_barcode_scanning/google_mlkit_barcode_scanning.dart'; import 'package:path/path.dart' as path; import 'package:permission_handler/permission_handler.dart'; import '../bundle.dart'; import '../common.dart' as common; +import 'barcode_detector_painter.dart'; import 'draggable_widget.dart'; /// Camera example home widget. @@ -30,6 +33,12 @@ class _CameraWidgetState extends State with WidgetsBindingObserver CameraController? controller; late String bundleName; + final BarcodeScanner _barcodeScanner = BarcodeScanner(formats: [BarcodeFormat.ean13]); + bool _isBusy = false; + bool _canProcessBarcode = true; + CustomPaint? _customPaint; + final Map _registeredBarcodes = {}; + Directory get getBundleDir => Directory(path.join(common.bookyDir.path, bundleName)); Bundle get getBundle => Bundle(getBundleDir); @@ -57,6 +66,8 @@ class _CameraWidgetState extends State with WidgetsBindingObserver @override void dispose() { WidgetsBinding.instance.removeObserver(this); + _canProcessBarcode = false; + _barcodeScanner.close(); super.dispose(); } @@ -108,23 +119,49 @@ class _CameraWidgetState extends State with WidgetsBindingObserver body: Column( children: [ Expanded( - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Center( - child: _cameraPreviewWidget(), - ), + child: Row( + children: [ + SizedBox( + width: 200, + child: Column( + children: _registeredBarcodes.entries.map((entry) { + final barcode = entry.key; + return Text( + '$barcode ${entry.value}', + style: TextStyle( + fontWeight: barcode.startsWith('978') ? FontWeight.bold : FontWeight.normal, + color: entry.value > 20 ? Colors.black : Colors.grey.shade200), + ); + }).toList(), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Center( + child: _cameraPreviewWidget(), + ), + ), + ), + ], ), ), Padding( padding: const EdgeInsets.all(5.0), child: BottomWidget( - bundle: getBundle, - onSubmit: () { - setState(() { - _generateNewFolderPath(); - }); - Navigator.pop(context); - }), + bundle: getBundle, + onSubmit: () { + setState(() { + _generateNewFolderPath(); + }); + Navigator.pop(context); + }, + onBarcodeDetectStart: () => controller!.startImageStream(_processCameraImage), + onBarcodeDetectStop: () async { + await controller!.stopImageStream(); + setState(() => _customPaint = null); + }, + ), ), ], ), @@ -145,22 +182,28 @@ class _CameraWidgetState extends State with WidgetsBindingObserver ), ); } else { - return CameraPreview( - controller!, - child: LayoutBuilder( - builder: (context, boxConstraints) => GestureDetector( - onTapDown: (TapDownDetails details) async { - _onViewFinderTap(details, boxConstraints); - // The auto focus is not instantaneous. We must wait a little while before taking the picture - // In release mode, if we - // wait 100 ms : blurry - // wait 300 ms : sharp - // The optimum delay shall lie between the bounds - await Future.delayed(const Duration(milliseconds: 300)); - _onTakePictureButtonPressed(); - }, + return Stack( + fit: StackFit.expand, + children: [ + CameraPreview( + controller!, + child: LayoutBuilder( + builder: (context, boxConstraints) => GestureDetector( + onTapDown: (TapDownDetails details) async { + _onViewFinderTap(details, boxConstraints); + // The auto focus is not instantaneous. We must wait a little while before taking the picture + // In release mode, if we + // wait 100 ms : blurry + // wait 300 ms : sharp + // The optimum delay shall lie between the bounds + await Future.delayed(const Duration(milliseconds: 300)); + _onTakePictureButtonPressed(); + }, + ), + ), ), - ), + if (_customPaint != null) IgnorePointer(child: _customPaint!), + ], ); } } @@ -251,6 +294,65 @@ class _CameraWidgetState extends State with WidgetsBindingObserver } } + Future _processCameraImage(CameraImage image) async { + final WriteBuffer allBytes = WriteBuffer(); + for (final Plane plane in image.planes) { + allBytes.putUint8List(plane.bytes); + } + final bytes = allBytes.done().buffer.asUint8List(); + + final Size imageSize = Size(image.width.toDouble(), image.height.toDouble()); + + final camera = controller!.description; + final imageRotation = InputImageRotationValue.fromRawValue(camera.sensorOrientation); + if (imageRotation == null) return; + + final inputImageFormat = InputImageFormatValue.fromRawValue(image.format.raw as int); + if (inputImageFormat == null) return; + + final planeData = image.planes.map( + (Plane plane) { + return InputImagePlaneMetadata( + bytesPerRow: plane.bytesPerRow, + height: plane.height, + width: plane.width, + ); + }, + ).toList(); + + final inputImageData = InputImageData( + size: imageSize, + imageRotation: imageRotation, + inputImageFormat: inputImageFormat, + planeData: planeData, + ); + + final inputImage = InputImage.fromBytes(bytes: bytes, inputImageData: inputImageData); + + _extractBarcodeFromImage(inputImage); + } + + Future _extractBarcodeFromImage(InputImage inputImage) async { + if (!_canProcessBarcode) return; + if (_isBusy) return; + _isBusy = true; + final barcodes = await _barcodeScanner.processImage(inputImage); + if (inputImage.inputImageData?.size != null && inputImage.inputImageData?.imageRotation != null) { + final painter = + BarcodeDetectorPainter(barcodes, inputImage.inputImageData!.size, inputImage.inputImageData!.imageRotation); + _customPaint = CustomPaint(painter: painter); + + final barcodesString = barcodes.map((barcode) => barcode.displayValue).whereType(); + for (final barcodeString in barcodesString) { + _registeredBarcodes.update(barcodeString, (oldCount) => oldCount + 1, ifAbsent: () => 1); + } + } + _isBusy = false; + if (mounted) { + setState(() {}); + } + } + void _onTakePictureButtonPressed() { takePicture().then((XFile? file) async { if (mounted) { @@ -311,9 +413,15 @@ class _CameraWidgetState extends State with WidgetsBindingObserver } class BottomWidget extends StatefulWidget { - const BottomWidget({required this.bundle, required this.onSubmit}); + const BottomWidget( + {required this.bundle, + required this.onSubmit, + required this.onBarcodeDetectStart, + required this.onBarcodeDetectStop}); final Bundle bundle; final void Function() onSubmit; + final void Function() onBarcodeDetectStart; + final void Function() onBarcodeDetectStop; @override State createState() => _BottomWidgetState(); @@ -326,6 +434,7 @@ class _BottomWidgetState extends State { return Row( children: [ _thumbnailWidget(widget.bundle.images), + _barcodeDetectionButton(), _addMetadataButton(context: context, directory: widget.bundle.directory, onSubmit: widget.onSubmit), ], ); @@ -334,6 +443,18 @@ class _BottomWidgetState extends State { } } + Widget _barcodeDetectionButton() { + return GestureDetector( + onTapDown: (_) { + widget.onBarcodeDetectStart(); + }, + onTapUp: (_) { + widget.onBarcodeDetectStop(); + }, + child: const Icon(Icons.select_all_rounded), + ); + } + /// Display the thumbnail of the captured image or video. Widget _thumbnailWidget(Iterable images) { return Expanded( @@ -420,19 +541,3 @@ class _MetadataWidgetState extends State { } List _cameras = []; -/* - -Future main() async { - runApp(const MaterialApp(home: Explorer())); - // Fetch the available cameras before initializing the app. - */ -/*try { - WidgetsFlutterBinding.ensureInitialized(); - _cameras = await availableCameras(); - } on CameraException catch (e) { - _logError(e.code, e.description); - } - runApp(const CameraApp());*/ /* - -} -*/ diff --git a/lib/camera/coordinates_translator.dart b/lib/camera/coordinates_translator.dart new file mode 100644 index 0000000..a1fabf7 --- /dev/null +++ b/lib/camera/coordinates_translator.dart @@ -0,0 +1,36 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:google_mlkit_commons/google_mlkit_commons.dart'; + +double translateX( + double x, InputImageRotation rotation, Size size, Size absoluteImageSize) { + switch (rotation) { + case InputImageRotation.rotation90deg: + return x * + size.width / + (Platform.isIOS ? absoluteImageSize.width : absoluteImageSize.height); + case InputImageRotation.rotation270deg: + return size.width - + x * + size.width / + (Platform.isIOS + ? absoluteImageSize.width + : absoluteImageSize.height); + default: + return x * size.width / absoluteImageSize.width; + } +} + +double translateY( + double y, InputImageRotation rotation, Size size, Size absoluteImageSize) { + switch (rotation) { + case InputImageRotation.rotation90deg: + case InputImageRotation.rotation270deg: + return y * + size.height / + (Platform.isIOS ? absoluteImageSize.height : absoluteImageSize.width); + default: + return y * size.height / absoluteImageSize.height; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 39734b3..a83bfbc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,6 +30,8 @@ dependencies: kt_dart: ^1.1.0 camera: ^0.10.3+2 permission_handler: ^10.2.0 + google_mlkit_commons: ^0.3.0 + google_mlkit_barcode_scanning: ^0.6.0 flutter_image_compress: ^1.1.3 From 78292d9484ee2d73d7bba85cb25969e1180a7a01 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 19:48:47 +0200 Subject: [PATCH 105/150] Camera: Remove imageFormatGroup --- lib/camera/camera.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index cff397a..947e552 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -242,7 +242,9 @@ class _CameraWidgetState extends State with WidgetsBindingObserver final CameraController cameraController = CameraController( cameraDescription, ResolutionPreset.max, - imageFormatGroup: ImageFormatGroup.jpeg, + // If the imageFormatGroup is specified, the stream processing does not work anymore + // It seems that the default format for still picture is jpeg, bu the default format for streaming is not + // imageFormatGroup: ImageFormatGroup.jpeg, ); controller = cameraController; From 9c0676f025122ae40bf9b3ce086a963bd90c6585 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 19:59:25 +0200 Subject: [PATCH 106/150] Camera: preview keep camera ratio --- lib/camera/camera.dart | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 947e552..b23933d 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -182,28 +182,25 @@ class _CameraWidgetState extends State with WidgetsBindingObserver ), ); } else { - return Stack( - fit: StackFit.expand, - children: [ - CameraPreview( - controller!, - child: LayoutBuilder( - builder: (context, boxConstraints) => GestureDetector( - onTapDown: (TapDownDetails details) async { - _onViewFinderTap(details, boxConstraints); - // The auto focus is not instantaneous. We must wait a little while before taking the picture - // In release mode, if we - // wait 100 ms : blurry - // wait 300 ms : sharp - // The optimum delay shall lie between the bounds - await Future.delayed(const Duration(milliseconds: 300)); - _onTakePictureButtonPressed(); - }, - ), + return Center( + child: CameraPreview( + controller!, + child: LayoutBuilder( + builder: (context, boxConstraints) => GestureDetector( + onTapDown: (TapDownDetails details) async { + _onViewFinderTap(details, boxConstraints); + // The auto focus is not instantaneous. We must wait a little while before taking the picture + // In release mode, if we + // wait 100 ms : blurry + // wait 300 ms : sharp + // The optimum delay shall lie between the bounds + await Future.delayed(const Duration(milliseconds: 300)); + _onTakePictureButtonPressed(); + }, + child: _customPaint, ), ), - if (_customPaint != null) IgnorePointer(child: _customPaint!), - ], + ), ); } } From a33da584a74f9f3fab8debd414e311facf995e50 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 20:07:30 +0200 Subject: [PATCH 107/150] Camera: remove barcode by tappin bin icon --- lib/camera/camera.dart | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index b23933d..c1fbbcf 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -89,6 +89,7 @@ class _CameraWidgetState extends State with WidgetsBindingObserver } // #enddocregion AppLifecycle + static const MIN_BARCODE_OCCURENCE = 20; @override Widget build(BuildContext context) { return Scaffold( @@ -122,17 +123,28 @@ class _CameraWidgetState extends State with WidgetsBindingObserver child: Row( children: [ SizedBox( - width: 200, - child: Column( - children: _registeredBarcodes.entries.map((entry) { - final barcode = entry.key; - return Text( - '$barcode ${entry.value}', - style: TextStyle( - fontWeight: barcode.startsWith('978') ? FontWeight.bold : FontWeight.normal, - color: entry.value > 20 ? Colors.black : Colors.grey.shade200), - ); - }).toList(), + width: 180, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: _registeredBarcodes.entries + .where((entry) => entry.key.startsWith('978') && entry.value > MIN_BARCODE_OCCURENCE) + .map((entry) { + final barcode = entry.key; + return Row( + children: [ + Expanded(child: Text(barcode, style: const TextStyle(fontWeight: FontWeight.bold))), + IconButton( + onPressed: () { + setState(() { + _registeredBarcodes.remove(barcode); + }); + }, + icon: const Icon(Icons.delete)) + ], + ); + }).toList(), + ), ), ), Expanded( From a7e73999f988f6148a33fef5e0bb2b1536728fe0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 12 May 2023 20:24:31 +0200 Subject: [PATCH 108/150] Camera: Look for barcode in each picture taken --- lib/camera/camera.dart | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index c1fbbcf..8162a79 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -89,7 +89,13 @@ class _CameraWidgetState extends State with WidgetsBindingObserver } // #enddocregion AppLifecycle - static const MIN_BARCODE_OCCURENCE = 20; + /// The minimum number of time the barcode stream decoder must see a barcode to consider it valid. + /// Use the prevent false barcode decoding to show up (due to glare, poor image quality) + static const minBarcodeOccurrence = 20; + + /// All ISBN (EAN-13) should start with 978 + /// Use to prevent false barcode decoding + static const isbnPrefix = '978'; @override Widget build(BuildContext context) { return Scaffold( @@ -128,7 +134,7 @@ class _CameraWidgetState extends State with WidgetsBindingObserver padding: const EdgeInsets.all(8.0), child: Column( children: _registeredBarcodes.entries - .where((entry) => entry.key.startsWith('978') && entry.value > MIN_BARCODE_OCCURENCE) + .where((entry) => entry.key.startsWith(isbnPrefix) && entry.value >= minBarcodeOccurrence) .map((entry) { final barcode = entry.key; return Row( @@ -366,11 +372,24 @@ class _CameraWidgetState extends State with WidgetsBindingObserver void _onTakePictureButtonPressed() { takePicture().then((XFile? file) async { + if (file == null) { + return; + } if (mounted) { - if (file != null) { - await getBundleDir.create(); - file.saveTo(_getFirstUnusedName(getBundleDir)); - } + await getBundleDir.create(); + file.saveTo(_getFirstUnusedName(getBundleDir)); + } + + final inputImage = InputImage.fromFilePath(file.path); + final barcodes = await _barcodeScanner.processImage(inputImage); + + final barcodesString = barcodes.map((barcode) => barcode.displayValue).whereType(); + for (final barcodeString in barcodesString) { + _registeredBarcodes.update(barcodeString, (oldCount) => oldCount + minBarcodeOccurrence, + ifAbsent: () => minBarcodeOccurrence); + } + if (mounted) { + setState(() {}); } }); } From d33be665af291b159af2f5ed265cd90eb94459af Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 13 May 2023 00:13:59 +0200 Subject: [PATCH 109/150] BundleSelection: add show in folder button on Linux --- lib/enrichment/bundle_selection.dart | 31 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 9bb02b2..13c9609 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -236,17 +236,26 @@ class _BundleWidgetState extends State { children: [ ...imagesShown, const Expanded(child: SizedBox.expand()), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - final segments = path.split(widget.bundle.directory.path); - segments[segments.length - 2] = 'booky_deleted'; - widget.bundle.directory.renameSync(path.joinAll(segments)); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text('Deleted'), - )); - widget.onDelete(); - }, + Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + if (Platform.isLinux) + IconButton( + onPressed: () => Process.run('pcmanfm', [widget.bundle.directory.path]), + icon: const Icon(Icons.open_in_new)), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + final segments = path.split(widget.bundle.directory.path); + segments[segments.length - 2] = 'booky_deleted'; + widget.bundle.directory.renameSync(path.joinAll(segments)); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Deleted'), + )); + widget.onDelete(); + }, + ), + ], ), ], ), From cf6378f1d1aeaf120b33f3b6845b7aee203277dd Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 13 May 2023 00:38:27 +0200 Subject: [PATCH 110/150] Camera: save ISBNs list to metadata.json --- lib/camera/camera.dart | 39 ++++++++++++++++++++++++++++----------- lib/common.dart | 3 ++- lib/common.g.dart | 4 +++- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 8162a79..877acf1 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -133,10 +133,7 @@ class _CameraWidgetState extends State with WidgetsBindingObserver child: Padding( padding: const EdgeInsets.all(8.0), child: Column( - children: _registeredBarcodes.entries - .where((entry) => entry.key.startsWith(isbnPrefix) && entry.value >= minBarcodeOccurrence) - .map((entry) { - final barcode = entry.key; + children: _getValidRegisteredBarcodes().map((barcode) { return Row( children: [ Expanded(child: Text(barcode, style: const TextStyle(fontWeight: FontWeight.bold))), @@ -179,6 +176,7 @@ class _CameraWidgetState extends State with WidgetsBindingObserver await controller!.stopImageStream(); setState(() => _customPaint = null); }, + isbns: _getValidRegisteredBarcodes(), ), ), ], @@ -440,18 +438,28 @@ class _CameraWidgetState extends State with WidgetsBindingObserver _logError(e.code, e.description); showInSnackBar('Error: ${e.code}\n${e.description}'); } + + List _getValidRegisteredBarcodes() { + return _registeredBarcodes.entries + .where((entry) => entry.key.startsWith(isbnPrefix) && entry.value >= minBarcodeOccurrence) + .map((e) => e.key) + .toList(); + } } class BottomWidget extends StatefulWidget { - const BottomWidget( - {required this.bundle, - required this.onSubmit, - required this.onBarcodeDetectStart, - required this.onBarcodeDetectStop}); + const BottomWidget({ + required this.bundle, + required this.onSubmit, + required this.onBarcodeDetectStart, + required this.onBarcodeDetectStop, + required this.isbns, + }); final Bundle bundle; final void Function() onSubmit; final void Function() onBarcodeDetectStart; final void Function() onBarcodeDetectStop; + final List isbns; @override State createState() => _BottomWidgetState(); @@ -516,23 +524,32 @@ class _BottomWidgetState extends State { icon: const Icon(Icons.keyboard_arrow_right), onPressed: () => showDialog( context: context, - builder: (BuildContext context) => MetadataWidget(directory: directory, onSubmit: onSubmit))); + builder: (BuildContext context) => + MetadataWidget(directory: directory, isbns: widget.isbns, onSubmit: onSubmit))); } class MetadataWidget extends StatefulWidget { const MetadataWidget({ required this.directory, required this.onSubmit, + required this.isbns, }); final Directory directory; final void Function() onSubmit; + final List isbns; @override State createState() => _MetadataWidgetState(); } class _MetadataWidgetState extends State { - var metadata = common.Metadata(); + late common.Metadata metadata; + + @override + void initState() { + super.initState(); + metadata = common.Metadata(isbns: widget.isbns); + } @override Widget build(BuildContext context) { diff --git a/lib/common.dart b/lib/common.dart index a5936ff..180689d 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -48,9 +48,10 @@ enum ItemState { @JsonSerializable() class Metadata { - Metadata({this.weightGrams, this.itemState}); + Metadata({this.weightGrams, this.itemState, this.isbns}); int? weightGrams; ItemState? itemState; + List? isbns; factory Metadata.fromJson(Map json) => _$MetadataFromJson(json); diff --git a/lib/common.g.dart b/lib/common.g.dart index 915ee66..c55601a 100644 --- a/lib/common.g.dart +++ b/lib/common.g.dart @@ -9,11 +9,13 @@ part of 'common.dart'; Metadata _$MetadataFromJson(Map json) => Metadata( weightGrams: json['weightGrams'] as int?, itemState: $enumDecodeNullable(_$ItemStateEnumMap, json['itemState']), - ); + )..isbns = + (json['isbns'] as List?)?.map((e) => e as String).toList(); Map _$MetadataToJson(Metadata instance) => { 'weightGrams': instance.weightGrams, 'itemState': _$ItemStateEnumMap[instance.itemState], + 'isbns': instance.isbns, }; const _$ItemStateEnumMap = { From 301772cb084f3703048994dd49e9858f9ac620a9 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 13 May 2023 00:50:59 +0200 Subject: [PATCH 111/150] BundleSelection: show a snackbar when compression is finished --- lib/enrichment/bundle_selection.dart | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 13c9609..172bb5d 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -102,17 +102,25 @@ class _BundleListState extends State { @override void initState() { super.initState(); - for (final bundle in widget.bundles) { - for (final image in bundle.images) { - final segments = path.split(image.path); - segments.insert(segments.length - 1, 'compressed'); - print('new path ${path.joinAll(segments)}'); - final targetPath = path.joinAll(segments); - if (!File(targetPath).existsSync()) { - _testCompressAndGetFile(image, targetPath); + Future(() async { + final futures = widget.bundles.map>((bundle) async { + for (final image in bundle.images) { + final segments = path.split(image.path); + segments.insert(segments.length - 1, 'compressed'); + print('new path ${path.joinAll(segments)}'); + final targetPath = path.joinAll(segments); + if (!File(targetPath).existsSync()) { + await _testCompressAndGetFile(image, targetPath); + } } + }); + await Future.wait(futures); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Compression finished'), + )); } - } + }); } Future _testCompressAndGetFile(File file, String targetPath) async { From 5ad259e65d5525a1b077cc0d649639daac287258 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 13 May 2023 10:46:49 +0200 Subject: [PATCH 112/150] BundleSelection: show compression progress and better use async --- lib/enrichment/bundle_selection.dart | 78 ++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 23 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 172bb5d..6e6b017 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -99,22 +99,31 @@ class BundleList extends StatefulWidget { } class _BundleListState extends State { + int? bundleNb; + int compressedBundleNb = 0; + @override void initState() { super.initState(); Future(() async { - final futures = widget.bundles.map>((bundle) async { - for (final image in bundle.images) { + if (mounted) { + setState(() => bundleNb = widget.bundles.length); + } + final bundleFutures = widget.bundles.map>((bundle) async { + final imagesFutures = bundle.images.map((image) async { final segments = path.split(image.path); segments.insert(segments.length - 1, 'compressed'); - print('new path ${path.joinAll(segments)}'); final targetPath = path.joinAll(segments); - if (!File(targetPath).existsSync()) { + if (!(await File(targetPath).exists())) { await _testCompressAndGetFile(image, targetPath); } + }); + await Future.wait(imagesFutures); + if (mounted) { + setState(() => compressedBundleNb += 1); } }); - await Future.wait(futures); + await Future.wait(bundleFutures); if (mounted) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Compression finished'), @@ -139,26 +148,49 @@ class _BundleListState extends State { return result; } + Widget _compressIndicator() { + if (compressedBundleNb == bundleNb) return const SizedBox.shrink(); + return bundleNb.ifIs( + nul: () => const LinearProgressIndicator(), + notnull: (bundleNb) => Row( + children: [ + const Text('Compressing '), + Expanded(child: LinearProgressIndicator(value: compressedBundleNb / bundleNb)), + Text('$compressedBundleNb / $bundleNb') + ], + )); + } + @override Widget build(BuildContext context) { - return GridView.extent( - maxCrossAxisExtent: 500, - childAspectRatio: 2, - children: widget.bundles - .map((bundle) => GestureDetector( - child: BundleWidget(bundle, onDelete: () { - setState(() {}); - }), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ISBNDecodingWidget( - step: ISBNDecodingStep(bundle: bundle), - ))); - }, - )) - .toList(), + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: _compressIndicator(), + ), + Expanded( + child: GridView.extent( + maxCrossAxisExtent: 500, + childAspectRatio: 2, + children: widget.bundles + .map((bundle) => GestureDetector( + child: BundleWidget(bundle, onDelete: () { + setState(() {}); + }), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ISBNDecodingWidget( + step: ISBNDecodingStep(bundle: bundle), + ))); + }, + )) + .toList(), + ), + ), + ], ); } } From 6dad90b0be4f7469c30ecd9d2e11471c8ad76fa1 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 14 May 2023 23:19:47 +0200 Subject: [PATCH 113/150] BundleSelection: Add refresh button to refresh list and compress images --- lib/enrichment/bundle_selection.dart | 144 ++++++++++++++------------- 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 6e6b017..5ce3faa 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -1,9 +1,11 @@ +import 'dart:async'; import 'dart:io'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:flutter_rust_bridge_template/camera/camera.dart'; +import 'package:kt_dart/kt.dart'; import 'package:path/path.dart' as path; import '../bundle.dart'; @@ -21,16 +23,32 @@ class BundleSelection extends StatefulWidget { } class _BundleSelectionState extends State { + int? bundleNb; + int compressedBundleNb = 0; + + @override + void initState() { + super.initState(); + _listBundles()?.let((bundles) => _compressedAllBundleImages(bundles)); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Bundle Section'), actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + setState(() {}); + _listBundles()?.let((bundles) => _compressedAllBundleImages(bundles)); + }, + ), IconButton( icon: const Icon(Icons.cloud_download), onPressed: () async { - _listBundles().forEach((bundle) async { + _listBundles()?.forEach((bundle) async { final isbnsList = await Future.wait(bundle.images.map((img) => common.extractIsbnsFromImage(img))); Set isbns = isbnsList.expand((i) => i).toSet(); await api.getMetadataFromIsbns( @@ -53,83 +71,70 @@ class _BundleSelectionState extends State { } Widget _getBody() { - try { - final bundles = _listBundles(); + final bundles = _listBundles(); - return BundleList(bundles); + if (bundles == null) { + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'Device not connected', + style: TextStyle(fontSize: 30), + ), + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + setState(() {}); + }, + ), + ], + ), + ); + } + return _bundleListWidget(bundles); + } + + static Iterable? _listBundles() { + try { + return common.bookyDir + .listSync() + .whereType() + .sorted((d1, d2) => d1.path.compareTo(d2.path)) + .map((d) => Bundle(d)); } catch (e) { if (e is PathNotFoundException || e is FileSystemException) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const Text( - 'Device not connected', - style: TextStyle(fontSize: 30), - ), - IconButton( - icon: const Icon(Icons.refresh), - onPressed: () { - setState(() {}); - }, - ), - ], - ), - ); + return null; } + print('Unhandled exception $e'); rethrow; } } - static Iterable _listBundles() { - return common.bookyDir - .listSync() - .whereType() - .sorted((d1, d2) => d1.path.compareTo(d2.path)) - .map((d) => Bundle(d)); - } -} - -class BundleList extends StatefulWidget { - const BundleList(this.bundles); - final Iterable bundles; - - @override - State createState() => _BundleListState(); -} - -class _BundleListState extends State { - int? bundleNb; - int compressedBundleNb = 0; - - @override - void initState() { - super.initState(); - Future(() async { - if (mounted) { - setState(() => bundleNb = widget.bundles.length); - } - final bundleFutures = widget.bundles.map>((bundle) async { - final imagesFutures = bundle.images.map((image) async { - final segments = path.split(image.path); - segments.insert(segments.length - 1, 'compressed'); - final targetPath = path.joinAll(segments); - if (!(await File(targetPath).exists())) { - await _testCompressAndGetFile(image, targetPath); - } - }); - await Future.wait(imagesFutures); - if (mounted) { - setState(() => compressedBundleNb += 1); + Future _compressedAllBundleImages(Iterable bundles) async { + if (mounted) { + setState(() => bundleNb = bundles.length); + } + final bundleFutures = bundles.map>((bundle) async { + final imagesFutures = bundle.images.map((image) async { + final segments = path.split(image.path); + segments.insert(segments.length - 1, 'compressed'); + final targetPath = path.joinAll(segments); + if (!(await File(targetPath).exists())) { + await _testCompressAndGetFile(image, targetPath); } }); - await Future.wait(bundleFutures); + await Future.wait(imagesFutures); if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text('Compression finished'), - )); + setState(() => compressedBundleNb += 1); } }); + await Future.wait(bundleFutures); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Compression finished'), + )); + } } Future _testCompressAndGetFile(File file, String targetPath) async { @@ -161,8 +166,7 @@ class _BundleListState extends State { )); } - @override - Widget build(BuildContext context) { + Widget _bundleListWidget(Iterable bundles) { return Column( children: [ Padding( @@ -173,11 +177,9 @@ class _BundleListState extends State { child: GridView.extent( maxCrossAxisExtent: 500, childAspectRatio: 2, - children: widget.bundles + children: bundles .map((bundle) => GestureDetector( - child: BundleWidget(bundle, onDelete: () { - setState(() {}); - }), + child: BundleWidget(bundle, onDelete: () => setState(() {})), onTap: () { Navigator.push( context, From de547affc0e9bbd7c86a471da515ff3937bbfc1d Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 14 May 2023 23:31:37 +0200 Subject: [PATCH 114/150] BundleSelection: Skip compression if not on Android, skip metadata fetching if file already exists --- lib/enrichment/bundle_selection.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 5ce3faa..643849c 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -49,6 +49,9 @@ class _BundleSelectionState extends State { icon: const Icon(Icons.cloud_download), onPressed: () async { _listBundles()?.forEach((bundle) async { + if (await bundle.autoMetadataFile.exists()) { + return; + } final isbnsList = await Future.wait(bundle.images.map((img) => common.extractIsbnsFromImage(img))); Set isbns = isbnsList.expand((i) => i).toSet(); await api.getMetadataFromIsbns( @@ -112,8 +115,12 @@ class _BundleSelectionState extends State { } Future _compressedAllBundleImages(Iterable bundles) async { + if (!Platform.isAndroid) return; if (mounted) { - setState(() => bundleNb = bundles.length); + setState(() { + bundleNb = bundles.length; + compressedBundleNb = 0; + }); } final bundleFutures = bundles.map>((bundle) async { final imagesFutures = bundle.images.map((image) async { @@ -154,7 +161,7 @@ class _BundleSelectionState extends State { } Widget _compressIndicator() { - if (compressedBundleNb == bundleNb) return const SizedBox.shrink(); + if (compressedBundleNb == bundleNb || !Platform.isAndroid) return const SizedBox.shrink(); return bundleNb.ifIs( nul: () => const LinearProgressIndicator(), notnull: (bundleNb) => Row( From 3482d07b80c25237544b8aa523b6e586a565df1a Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 15 May 2023 22:43:38 +0200 Subject: [PATCH 115/150] Use ISBN and MD from json file, not from detect binary or scrappers --- lib/bundle.dart | 16 ++ lib/common.dart | 14 - lib/enrichment/bundle_selection.dart | 10 +- lib/enrichment/enrichment.dart | 3 +- lib/enrichment/isbn_decoding.dart | 88 ------ lib/enrichment/metadata_collecting.dart | 345 +++++++++++------------- lib/helpers.dart | 12 + 7 files changed, 191 insertions(+), 297 deletions(-) delete mode 100644 lib/enrichment/isbn_decoding.dart diff --git a/lib/bundle.dart b/lib/bundle.dart index 894658a..fbbb553 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:collection/collection.dart'; import 'package:flutter_rust_bridge_template/common.dart'; +import 'package:flutter_rust_bridge_template/helpers.dart'; import 'package:path/path.dart' as path; import 'bridge_definitions.dart'; @@ -28,9 +29,11 @@ class Bundle { } extension ListProviderMetadataPairExt on List { + @Deprecated('Use MapProviderEnumBookMetaDataFromProviderExt') List getPrices() => map((e) => e.metadata?.marketPrice.toList()).whereNotNull().expand((i) => i).toList()..sort(); + @Deprecated('Use MapProviderEnumBookMetaDataFromProviderExt') BookMetaDataFromProvider mergeAllProvider() { return BookMetaDataFromProvider( title: map((e) => e.metadata?.title) @@ -42,6 +45,19 @@ extension ListProviderMetadataPairExt on List { } } +extension MapProviderEnumBookMetaDataFromProviderExt on Map { + List getPrices() => + values.map((e) => e?.marketPrice.toList()).whereNotNull().expand((i) => i).toList()..sort(); + BookMetaDataFromProvider mergeAllProvider() { + return BookMetaDataFromProvider( + title: entries.map((e) => e.value?.title).whereNotNull().biggest(), + authors: values.whereNotNull().map((md) => md.authors).biggest(), + blurb: values.map((e) => e?.blurb).whereNotNull().biggest(), + keywords: values.whereNotNull().map((e) => e.keywords).expand((e) => e).toList(), + marketPrice: Float32List.fromList(getPrices())); + } +} + extension _DirExt on Directory { Iterable listImages() => listSync().whereType().where((file) => path.extension(file.path) == '.jpg').sortByName(); diff --git a/lib/common.dart b/lib/common.dart index 180689d..39e71fa 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -4,20 +4,6 @@ import 'package:json_annotation/json_annotation.dart'; part 'common.g.dart'; -Future> extractIsbnsFromImage(File image) { - return Future(() async { - final decoderProcess = await Process.run( - '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', ['-in=' + image.path]); - if (decoderProcess.exitCode != 0) { - print('stdout is ${decoderProcess.stdout}'); - print('stderr is ${decoderProcess.stderr}'); - throw Exception('decoder status is ${decoderProcess.exitCode}'); - } - final s = decoderProcess.stdout as String; - return s.split('\n').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); - }); -} - extension IterableFutureMapEntryExt on Iterable>> { Future> toMap() async => Map.fromEntries(await Future.wait(this)); } diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 643849c..280df02 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -13,7 +13,7 @@ import '../common.dart' as common; import '../ffi.dart'; import '../helpers.dart'; import 'enrichment.dart'; -import 'isbn_decoding.dart'; +import 'metadata_collecting.dart'; class BundleSelection extends StatefulWidget { const BundleSelection(); @@ -52,8 +52,7 @@ class _BundleSelectionState extends State { if (await bundle.autoMetadataFile.exists()) { return; } - final isbnsList = await Future.wait(bundle.images.map((img) => common.extractIsbnsFromImage(img))); - Set isbns = isbnsList.expand((i) => i).toSet(); + Set isbns = bundle.metadata.isbns?.toSet() ?? {}; await api.getMetadataFromIsbns( isbns: isbns.toList(), path: bundle.autoMetadataFile.path, @@ -191,9 +190,8 @@ class _BundleSelectionState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => ISBNDecodingWidget( - step: ISBNDecodingStep(bundle: bundle), - ))); + builder: (context) => + BooksMetadataCollectingWidget(step: MetadataCollectingStep(bundle: bundle)))); }, )) .toList(), diff --git a/lib/enrichment/enrichment.dart b/lib/enrichment/enrichment.dart index c5fbc3f..475ace9 100644 --- a/lib/enrichment/enrichment.dart +++ b/lib/enrichment/enrichment.dart @@ -13,8 +13,7 @@ class ISBNDecodingStep implements BookyStep { class MetadataCollectingStep implements BookyStep { Bundle bundle; - Set isbns = {}; - MetadataCollectingStep({required this.bundle, required this.isbns}); + MetadataCollectingStep({required this.bundle}); } class AdEditingStep implements BookyStep { diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart deleted file mode 100644 index da83312..0000000 --- a/lib/enrichment/isbn_decoding.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:kt_dart/collection.dart'; - -import '../common.dart' as common; -import '../helpers.dart'; -import 'enrichment.dart'; -import 'metadata_collecting.dart'; - -class ISBNDecodingWidget extends StatefulWidget { - const ISBNDecodingWidget({required this.step}); - final ISBNDecodingStep step; - - @override - State createState() => _ISBNDecodingWidgetState(); -} - -class _ISBNDecodingWidgetState extends State { - KtMutableMap>> isbns = KtMutableMap.empty(); - - @override - void initState() { - super.initState(); - widget.step.bundle.images.forEach((image) { - isbns[image.path] = common.extractIsbnsFromImage(image); - }); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('ISBN decoding')), - body: SingleChildScrollView( - child: Column( - children: [ - Wrap( - children: widget.step.bundle.images - .map((imgPath) => Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - SizedBox(height: 600, child: ImageWidget(imgPath)), - FutureBuilder( - future: isbns[imgPath.path]!, - builder: (context, snap) { - if (snap.hasData == false) { - return const CircularProgressIndicator(); - } - return Column( - children: snap.data! - .map((isbn) => Text( - isbn, - style: TextStyle( - decoration: - isbn.startsWith('978') ? null : TextDecoration.lineThrough), - )) - .toList()); - }) - ], - ), - ), - )) - .toList(), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: FutureBuilder( - future: Future.wait(isbns.values.iter), - builder: (context, snap) { - return ElevatedButton( - onPressed: () { - final isbnSet = snap.data!.expand((e) => e).where((isbn) => isbn.startsWith('978')).toSet(); - print('isbnSet = $isbnSet'); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BooksMetadataCollectingWidget( - step: MetadataCollectingStep(bundle: widget.step.bundle, isbns: isbnSet)))); - }, - child: const Text('Validate ISBNs')); - }), - ) - ], - ), - ), - ); - } -} diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index d7f5436..fdd1cc4 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -1,8 +1,10 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_rust_bridge_template/bundle.dart'; import 'package:flutter_rust_bridge_template/enrichment/ad_editing.dart'; import 'package:flutter_rust_bridge_template/helpers.dart'; +import 'package:kt_dart/kt.dart'; import '../ffi.dart' if (dart.library.html) 'ffi_web.dart'; import 'enrichment.dart'; @@ -15,57 +17,82 @@ class BooksMetadataCollectingWidget extends StatefulWidget { State createState() => _BooksMetadataCollectingWidgetState(); } +class _Metadata { + _Metadata({required this.providerMetadatas, required this.bookControllerSet}); + + Map providerMetadatas; + final _BookControllerSet bookControllerSet; +} + class _BooksMetadataCollectingWidgetState extends State { - late Map controllers; + Map? controllers; @override void initState() { super.initState(); - controllers = Map.fromEntries(widget.step.isbns.map((isbn) => MapEntry(isbn, _BookControllerSet()))); + + Future(() async { + final autoMd = await api.getAutoMetadataFromBundle(path: widget.step.bundle.autoMetadataFile.path); + if (mounted) { + setState(() { + controllers = Map.fromEntries(autoMd.map((entry) => MapEntry( + entry.isbn, + _Metadata( + providerMetadatas: + entry.metadatas.map((md) => MapEntry(md.provider, md.metadata)).let((e) => Map.fromEntries(e)), + bookControllerSet: _BookControllerSet())))); + }); + } + }); } @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('Metadata Collecting'), - ), - body: SingleChildScrollView( - child: Column( - children: [ - ...controllers.entries - .map((entry) => _BookMetadataCollectingWidget( - isbn: entry.key, - controllers: entry.value, - )) - .toList(), - Padding( - padding: const EdgeInsets.all(8.0), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AdEditingWidget( - step: AdEditingStep( - bundle: widget.step.bundle, - metadata: controllers.map((key, value) => MapEntry( - key, - BookMetaDataManual( - title: value.titleTextFieldController.text, - authors: _stringToAuthors(value.authorsTextFieldController.text), - blurb: value.blurbTextFieldController.text, - keywords: _stringToKeywords(value.keywordsTextFieldController.text), - priceCent: - double.parse(value.priceTextFieldController.text).multiply(100).round(), - ))))))); - }, - child: const Text('Validate Metadatas')), - ) - ], + appBar: AppBar( + title: const Text('Metadata Collecting'), ), - ), - ); + body: controllers.ifIs( + nul: () => const CircularProgressIndicator(), + notnull: (controllers) => SingleChildScrollView( + child: Column( + children: [ + ...controllers.entries + .map((entry) => _BookMetadataCollectingWidget( + isbn: entry.key, + metadatas: entry.value, + )) + .toList(), + Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AdEditingWidget( + step: AdEditingStep( + bundle: widget.step.bundle, + metadata: controllers.map((key, value) => MapEntry( + key, + BookMetaDataManual( + title: value.bookControllerSet.titleTextFieldController.text, + authors: _stringToAuthors( + value.bookControllerSet.authorsTextFieldController.text), + blurb: value.bookControllerSet.blurbTextFieldController.text, + keywords: _stringToKeywords( + value.bookControllerSet.keywordsTextFieldController.text), + priceCent: double.parse( + value.bookControllerSet.priceTextFieldController.text) + .multiply(100) + .round(), + ))))))); + }, + child: const Text('Validate Metadatas')), + ) + ], + ), + ))); } } @@ -84,76 +111,42 @@ String _authorsToString(List authors) => authors.map((a) => a.toText()). List _stringToAuthors(String s) => s.split('\n').map((line) => Author(firstName: '', lastName: line)).toList(); class _BookMetadataCollectingWidget extends StatefulWidget { - const _BookMetadataCollectingWidget({required this.isbn, required this.controllers}); + const _BookMetadataCollectingWidget({required this.isbn, required this.metadatas}); final String isbn; - final _BookControllerSet controllers; + final _Metadata metadatas; @override State<_BookMetadataCollectingWidget> createState() => _BookMetadataCollectingWidgetState(); } class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWidget> { - late Map> metadata; - @override void initState() { super.initState(); - final isbn = widget.isbn; - metadata = Map.fromEntries(ProviderEnum.values.map((provider) { - final md = api.getMetadataFromProvider(provider: provider, isbn: isbn); - md.then((value) { - if (value != null) { - _replaceIfBetterString(value.title, widget.controllers.titleTextFieldController.text, () { - _updateManualTitle(value.title!); - }); - - final joinedAuthors = _authorsToString(value.authors); - _replaceIfBetterString(joinedAuthors, widget.controllers.authorsTextFieldController.text, () { - _updateManualAuthors(joinedAuthors); - }); - _replaceIfBetterString(value.blurb, widget.controllers.blurbTextFieldController.text, () { - _updateManualBlurb(value.blurb!); - }); - final joinedKeywords = _keywordsToString(value.keywords); - _replaceIfBetterString(joinedAuthors, widget.controllers.keywordsTextFieldController.text, () { - _updateManualKeywords(joinedKeywords); - }); - - if (value.marketPrice.isNotEmpty) { - // TODO: If multiple provider give a marketPrice, we should update the manual price by taking into account all the marketPrice received yet, not just the most recent - widget.controllers.priceTextFieldController.text = value.marketPrice.average.round().toString(); - } - } - }); - return MapEntry(provider, md); - })); - } - - void _replaceIfBetterString(String? providerStr, String manualStr, void Function() onReplace) { - if (providerStr == null || manualStr.length > providerStr.length) return; - onReplace(); - } - - void _updateManualTitle(String newTitle) { - setState(() => widget.controllers.titleTextFieldController.text = newTitle); - } - - void _updateManualAuthors(String newAuthor) { - setState(() => widget.controllers.authorsTextFieldController.text = newAuthor); + final manualMD = widget.metadatas.providerMetadatas.mergeAllProvider(); + final controllers = widget.metadatas.bookControllerSet; + controllers.titleTextFieldController.text = manualMD.title ?? ''; + controllers.authorsTextFieldController.text = _authorsToString(manualMD.authors); + controllers.blurbTextFieldController.text = manualMD.blurb ?? ''; + controllers.keywordsTextFieldController.text = _keywordsToString(manualMD.keywords); + controllers.priceTextFieldController.text = + manualMD.marketPrice.isEmpty ? '' : manualMD.marketPrice.average.round().toString(); } void _updateManualBlurb(String newBlurb) { - setState(() => widget.controllers.blurbTextFieldController.text = newBlurb); + setState(() => widget.metadatas.bookControllerSet.blurbTextFieldController.text = newBlurb); } void _updateManualKeywords(String newKeywords) { - setState(() => widget.controllers.keywordsTextFieldController.text = newKeywords); + setState(() => widget.metadatas.bookControllerSet.keywordsTextFieldController.text = newKeywords); } @override Widget build(BuildContext context) { const columnHeaderStyle = TextStyle(fontSize: 20, fontWeight: FontWeight.bold); + const providers = ProviderEnum.values; + final iter = providers.map((provider) => widget.metadatas.providerMetadatas[provider]); return Card( margin: const EdgeInsets.all(10), child: Padding( @@ -166,117 +159,95 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi TableRow( children: [ const Text('Manual', style: columnHeaderStyle), - const Text('Babelio', style: columnHeaderStyle), - const Text('GoogleBooks', style: columnHeaderStyle), - const Text('BooksPrice', style: columnHeaderStyle), + ...providers.map((p) => Text(p.name, style: columnHeaderStyle)) ].map((e) => Center(child: e)).toList()), TableRow(children: [ - FutureWidget( - future: metadata.entries.first.value, - builder: (data) => TextFormField( - controller: widget.controllers.titleTextFieldController, - decoration: const InputDecoration( - icon: Icon(Icons.title), - labelText: 'Book title', - ), - )), - ...metadata.entries.map((e) => FutureWidget( - future: e.value, builder: (data) => data == null ? _noneText : SelectableText(data.title ?? ''))), + TextFormField( + controller: widget.metadatas.bookControllerSet.titleTextFieldController, + decoration: const InputDecoration( + icon: Icon(Icons.title), + labelText: 'Book title', + ), + ), + ...iter.map((data) => data == null ? _noneText : SelectableText(data.title ?? '')), ]), TableRow(children: [ - FutureWidget( - future: metadata.entries.first.value, - builder: (data) => TextFormField( - controller: widget.controllers.authorsTextFieldController, - maxLines: null, - decoration: const InputDecoration( - icon: Icon(Icons.person), - labelText: 'Authors', - ), + TextFormField( + controller: widget.metadatas.bookControllerSet.authorsTextFieldController, + maxLines: null, + decoration: const InputDecoration( + icon: Icon(Icons.person), + labelText: 'Authors', ), ), - ...metadata.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final authors = data?.authors; - if (authors == null || authors.isEmpty) { - return _noneText; - } - return SelectableText(_authorsToString(authors)); - })), + ...iter.map((data) { + final authors = data?.authors; + if (authors == null || authors.isEmpty) { + return _noneText; + } + return SelectableText(_authorsToString(authors)); + }), ]), TableRow(children: [ - FutureWidget( - future: metadata.entries.first.value, - builder: (data) => TextFormField( - controller: widget.controllers.blurbTextFieldController, - maxLines: null, - decoration: const InputDecoration( - icon: Icon(Icons.description), - labelText: 'Book blurb', - ), - )), - ...metadata.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final blurb = data?.blurb; - if (blurb == null) { - return _noneText; - } - return _SelectableTextAndUse( - blurb, - onUse: (b) => _updateManualBlurb(b), - ); - })), + TextFormField( + controller: widget.metadatas.bookControllerSet.blurbTextFieldController, + maxLines: null, + decoration: const InputDecoration( + icon: Icon(Icons.description), + labelText: 'Book blurb', + ), + ), + ...iter.map((data) { + final blurb = data?.blurb; + if (blurb == null) { + return _noneText; + } + return _SelectableTextAndUse( + blurb, + onUse: (b) => _updateManualBlurb(b), + ); + }), ]), TableRow(children: [ - FutureWidget( - future: metadata.entries.first.value, - builder: (data) => TextFormField( - controller: widget.controllers.keywordsTextFieldController, - maxLines: null, - decoration: const InputDecoration( - icon: Icon(Icons.manage_search), - labelText: 'Keywords', - ), - )), - ...metadata.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final keywords = data?.keywords; - if (keywords?.isEmpty ?? true) { - return _noneText; - } - return _SelectableTextAndUse( - _keywordsToString(keywords!), - onUse: (b) => _updateManualBlurb(b), - ); - })), + TextFormField( + controller: widget.metadatas.bookControllerSet.keywordsTextFieldController, + maxLines: null, + decoration: const InputDecoration( + icon: Icon(Icons.manage_search), + labelText: 'Keywords', + ), + ), + ...iter.map((data) { + final keywords = data?.keywords; + if (keywords?.isEmpty ?? true) { + return _noneText; + } + return _SelectableTextAndUse( + _keywordsToString(keywords!), + onUse: (kw) => _updateManualKeywords(kw), + ); + }), ]), TableRow(children: [ - FutureWidget( - future: metadata.entries.first.value, - builder: (data) => TextFormField( - controller: widget.controllers.priceTextFieldController, - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[0-9]+[,.]{0,1}[0-9]*')), - ], - decoration: const InputDecoration( - icon: Icon(Icons.euro), - labelText: 'Price', - ), - )), - ...metadata.entries.map((e) => FutureWidget( - future: e.value, - builder: (data) { - final marketPrices = data?.marketPrice.toList()?..sort(); - if (marketPrices == null || marketPrices.isEmpty) { - return _noneText; - } - return SelectableText( - '${marketPrices.first.toStringAsFixed(2)} - ${marketPrices.last.toStringAsFixed(2)}', - ); - })), + TextFormField( + controller: widget.metadatas.bookControllerSet.priceTextFieldController, + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9]+[,.]{0,1}[0-9]*')), + ], + decoration: const InputDecoration( + icon: Icon(Icons.euro), + labelText: 'Price', + ), + ), + ...iter.map((data) { + final marketPrices = data?.marketPrice.toList()?..sort(); + if (marketPrices == null || marketPrices.isEmpty) { + return _noneText; + } + return SelectableText( + '${marketPrices.first.toStringAsFixed(2)} - ${marketPrices.last.toStringAsFixed(2)}', + ); + }), ]), ], ), diff --git a/lib/helpers.dart b/lib/helpers.dart index 2843a6c..97272ac 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -89,6 +89,18 @@ extension IfNullExt on T? { } } +extension ListExt on List { + List? nullIfEmpty() => isEmpty ? null : this; +} + +extension IterableListExt on Iterable> { + List biggest() => fold([], (biggest, element) => element.length > biggest.length ? element : biggest); +} + +extension IterableStringExt on Iterable { + String? biggest() => fold(null, (biggest, element) => element.length > (biggest?.length ?? 0) ? element : biggest); +} + class BookMetaDataManual { String? title; List authors; From 772dd073bdb8ed4c297f9a60a4ebf16c4bca66b8 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 15 May 2023 22:48:22 +0200 Subject: [PATCH 116/150] Camera: Clear _registeredBarcodes when submitting --- lib/camera/camera.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index 877acf1..f4f3e62 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -168,6 +168,7 @@ class _CameraWidgetState extends State with WidgetsBindingObserver onSubmit: () { setState(() { _generateNewFolderPath(); + _registeredBarcodes.clear(); }); Navigator.pop(context); }, From e37f5c859404a6b808d2c9cd1607b0f48af1541c Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 15 May 2023 23:20:12 +0200 Subject: [PATCH 117/150] TODO: Update --- TODO.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index 99dd7a1..d5beb24 100644 --- a/TODO.md +++ b/TODO.md @@ -2,14 +2,14 @@ * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty * [ ] AdEditing: Disable 'Mark as published' if the title is empty -* [ ] BundleSelection: Automatically update bundle list when a bundle is removed or added by the app -* [ ] BundleSelection: Allow manual refresh if the bundle list has been modified by external device -* [ ] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) -* [ ] Camera: Grab the ISBN in real-time with ML Kit (add a minimum repetition of about 20 to avoid false ISBN detection) * [ ] Search with Selenium in headless mode # DONE +* [x] BundleSelection: Automatically update bundle list when a bundle is removed or added by the app +* [x] BundleSelection: Allow manual refresh if the bundle list has been modified by external device +* [x] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) +* [x] Camera: Grab the ISBN in real-time with ML Kit (add a minimum repetition of about 20 to avoid false ISBN detection) * [x] MetadataCollecting: Price auto fill * [x] MetadataCollecting: Add back the keywords * [x] MetadataCollecting: Create multiple textFieldController when multiple ISBN From f4308221fd1bab6e155d5b67702e963e957491ab Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 15 May 2023 23:20:40 +0200 Subject: [PATCH 118/150] AdEditing: Fix bug where cursor goes back to the beginning on edit --- lib/enrichment/ad_editing.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index f9ca7e5..e004584 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -99,7 +99,6 @@ class _AdEditingWidgetState extends State { children: [ CopiableTextField(TextFormField( controller: TextEditingController(text: ad.title), - onChanged: (newText) => setState(() => ad.title = newText), decoration: const InputDecoration( icon: Icon(Icons.title), labelText: 'Ad title', @@ -118,7 +117,6 @@ class _AdEditingWidgetState extends State { controller: TextEditingController(text: ad.description), maxLines: null, scrollPhysics: const NeverScrollableScrollPhysics(), - onChanged: (newText) => setState(() => ad.description = newText), decoration: const InputDecoration( icon: Icon(Icons.text_snippet), labelText: 'Ad description', @@ -126,8 +124,6 @@ class _AdEditingWidgetState extends State { )), CopiableTextField(TextFormField( controller: TextEditingController(text: ad.priceCent.divide(100).toString()), - onChanged: (newText) => - setState(() => ad.priceCent = double.tryParse(newText)! /*?*/ .multiply(100).round()), decoration: const InputDecoration( icon: Icon(Icons.euro), labelText: 'Price', From fcc97978302bd1a51e112cb68b6535b5bce7a8e0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 16 May 2023 22:58:23 +0200 Subject: [PATCH 119/150] CachedClient: create cache sub-directory if needed --- native/src/cached_client.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/native/src/cached_client.rs b/native/src/cached_client.rs index 41a5b92..901ae82 100644 --- a/native/src/cached_client.rs +++ b/native/src/cached_client.rs @@ -1,3 +1,5 @@ +use std::path::Path; + pub trait Client { fn make_request( &self, @@ -49,6 +51,8 @@ impl Client for CachedClient { Err(_) => { println!("No file name {} in the cache", &cache_file_path); let resp = _make_request(&self.http_client); + let dir = Path::new(&cache_file_path).parent().unwrap(); + std::fs::create_dir_all(dir).unwrap(); let write_res = std::fs::write(&cache_file_path, &resp); write_res.expect(format!("Can't write to file {}", cache_file_path).as_str()); resp From d8c691a36bff915539b89d6b22ebcc400f4e2169 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 22 May 2023 18:50:40 +0200 Subject: [PATCH 120/150] Rust: Add AbeBooks provider for prices --- TODO.md | 2 + lib/bridge_definitions.dart | 1 + native/src/abebooks.rs | 20 + native/src/abebooks/parser.rs | 42 + native/src/abebooks/request.rs | 16 + .../test/search_isbn_9782703304180.html | 2371 +++++++++++++++++ native/src/api.rs | 4 +- native/src/bridge_generated.rs | 2 + native/src/lib.rs | 1 + 9 files changed, 2458 insertions(+), 1 deletion(-) create mode 100644 native/src/abebooks.rs create mode 100644 native/src/abebooks/parser.rs create mode 100644 native/src/abebooks/request.rs create mode 100644 native/src/abebooks/test/search_isbn_9782703304180.html diff --git a/TODO.md b/TODO.md index d5beb24..11f739b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,7 @@ # TODO +* [ ] BUG: BundleList fail to refresh after: autoMetadata download +* [ ] BUG: BundleList show autoMetadata from other bundle when some bundle are deleted * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty * [ ] AdEditing: Disable 'Mark as published' if the title is empty * [ ] Search with Selenium in headless mode diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index e886412..6888615 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -93,6 +93,7 @@ enum ProviderEnum { Babelio, GoogleBooks, BooksPrice, + AbeBooks, } class ProviderMetadataPair { diff --git a/native/src/abebooks.rs b/native/src/abebooks.rs new file mode 100644 index 0000000..868d642 --- /dev/null +++ b/native/src/abebooks.rs @@ -0,0 +1,20 @@ +use crate::{cached_client::CachedClient, common}; +mod parser; +mod request; + +pub struct AbeBooks; + +impl common::Provider for AbeBooks { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let client = reqwest::blocking::Client::builder().build().unwrap(); + let cached_client = CachedClient { + http_client: client, + }; + let isbn_search_result = request::isbn_search(&cached_client, isbn); + let prices = parser::extract_prices(&isbn_search_result); + Some(common::BookMetaDataFromProvider { + market_price: prices, + ..Default::default() + }) + } +} diff --git a/native/src/abebooks/parser.rs b/native/src/abebooks/parser.rs new file mode 100644 index 0000000..9ef1f4f --- /dev/null +++ b/native/src/abebooks/parser.rs @@ -0,0 +1,42 @@ +pub fn extract_prices(isbn_search_result: &str) -> Vec { + let doc = scraper::Html::parse_document(isbn_search_result); + + let prices = (1..) + .map_while(|index| { + let selector = + scraper::Selector::parse(&format!("#add-to-basket-link-{}", index)).unwrap(); + let mut res = doc.select(&selector); + + match res.next() { + None => None, + Some(e) => { + let raw_book_price: f32 = + e.value().attr("data-csa-c-cost").unwrap().parse().unwrap(); + let shipping_cost: f32 = e + .value() + .attr("data-csa-c-shipping-cost") + .unwrap() + .parse() + .unwrap(); + + Some(raw_book_price + shipping_cost) + } + } + }) + .collect(); + + return prices; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_extract_prices() { + let html = + std::fs::read_to_string("src/abebooks/test/search_isbn_9782703304180.html").unwrap(); + let prices = extract_prices(&html); + assert_eq!(prices, vec![15.34, 17.06, 17.59, 27.7, 42.0, 44.9]); + } +} diff --git a/native/src/abebooks/request.rs b/native/src/abebooks/request.rs new file mode 100644 index 0000000..cd4dd88 --- /dev/null +++ b/native/src/abebooks/request.rs @@ -0,0 +1,16 @@ +use crate::cached_client::{CachedClient, Client}; + +pub fn isbn_search(client: &CachedClient, isbn: &str) -> String { + let raw_search_results = client.make_request( + format!("abebooks/get_book_url_{}.html", isbn).as_str(), + &|http_client| { + http_client + .get(format!("http://www.abebooks.fr/servlet/SearchResults?bx=off&sts=t&ds=30&bi=0&isbn={}&sortby=2", &isbn)) + .send() + .unwrap() + .text() + .unwrap() + }, + ); + raw_search_results +} diff --git a/native/src/abebooks/test/search_isbn_9782703304180.html b/native/src/abebooks/test/search_isbn_9782703304180.html new file mode 100644 index 0000000..4063b64 --- /dev/null +++ b/native/src/abebooks/test/search_isbn_9782703304180.html @@ -0,0 +1,2371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 9782703304180 - Tantrisme : Doctrine, pratique, art, rituel de Feuga, Pierre - AbeBooks + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+ + + + + + +
+ +
+
+ + + + +
+
+
+
+
+
+ + + + +
+ + Search preferences + +
+
+ +
+ +
+

Type d'article +

+
    + + + +
  • + Tous les types d'articles +
  • + + + +
  • + Livres (6) +
  • + + + +
  • + Magazines & Périodiques +
  • + + + +
  • + Bandes dessinées +
  • + + + +
  • + Partitions de musique +
  • + + + +
  • + Art, Affiches et Gravures +
  • + + + +
  • + Photographies +
  • + + + +
  • + Cartes +
  • + + + +
  • + Manuscrits &
    Papiers anciens
    +
  • +
+ + + +

Etat

+
    + + +
  • + Tous +
  • + + +
  • + Neuf +
  • + + +
  • + Ancien ou d'occasion +
  • +
+ +

Reliure

+
    + + +
  • + Toutes +
  • + + +
  • + Couverture rigide +
  • + + +
  • + Couverture souple +
  • +
+ +

Particularités

+ + + +

Pays

+
    +
  • +
    + + + + + +
    + +
    + +
    +
  • +
  • +
    + + + + + +
    + +
    + +
    +
  • +
+ + +

Evaluation du vendeur

+
    + + + + +
  • + Tous les vendeurs +
  • + + + + +
  • + et plus +
  • + + + + +
  • + et plus +
  • + + + + +
  • + et plus +
  • + + + + +
  • +   (5) +
  • +
+
+ + +
+
+
+ + + + +
+ +
+ +
+
+
+
+ + + + + + +
+
+ + + +
+ + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/native/src/api.rs b/native/src/api.rs index 0c9e826..55a4557 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -7,7 +7,7 @@ use crate::common; use crate::common::Ad; use crate::common::{LbcCredential, Provider}; use crate::publisher::Publisher; -use crate::{babelio, booksprice, google_books, leboncoin}; +use crate::{abebooks, babelio, booksprice, google_books, leboncoin}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; @@ -18,6 +18,7 @@ pub enum ProviderEnum { Babelio, GoogleBooks, BooksPrice, + AbeBooks, } pub fn get_metadata_from_isbns(isbns: Vec, path: String) -> Result<(), anyhow::Error> { @@ -111,6 +112,7 @@ pub fn get_metadata_from_provider( } .get_book_metadata_from_isbn(&isbn), ProviderEnum::BooksPrice => booksprice::BooksPrice {}.get_book_metadata_from_isbn(&isbn), + ProviderEnum::AbeBooks => abebooks::AbeBooks {}.get_book_metadata_from_isbn(&isbn), } } diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 3834c41..c59db3d 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -131,6 +131,7 @@ impl Wire2Api for i32 { 0 => ProviderEnum::Babelio, 1 => ProviderEnum::GoogleBooks, 2 => ProviderEnum::BooksPrice, + 3 => ProviderEnum::AbeBooks, _ => unreachable!("Invalid variant for ProviderEnum: {}", self), } } @@ -177,6 +178,7 @@ impl support::IntoDart for ProviderEnum { Self::Babelio => 0, Self::GoogleBooks => 1, Self::BooksPrice => 2, + Self::AbeBooks => 3, } .into_dart() } diff --git a/native/src/lib.rs b/native/src/lib.rs index 691c70e..4e749b3 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -13,3 +13,4 @@ mod jwt_decoder; mod leboncoin; mod publisher; mod booksprice; +mod abebooks; From 2c52228b85aed19c364529a87aa864bed0c3ffb4 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 22 May 2023 19:00:50 +0200 Subject: [PATCH 121/150] MetadataCollecting: Prefill with minimum price instead of average --- lib/enrichment/metadata_collecting.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index fdd1cc4..03d658f 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -130,8 +130,14 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi controllers.authorsTextFieldController.text = _authorsToString(manualMD.authors); controllers.blurbTextFieldController.text = manualMD.blurb ?? ''; controllers.keywordsTextFieldController.text = _keywordsToString(manualMD.keywords); - controllers.priceTextFieldController.text = - manualMD.marketPrice.isEmpty ? '' : manualMD.marketPrice.average.round().toString(); + if (manualMD.marketPrice.isEmpty) { + controllers.priceTextFieldController.text = ''; + } else { + final minMarketPrice = manualMD.marketPrice.min; + const minimumSellingPrice = 1.0; + controllers.priceTextFieldController.text = + minMarketPrice < minimumSellingPrice ? minimumSellingPrice.toString() : minMarketPrice.round().toString(); + } } void _updateManualBlurb(String newBlurb) { From be75ec38aa630ff39a6b3206f66b6311b2d222e0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 22 May 2023 23:50:06 +0200 Subject: [PATCH 122/150] Rust: Add LesLibraires providers --- lib/bridge_definitions.dart | 1 + native/src/api.rs | 4 +- native/src/bridge_generated.rs | 2 + native/src/leslibraires.rs | 16 + native/src/leslibraires/parser.rs | 66 ++ native/src/leslibraires/request.rs | 37 + .../src/leslibraires/test/9782286056636.html | 1034 +++++++++++++++++ native/src/lib.rs | 1 + 8 files changed, 1160 insertions(+), 1 deletion(-) create mode 100644 native/src/leslibraires.rs create mode 100644 native/src/leslibraires/parser.rs create mode 100644 native/src/leslibraires/request.rs create mode 100644 native/src/leslibraires/test/9782286056636.html diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 6888615..558a563 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -94,6 +94,7 @@ enum ProviderEnum { GoogleBooks, BooksPrice, AbeBooks, + LesLibraires, } class ProviderMetadataPair { diff --git a/native/src/api.rs b/native/src/api.rs index 55a4557..4a243e5 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -7,7 +7,7 @@ use crate::common; use crate::common::Ad; use crate::common::{LbcCredential, Provider}; use crate::publisher::Publisher; -use crate::{abebooks, babelio, booksprice, google_books, leboncoin}; +use crate::{abebooks, babelio, booksprice, google_books, leboncoin, leslibraires}; use itertools::Itertools; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; @@ -19,6 +19,7 @@ pub enum ProviderEnum { GoogleBooks, BooksPrice, AbeBooks, + LesLibraires, } pub fn get_metadata_from_isbns(isbns: Vec, path: String) -> Result<(), anyhow::Error> { @@ -113,6 +114,7 @@ pub fn get_metadata_from_provider( .get_book_metadata_from_isbn(&isbn), ProviderEnum::BooksPrice => booksprice::BooksPrice {}.get_book_metadata_from_isbn(&isbn), ProviderEnum::AbeBooks => abebooks::AbeBooks {}.get_book_metadata_from_isbn(&isbn), + ProviderEnum::LesLibraires => leslibraires::LesLibraires {}.get_book_metadata_from_isbn(&isbn), } } diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index c59db3d..c94459b 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -132,6 +132,7 @@ impl Wire2Api for i32 { 1 => ProviderEnum::GoogleBooks, 2 => ProviderEnum::BooksPrice, 3 => ProviderEnum::AbeBooks, + 4 => ProviderEnum::LesLibraires, _ => unreachable!("Invalid variant for ProviderEnum: {}", self), } } @@ -179,6 +180,7 @@ impl support::IntoDart for ProviderEnum { Self::GoogleBooks => 1, Self::BooksPrice => 2, Self::AbeBooks => 3, + Self::LesLibraires => 4, } .into_dart() } diff --git a/native/src/leslibraires.rs b/native/src/leslibraires.rs new file mode 100644 index 0000000..aec547b --- /dev/null +++ b/native/src/leslibraires.rs @@ -0,0 +1,16 @@ +use crate::{cached_client::CachedClient, common}; +mod parser; +mod request; + +pub struct LesLibraires; + +impl common::Provider for LesLibraires { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let client = reqwest::blocking::Client::builder().build().unwrap(); + let cached_client = CachedClient { + http_client: client, + }; + let isbn_search_result = request::isbn_search(&cached_client, isbn); + parser::extract_metadata(&isbn_search_result) + } +} diff --git a/native/src/leslibraires/parser.rs b/native/src/leslibraires/parser.rs new file mode 100644 index 0000000..452bcdb --- /dev/null +++ b/native/src/leslibraires/parser.rs @@ -0,0 +1,66 @@ +use itertools::Itertools; + +use crate::common::{self, Author}; + +pub fn extract_metadata(isbn_search_result: &str) -> Option { + let doc = scraper::Html::parse_document(isbn_search_result); + + let title_selector = scraper::Selector::parse(".main-infos [itemprop=\"name\"]").unwrap(); + let title = doc + .select(&title_selector) + .exactly_one() + .unwrap() + .first_child() + .unwrap() + .value() + .as_text() + .unwrap() + .to_string(); + + let author_selector = scraper::Selector::parse(".main-infos [itemprop=\"author\"]").unwrap(); + let author_last_name = doc + .select(&author_selector) + .exactly_one() + .unwrap() + .first_child() + .unwrap() + .value() + .as_text() + .unwrap() + .to_string(); + + Some(common::BookMetaDataFromProvider { + title: Some(title), + authors: vec![Author { + first_name: "".to_owned(), + last_name: author_last_name, + }], + ..Default::default() + }) +} + +#[cfg(test)] +mod tests { + use std::vec; + + use crate::common::Author; + + use super::*; + + #[test] + fn test_extract_prices() { + let html = std::fs::read_to_string("src/leslibraires/test/9782286056636.html").unwrap(); + let md = extract_metadata(&html); + assert_eq!( + md, + Some(common::BookMetaDataFromProvider { + title: Some("Les autres et moi".to_owned()), + authors: vec![Author { + first_name: "".to_owned(), + last_name: "Isabelle Filliozat".to_owned() + }], + ..Default::default() + }) + ); + } +} diff --git a/native/src/leslibraires/request.rs b/native/src/leslibraires/request.rs new file mode 100644 index 0000000..1bd6e23 --- /dev/null +++ b/native/src/leslibraires/request.rs @@ -0,0 +1,37 @@ +use crate::cached_client::{CachedClient, Client}; + +pub fn isbn_search(client: &CachedClient, isbn: &str) -> String { + let redirection = client.make_request( + format!("leslibraires/isbn_{}.html", isbn).as_str(), + &|http_client| { + http_client + .get(format!( + "https://www.leslibraires.fr/recherche/?q={}", + &isbn + )) + .send() + .unwrap() + .headers() + .get("location") + .map(|header_value| header_value.to_str().unwrap()) + .unwrap() + .to_owned() + }, + ); + let result = client.make_request( + format!("leslibraires/get_book_url_{}.html", redirection).as_str(), + &|http_client| { + http_client + .get(format!( + "https://www.leslibraires.fr/{}", + &redirection + )) + .send() + .unwrap() + .text() + .unwrap() + }, + ); + result +} + diff --git a/native/src/leslibraires/test/9782286056636.html b/native/src/leslibraires/test/9782286056636.html new file mode 100644 index 0000000..12cf593 --- /dev/null +++ b/native/src/leslibraires/test/9782286056636.html @@ -0,0 +1,1034 @@ + + + + + Les autres et moi, Comment développer son intelligence sociale - Isabelle Filliozat - Leslibraires.fr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+
+ + + + + + + +
+
+ + + +
+
+ + +
+ + +
+ +
+ +
Les autres et moi, Comment développer son intelligence sociale
+
+ + + +
+
+ + +
+ + + +
+
+
    +
  • +
  • +
  • +
  • +
  • +
+ + +
+
+ + + +
+
+ + + +
EAN13
+
9782286056636
+ +
ISBN
+
978-2-286-05663-6
+ + + + +
Éditeur
+
Le Grand Livre du Mois GF
+ + + + + + + + + +
Nombre de pages
+
237
+ + + + + + + +
Langue
+
français
+ + + + + + + +
+
+
+ + +
+ + +
+ +

+ + Les autres et moi +

+ +

Comment développer son intelligence sociale

+ + + + + +

Autres contributions de

+ + + +

Le Grand Livre du Mois GF

+ + +
+ + + + +
+ + + + + +
+ + +
Trouvez les offres des librairies les plus proches :
+ +
+
+ + + + +
+ + ou + +
+ entrez le nom de votre ville +
+ +
+ +
+
+
+ + + +
+ + + + +
+ +

Offres

+ +
    + +
  • + + + + + + +
    +
    + + +
    + Vendu par Livre au trésor + +
    + + + + + +
    + +
    État de l'exemplaire
    BROCHE BON.. TRANCHE FROTTER. CD INCLUS. . PHOTO SUR DEMANDE Les Autres et Moi - Comment développer son intelligence sociale. Isabelle Filliozat. Le Grand Livre du Mois. Broché . 2009
    + +
    + + + + + + +
    + + + + + + + 12.00 + + + (Occasion) + + + + +
    + +
    +
    + + + +
    + + + + +
    + + +
    +
    + + +
  • + + +
+ + + + + +
+ + + + + + + + +
+ + + + + + + + + + +
+
+ +
+ +
+ + S'identifier pour envoyer des commentaires. + +
+
+ + + + +
+
+
+
+
+
+
+ + + + +
+
+ + + + + + + + + +
+
+ + +

Autres contributions de...

+ + + +
+ +
+ + Plus d'informations sur Isabelle Filliozat +
+ +
+ +
+
+ + + + + +
+
+
+
+
+
+ + + +
+
+ + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/native/src/lib.rs b/native/src/lib.rs index 4e749b3..41fdf89 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -14,3 +14,4 @@ mod leboncoin; mod publisher; mod booksprice; mod abebooks; +mod leslibraires; From a63ee1f83cbd94e516a97cad1bf5243b1c13f98b Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 23 May 2023 00:22:45 +0200 Subject: [PATCH 123/150] Fix LesLibraires providers --- native/src/leslibraires/request.rs | 36 +++++++++++++++++++----------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/native/src/leslibraires/request.rs b/native/src/leslibraires/request.rs index 1bd6e23..0c218bd 100644 --- a/native/src/leslibraires/request.rs +++ b/native/src/leslibraires/request.rs @@ -5,27 +5,23 @@ pub fn isbn_search(client: &CachedClient, isbn: &str) -> String { format!("leslibraires/isbn_{}.html", isbn).as_str(), &|http_client| { http_client - .get(format!( - "https://www.leslibraires.fr/recherche/?q={}", - &isbn - )) + .get(format!("https://www.leslibraires.fr/article/{}", &isbn)) .send() .unwrap() - .headers() - .get("location") - .map(|header_value| header_value.to_str().unwrap()) - .unwrap() + .url() + .path() .to_owned() }, ); let result = client.make_request( - format!("leslibraires/get_book_url_{}.html", redirection).as_str(), + format!( + "leslibraires/get_book_url_{}.html", + redirection.replace("/", "_slash_") + ) + .as_str(), &|http_client| { http_client - .get(format!( - "https://www.leslibraires.fr/{}", - &redirection - )) + .get(format!("https://www.leslibraires.fr{}", &redirection)) .send() .unwrap() .text() @@ -35,3 +31,17 @@ pub fn isbn_search(client: &CachedClient, isbn: &str) -> String { result } +/* #[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_isbn_search() { + let client = reqwest::blocking::Client::builder().build().unwrap(); + let cached_client = CachedClient { + http_client: client, + }; + isbn_search(&cached_client, "9782286056636"); + } +} + */ From 952ebfb6d182c3986c4fea9c524583d41ea19a40 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 23 May 2023 18:37:43 +0200 Subject: [PATCH 124/150] Rust: LesLibraires: Allow for optional Title and Author --- native/src/leslibraires/parser.rs | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/native/src/leslibraires/parser.rs b/native/src/leslibraires/parser.rs index 452bcdb..73be4c2 100644 --- a/native/src/leslibraires/parser.rs +++ b/native/src/leslibraires/parser.rs @@ -9,32 +9,40 @@ pub fn extract_metadata(isbn_search_result: &str) -> Option Date: Wed, 24 May 2023 18:43:02 +0200 Subject: [PATCH 125/150] ISBNDecoding: Add back. Can manualy add and delete ISBN --- lib/bundle.dart | 21 ++++- lib/common.dart | 14 +++ lib/enrichment/bundle_selection.dart | 11 +++ lib/enrichment/isbn_decoding.dart | 133 +++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 lib/enrichment/isbn_decoding.dart diff --git a/lib/bundle.dart b/lib/bundle.dart index fbbb553..7d4dda0 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -20,11 +20,30 @@ class Bundle { Iterable get compressedImages => compressedImagesDir.listImages(); + File get metadataFile => File(path.join(directory.path, 'metadata.json')); + Metadata get metadata { - final metadataFile = File(path.join(directory.path, 'metadata.json')); return Metadata.fromJson(jsonDecode(metadataFile.readAsStringSync()) as Map); } + Future overwriteMetadata(Metadata md) async { + final tmpFile = File('tmp.json'); + + try { + await tmpFile.writeAsString(jsonEncode(md.toJson())); + } on FileSystemException catch (e) { + print(e); + return false; + } + final decoderProcess = await Process.run('gio', ['move', tmpFile.path, metadataFile.path]); + if (decoderProcess.exitCode != 0) { + print('stdout is ${decoderProcess.stdout}'); + print('stderr is ${decoderProcess.stderr}'); + return false; + } + return true; + } + File get autoMetadataFile => File(path.join(directory.path, 'automatic_metadata.json')); } diff --git a/lib/common.dart b/lib/common.dart index 39e71fa..e20f7af 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -43,3 +43,17 @@ class Metadata { Map toJson() => _$MetadataToJson(this); } + +Future> extractIsbnsFromImage(File image) { + return Future(() async { + final decoderProcess = await Process.run( + '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', ['-in=' + image.path]); + if (decoderProcess.exitCode != 0) { + print('stdout is ${decoderProcess.stdout}'); + print('stderr is ${decoderProcess.stderr}'); + throw Exception('decoder status is ${decoderProcess.exitCode}'); + } + final s = decoderProcess.stdout as String; + return s.split('\n').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); + }); +} diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 280df02..ef6fc62 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -5,6 +5,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:flutter_rust_bridge_template/camera/camera.dart'; +import 'package:flutter_rust_bridge_template/enrichment/isbn_decoding.dart'; import 'package:kt_dart/kt.dart'; import 'package:path/path.dart' as path; @@ -290,6 +291,16 @@ class _BundleWidgetState extends State { IconButton( onPressed: () => Process.run('pcmanfm', [widget.bundle.directory.path]), icon: const Icon(Icons.open_in_new)), + IconButton( + icon: const Icon(Icons.image_search), + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + ISBNDecodingWidget(step: ISBNDecodingStep(bundle: widget.bundle)))); + }, + ), IconButton( icon: const Icon(Icons.delete), onPressed: () { diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart new file mode 100644 index 0000000..05965b6 --- /dev/null +++ b/lib/enrichment/isbn_decoding.dart @@ -0,0 +1,133 @@ +import 'package:flutter/material.dart'; +import 'package:kt_dart/collection.dart'; + +import '../common.dart' as common; +import '../helpers.dart'; +import 'enrichment.dart'; + +class ISBNDecodingWidget extends StatefulWidget { + const ISBNDecodingWidget({required this.step}); + final ISBNDecodingStep step; + + @override + State createState() => _ISBNDecodingWidgetState(); +} + +class _ISBNDecodingWidgetState extends State { + KtMutableMap>> decodedIsbns = KtMutableMap.empty(); + KtMutableSet selectedIsbns = KtMutableSet.empty(); + + @override + void initState() { + super.initState(); + widget.step.bundle.images.forEach((image) { + decodedIsbns[image.path] = common.extractIsbnsFromImage(image); + }); + selectedIsbns = (widget.step.bundle.metadata.isbns ?? []).toSet().kt; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('ISBN decoding')), + body: SingleChildScrollView( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Wrap( + children: widget.step.bundle.images + .map((imgPath) => Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + SizedBox(height: 600, child: ImageWidget(imgPath)), + FutureBuilder( + future: decodedIsbns[imgPath.path]!, + builder: (context, snap) { + if (snap.hasData == false) { + return const CircularProgressIndicator(); + } + return Column( + children: snap.data! + .map( + (isbn) => Padding( + padding: const EdgeInsets.all(8.0), + child: ElevatedButton( + onPressed: selectedIsbns.contains(isbn) + ? null + : () => setState(() => selectedIsbns.add(isbn)), + child: Text( + isbn, + style: TextStyle( + decoration: isbn.startsWith('978') + ? null + : TextDecoration.lineThrough), + )), + ), + ) + .toList()); + }) + ], + ), + ), + )) + .toList(), + ), + ), + const Spacer(), + SizedBox( + width: 300, + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + onSubmitted: (newIsbn) { + setState(() => selectedIsbns.add(newIsbn)); + }, + decoration: const InputDecoration(hintText: 'Type manually the ISBN here'), + ), + const SizedBox(height: 20), + ...selectedIsbns.iter.map((isbn) => Row( + children: [ + IconButton( + icon: const Icon(Icons.delete), + onPressed: () { + setState(() => selectedIsbns.remove(isbn)); + }, + ), + Text(isbn), + ], + )), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () async { + final md = widget.step.bundle.metadata; + md.isbns = selectedIsbns.toList().asList(); + final res = await widget.step.bundle.overwriteMetadata(md); + print('res = $res'); + if (!mounted) return; + if (res) { + Navigator.pop(context); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Error while trying to update metadata.json'))); + } + }, + child: const Text('Validate ISBNs')), + ], + ), + ), + ), + ) + ], + ), + ), + ); + } +} From 4b6347b7ec53256a88ef905b1ec3ee93824206a4 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 26 May 2023 21:55:42 +0200 Subject: [PATCH 126/150] AdEditing: Show only one author if there is more than 2 --- lib/enrichment/ad_editing.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index e004584..4194b46 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -25,7 +25,8 @@ String vecFmt(Iterable it) { if (vec.length == 0) return ''; if (vec.length == 1) return 'de ${vec[0]}'; if (vec.length == 2) return 'de ${vec[0]} et ${vec[1]}'; - throw UnimplementedError('More than 2 authors'); + print('Warning: more than 2 authors, only show the first one'); + return 'de ${vec[0]}'; } String _bookFormatTitleAndAuthor(String title, Iterable authors) { From 2fb0613465a6f7b8e30d13e6e2e65e0382dca663 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 26 May 2023 22:14:30 +0200 Subject: [PATCH 127/150] Add linux barcode detection Show zoom on barcode Can manualy add and delete barcode --- README.md | 4 +- lib/bridge_definitions.dart | 33 ++++ lib/bridge_generated.dart | 77 ++++++++ lib/common.dart | 14 -- lib/enrichment/isbn_decoding.dart | 173 +++++++++++++----- lib/image_helper.dart | 28 +++ native/.gitignore | 3 +- native/src/api.rs | 68 ++++++- native/src/bridge_generated.io.rs | 5 + native/src/bridge_generated.rs | 37 ++++ .../tests/test_images/operation_napoleon.jpg | Bin 0 -> 890042 bytes pubspec.yaml | 1 + 12 files changed, 384 insertions(+), 59 deletions(-) create mode 100644 lib/image_helper.dart create mode 100755 native/tests/test_images/operation_napoleon.jpg diff --git a/README.md b/README.md index 9076af2..00883ce 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,9 @@ In the first response, the title use a big C for "Cité", but in the second, it ### Build the barcode detector binary Clone the 3 OpenCV repo: - https://github.com/opencv/opencv.git (main repo) -- https://github.com/opencv/opencv_contrib.git (contain the barcode contrib module) +- https://github.com/pixelshot91/open_cv_barcode_book_metadata_finder + (fork of https://github.com/opencv/opencv_contrib.git) + Contain the barcode contrib module - https://github.com/opencv/opencv_extra.git (optionnal, contain the test data to test OpenCV) ```shell diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 558a563..afa3194 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -8,6 +8,11 @@ import 'package:meta/meta.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; abstract class Native { + Future detectBarcodeInImage( + {required String imgPath, dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kDetectBarcodeInImageConstMeta; + Future getMetadataFromIsbns( {required List isbns, required String path, dynamic hint}); @@ -53,6 +58,24 @@ class Author { }); } +class BarcodeDetectResult { + final String value; + final List corners; + + const BarcodeDetectResult({ + required this.value, + required this.corners, + }); +} + +class BarcodeDetectResults { + final List results; + + const BarcodeDetectResults({ + required this.results, + }); +} + class BookMetaDataFromProvider { String? title; List authors; @@ -89,6 +112,16 @@ class LbcCredential { }); } +class Point { + final int x; + final int y; + + const Point({ + required this.x, + required this.y, + }); +} + enum ProviderEnum { Babelio, GoogleBooks, diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index f23e032..4c15677 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -24,6 +24,25 @@ class NativeImpl implements Native { factory NativeImpl.wasm(FutureOr module) => NativeImpl(module as ExternalLibrary); NativeImpl.raw(this._platform); + Future detectBarcodeInImage( + {required String imgPath, dynamic hint}) { + var arg0 = _platform.api2wire_String(imgPath); + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => + _platform.inner.wire_detect_barcode_in_image(port_, arg0), + parseSuccessData: _wire2api_barcode_detect_results, + constMeta: kDetectBarcodeInImageConstMeta, + argValues: [imgPath], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kDetectBarcodeInImageConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "detect_barcode_in_image", + argNames: ["imgPath"], + ); + Future getMetadataFromIsbns( {required List isbns, required String path, dynamic hint}) { var arg0 = _platform.api2wire_StringList(isbns); @@ -125,6 +144,25 @@ class NativeImpl implements Native { ); } + BarcodeDetectResult _wire2api_barcode_detect_result(dynamic raw) { + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return BarcodeDetectResult( + value: _wire2api_String(arr[0]), + corners: _wire2api_list_point(arr[1]), + ); + } + + BarcodeDetectResults _wire2api_barcode_detect_results(dynamic raw) { + final arr = raw as List; + if (arr.length != 1) + throw Exception('unexpected arr length: expect 1 but see ${arr.length}'); + return BarcodeDetectResults( + results: _wire2api_list_barcode_detect_result(arr[0]), + ); + } + BookMetaDataFromProvider _wire2api_book_meta_data_from_provider(dynamic raw) { final arr = raw as List; if (arr.length != 5) @@ -173,10 +211,18 @@ class NativeImpl implements Native { return (raw as List).map(_wire2api_author).toList(); } + List _wire2api_list_barcode_detect_result(dynamic raw) { + return (raw as List).map(_wire2api_barcode_detect_result).toList(); + } + List _wire2api_list_isbn_metadata_pair(dynamic raw) { return (raw as List).map(_wire2api_isbn_metadata_pair).toList(); } + List _wire2api_list_point(dynamic raw) { + return (raw as List).map(_wire2api_point).toList(); + } + List _wire2api_list_provider_metadata_pair( dynamic raw) { return (raw as List) @@ -195,6 +241,16 @@ class NativeImpl implements Native { : _wire2api_box_autoadd_book_meta_data_from_provider(raw); } + Point _wire2api_point(dynamic raw) { + final arr = raw as List; + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return Point( + x: _wire2api_u16(arr[0]), + y: _wire2api_u16(arr[1]), + ); + } + ProviderEnum _wire2api_provider_enum(dynamic raw) { return ProviderEnum.values[raw as int]; } @@ -209,6 +265,10 @@ class NativeImpl implements Native { ); } + int _wire2api_u16(dynamic raw) { + return raw as int; + } + int _wire2api_u8(dynamic raw) { return raw as int; } @@ -404,6 +464,23 @@ class NativeWire implements FlutterRustBridgeWireBase { late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr .asFunction)>(); + void wire_detect_barcode_in_image( + int port_, + ffi.Pointer img_path, + ) { + return _wire_detect_barcode_in_image( + port_, + img_path, + ); + } + + late final _wire_detect_barcode_in_imagePtr = _lookup< + ffi.NativeFunction< + ffi.Void Function(ffi.Int64, + ffi.Pointer)>>('wire_detect_barcode_in_image'); + late final _wire_detect_barcode_in_image = _wire_detect_barcode_in_imagePtr + .asFunction)>(); + void wire_get_metadata_from_isbns( int port_, ffi.Pointer isbns, diff --git a/lib/common.dart b/lib/common.dart index e20f7af..39e71fa 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -43,17 +43,3 @@ class Metadata { Map toJson() => _$MetadataToJson(this); } - -Future> extractIsbnsFromImage(File image) { - return Future(() async { - final decoderProcess = await Process.run( - '/home/julien/Perso/LeBonCoin/chain_automatisation/book_metadata_finder/detect_barcode', ['-in=' + image.path]); - if (decoderProcess.exitCode != 0) { - print('stdout is ${decoderProcess.stdout}'); - print('stderr is ${decoderProcess.stderr}'); - throw Exception('decoder status is ${decoderProcess.exitCode}'); - } - final s = decoderProcess.stdout as String; - return s.split('\n').map((e) => e.trim()).where((e) => e.isNotEmpty).toList(); - }); -} diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index 05965b6..8d22e81 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -1,8 +1,14 @@ +import 'dart:io'; +import 'dart:math'; +import 'dart:ui' as ui; + import 'package:flutter/material.dart'; +import 'package:flutter_rust_bridge_template/ffi.dart'; +import 'package:image/image.dart' as image; import 'package:kt_dart/collection.dart'; -import '../common.dart' as common; import '../helpers.dart'; +import '../image_helper.dart'; import 'enrichment.dart'; class ISBNDecodingWidget extends StatefulWidget { @@ -14,14 +20,14 @@ class ISBNDecodingWidget extends StatefulWidget { } class _ISBNDecodingWidgetState extends State { - KtMutableMap>> decodedIsbns = KtMutableMap.empty(); + KtMutableMap> decodedIsbns = KtMutableMap.empty(); KtMutableSet selectedIsbns = KtMutableSet.empty(); @override void initState() { super.initState(); widget.step.bundle.images.forEach((image) { - decodedIsbns[image.path] = common.extractIsbnsFromImage(image); + decodedIsbns[image.path] = api.detectBarcodeInImage(imgPath: image.path); }); selectedIsbns = (widget.step.bundle.metadata.isbns ?? []).toSet().kt; } @@ -43,31 +49,37 @@ class _ISBNDecodingWidgetState extends State { child: Column( children: [ SizedBox(height: 600, child: ImageWidget(imgPath)), - FutureBuilder( + FutureWidget( future: decodedIsbns[imgPath.path]!, - builder: (context, snap) { - if (snap.hasData == false) { - return const CircularProgressIndicator(); - } + builder: (results) { return Column( - children: snap.data! - .map( - (isbn) => Padding( - padding: const EdgeInsets.all(8.0), - child: ElevatedButton( - onPressed: selectedIsbns.contains(isbn) - ? null - : () => setState(() => selectedIsbns.add(isbn)), - child: Text( - isbn, - style: TextStyle( - decoration: isbn.startsWith('978') - ? null - : TextDecoration.lineThrough), - )), - ), - ) - .toList()); + children: results.results.map( + (result) { + final isbn = result.value; + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + ElevatedButton( + onPressed: selectedIsbns.contains(isbn) + ? null + : () => setState(() => selectedIsbns.add(isbn)), + child: Text( + isbn, + style: TextStyle( + decoration: isbn.startsWith('978') + ? null + : TextDecoration.lineThrough), + )), + const SizedBox(width: 20), + SizedBox( + width: 200, child: ISBNPreview(imgPath, corners: result.corners)), + ], + ), + ); + }, + ).toList()); }) ], ), @@ -76,7 +88,7 @@ class _ISBNDecodingWidgetState extends State { .toList(), ), ), - const Spacer(), + // const Spacer(), SizedBox( width: 300, child: Card( @@ -105,21 +117,31 @@ class _ISBNDecodingWidgetState extends State { ], )), const SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - final md = widget.step.bundle.metadata; - md.isbns = selectedIsbns.toList().asList(); - final res = await widget.step.bundle.overwriteMetadata(md); - print('res = $res'); - if (!mounted) return; - if (res) { - Navigator.pop(context); - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Error while trying to update metadata.json'))); - } - }, - child: const Text('Validate ISBNs')), + () { + final md = widget.step.bundle.metadata; + final isbnsDidNotChanged = (md.isbns ?? []).toImmutableSet() == selectedIsbns; + return ElevatedButton( + onPressed: isbnsDidNotChanged + ? null + : () async { + final md = widget.step.bundle.metadata; + + md.isbns = selectedIsbns.toList().asList(); + final res = await widget.step.bundle.overwriteMetadata(md); + print('res = $res'); + if (res) { + widget.step.bundle.autoMetadataFile.delete(); + } + if (!mounted) return; + if (res) { + Navigator.pop(context); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Error while trying to update metadata.json'))); + } + }, + child: const Text('Validate ISBNs')); + }(), ], ), ), @@ -131,3 +153,70 @@ class _ISBNDecodingWidgetState extends State { ); } } + +class ISBNPreview extends StatefulWidget { + const ISBNPreview(this.fullImageFile, {required this.corners}); + + final File fullImageFile; + final List corners; + + @override + State createState() => _ISBNPreviewState(); +} + +extension PointExt on Point { + image.Point toImgPoint() => image.Point(x, y); +} + +class _ISBNPreviewState extends State { + ui.Image? barcodePreview; + + @override + void initState() { + super.initState(); + Future(() async { + final fullImage = image.decodeJpg(await widget.fullImageFile.readAsBytes())!; + + /// Add some space around the barcode to be sure the text ISBN will be in the frame + const padding = 50; + + final topLeft = widget.corners[1].toImgPoint() + image.Point(-padding, -padding); + final topRight = widget.corners[2].toImgPoint() + image.Point(padding, -padding); + final bottomLeft = widget.corners[0].toImgPoint() + image.Point(-padding, padding); + final bottomRight = widget.corners[3].toImgPoint() + image.Point(padding, padding); + + /// By default, copyRectify try to conserve the ratio of the full image + /// But the barcode zone ratio is has no link with the full image ratio + /// So the barcode ratio is computed manually then given to `copyRectify` through its `toImage` parameter + final height = max(bottomLeft.y - topLeft.y, bottomRight.y - topRight.y); + final width = max(topRight.x - topLeft.x, bottomRight.x - bottomLeft.x); + final dest = image.Image(height: height.toInt(), width: width.toInt()); + + final rectified = image.copyRectify( + fullImage, + topLeft: topLeft, + topRight: topRight, + bottomLeft: bottomLeft, + bottomRight: bottomRight, + toImage: dest, + ); + + final rectifiedUi = await convertImageToFlutterUi(rectified); + setState(() { + barcodePreview = rectifiedUi; + }); + }); + } + + @override + Widget build(BuildContext context) { + final barcodePreview = this.barcodePreview; + if (barcodePreview == null) { + return const CircularProgressIndicator(); + } + return RawImage( + image: barcodePreview, + fit: BoxFit.fitWidth, + ); + } +} diff --git a/lib/image_helper.dart b/lib/image_helper.dart new file mode 100644 index 0000000..53fc3bf --- /dev/null +++ b/lib/image_helper.dart @@ -0,0 +1,28 @@ +/// From https://github.com/brendan-duncan/image/blob/main/doc/flutter.md +import 'dart:ui' as ui; + +import 'package:image/image.dart' as img; + +Future convertImageToFlutterUi(img.Image image) async { + if (image.format != img.Format.uint8 || image.numChannels != 4) { + final cmd = img.Command() + ..image(image) + ..convert(format: img.Format.uint8, numChannels: 4); + final rgba8 = await cmd.getImageThread(); + if (rgba8 != null) { + image = rgba8; + } + } + + ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(image.toUint8List()); + + ui.ImageDescriptor id = + ui.ImageDescriptor.raw(buffer, height: image.height, width: image.width, pixelFormat: ui.PixelFormat.rgba8888); + + ui.Codec codec = await id.instantiateCodec(targetHeight: image.height, targetWidth: image.width); + + ui.FrameInfo fi = await codec.getNextFrame(); + ui.Image uiImage = fi.image; + + return uiImage; +} diff --git a/native/.gitignore b/native/.gitignore index 8d19f7f..10e9d83 100644 --- a/native/.gitignore +++ b/native/.gitignore @@ -1,2 +1,3 @@ personal_info.rs -config.rs \ No newline at end of file +config.rs +detect_barcode diff --git a/native/src/api.rs b/native/src/api.rs index 4a243e5..6b877b7 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -10,6 +10,7 @@ use crate::publisher::Publisher; use crate::{abebooks, babelio, booksprice, google_books, leboncoin, leslibraires}; use itertools::Itertools; use serde::{Deserialize, Serialize}; +use serde_json::json; use strum::IntoEnumIterator; use strum_macros::EnumIter; @@ -22,6 +23,69 @@ pub enum ProviderEnum { LesLibraires, } +#[derive(PartialEq, Debug, Deserialize, Serialize)] +pub struct Point { + pub x: u16, + pub y: u16, +} +#[derive(PartialEq, Debug, Deserialize, Serialize)] +pub struct BarcodeDetectResult { + pub value: String, + pub corners: Vec, +} + +#[derive(PartialEq, Debug, Deserialize, Serialize)] +pub struct BarcodeDetectResults { + pub results: Vec, +} + +pub fn detect_barcode_in_image(img_path: String) -> anyhow::Result { + let output = std::process::Command::new( + "/home/julien/Perso/LeBonCoin/chain_automatisation/booky/native/detect_barcode", + ) + .arg(format!("--in={}", img_path)) + .output()?; + + if !output.status.success() { + println!("status: {}", output.status); + println!("stdout: {:?}", &std::str::from_utf8(&output.stdout)); + println!("stderr: {:?}", &std::str::from_utf8(&output.stderr)); + return Err(anyhow::anyhow!( + "detect_barcode returned non-zero exit code" + )); + } + + let r: Vec = + serde_json::from_str(&std::str::from_utf8(&output.stdout).unwrap()).unwrap(); + Ok(BarcodeDetectResults { results: r }) +} + +#[cfg(test)] +mod tests { + use super::{BarcodeDetectResult, BarcodeDetectResults, Point}; + #[test] + fn test_detect_barcode_in_image() { + let res = + super::detect_barcode_in_image("tests/test_images/operation_napoleon.jpg".to_owned()) + .unwrap(); + println!("res = {:?}", res); + assert_eq!( + res, + BarcodeDetectResults { + results: vec![BarcodeDetectResult { + value: "9782757862582".to_owned(), + corners: vec![ + Point { x: 3229, y: 2749 }, + Point { x: 3089, y: 2746 }, + Point { x: 3106, y: 1906 }, + Point { x: 3246, y: 1909 } + ] + }] + } + ) + } +} + pub fn get_metadata_from_isbns(isbns: Vec, path: String) -> Result<(), anyhow::Error> { let res = isbns.iter().map(|isbn| { let mds: HashMap> = @@ -114,7 +178,9 @@ pub fn get_metadata_from_provider( .get_book_metadata_from_isbn(&isbn), ProviderEnum::BooksPrice => booksprice::BooksPrice {}.get_book_metadata_from_isbn(&isbn), ProviderEnum::AbeBooks => abebooks::AbeBooks {}.get_book_metadata_from_isbn(&isbn), - ProviderEnum::LesLibraires => leslibraires::LesLibraires {}.get_book_metadata_from_isbn(&isbn), + ProviderEnum::LesLibraires => { + leslibraires::LesLibraires {}.get_book_metadata_from_isbn(&isbn) + } } } diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index 15bb1a0..09a6361 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -1,6 +1,11 @@ use super::*; // Section: wire functions +#[no_mangle] +pub extern "C" fn wire_detect_barcode_in_image(port_: i64, img_path: *mut wire_uint_8_list) { + wire_detect_barcode_in_image_impl(port_, img_path) +} + #[no_mangle] pub extern "C" fn wire_get_metadata_from_isbns( port_: i64, diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index c94459b..66c61eb 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -26,6 +26,22 @@ use crate::common::LbcCredential; // Section: wire functions +fn wire_detect_barcode_in_image_impl( + port_: MessagePort, + img_path: impl Wire2Api + UnwindSafe, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap( + WrapInfo { + debug_name: "detect_barcode_in_image", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || { + let api_img_path = img_path.wire2api(); + move |task_callback| detect_barcode_in_image(api_img_path) + }, + ) +} fn wire_get_metadata_from_isbns_impl( port_: MessagePort, isbns: impl Wire2Api> + UnwindSafe, @@ -152,6 +168,20 @@ impl support::IntoDart for Author { } impl support::IntoDartExceptPrimitive for Author {} +impl support::IntoDart for BarcodeDetectResult { + fn into_dart(self) -> support::DartAbi { + vec![self.value.into_dart(), self.corners.into_dart()].into_dart() + } +} +impl support::IntoDartExceptPrimitive for BarcodeDetectResult {} + +impl support::IntoDart for BarcodeDetectResults { + fn into_dart(self) -> support::DartAbi { + vec![self.results.into_dart()].into_dart() + } +} +impl support::IntoDartExceptPrimitive for BarcodeDetectResults {} + impl support::IntoDart for BookMetaDataFromProvider { fn into_dart(self) -> support::DartAbi { vec![ @@ -173,6 +203,13 @@ impl support::IntoDart for ISBNMetadataPair { } impl support::IntoDartExceptPrimitive for ISBNMetadataPair {} +impl support::IntoDart for Point { + fn into_dart(self) -> support::DartAbi { + vec![self.x.into_dart(), self.y.into_dart()].into_dart() + } +} +impl support::IntoDartExceptPrimitive for Point {} + impl support::IntoDart for ProviderEnum { fn into_dart(self) -> support::DartAbi { match self { diff --git a/native/tests/test_images/operation_napoleon.jpg b/native/tests/test_images/operation_napoleon.jpg new file mode 100755 index 0000000000000000000000000000000000000000..c28d44909a89d49a977a09bbaa14da166f90892d GIT binary patch literal 890042 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 Zw!=K9e$u(`Q#n=;wQ6J{5y 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

SFX!q0{f2P8=i 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�+ zwZl?Fz2th!bgScwX0cjHLNeyn2UbCdE~}s3{d7n`3<)Mo1YdB#!p8xiLC?AtBL5v% zYjg~ArUu#BB8kjRg}lAR^LbbR z-hS;0I)Slfb*i(7VbCD$_@dyb#izY$6foKY6&aw3vTqraUAz>6|dF+ zS`}-{L!!61C&1R(Yv=xsA-(Tm?MjOTjKtV60aH=hXmu65c;|-No6PXW*!DNS7!GcZ ze#n#Vi^wHu8{<5X6_yUuDFjw;PP8opYG%C39;T(lrobg0Ads+4%W*eHfH<5iTW1R9 zxrIgenGh#zxUve_W=mo=OW=2GwQM(I0Bt^M$3~CFT03+`ZlDjkfI8K>Io6S=IUm{^ z+3L6NL#xG_b$OaRb$%|owFUGwtX@QddY_#U7wFP3AT*c5wI2e-m;ixMaB(3#j zQ$u@wdTOl_K&&@pV2o`#Q#kf)lon~l)~EU#V@F3%O57Zp^1?`n~^d1S`PgVgKEu?)|XHI1oCy z2nfOd04S^#+we^XZcXa4l*#u6Qemw#h4at>It78A8BG+cMR^Qd|53a7=1Z2}8}Qfn zJ1Ql_V?!K#$F3ZMh_k*^K8VzlRnQUXAhaY_zmX+{i#};1q&UEJ!w>)g-wyc7=v{6s zAs|bJn_yd4+U$4pu8~OwAYlV(=X_GOi0Qi*+S|M~gQ)q=Pi|6QarIgZByt*R0jX15 zj(;R=lEa0Jtn#ouuH1NKUc;hTU3wAlPY6|4^%^#jkNJ<>a9rn7@%;T2f$KBt+0h`+ zHC=skW#s??23<)wiTn8Qj>E2<=1HI9ZFgmMxOjlM@~`Ib(L*<0+_}4{`Lm6UH?lZ{ zZF1pWZZRMlEGM^n>xIvLgjEz1ra=U8c&~1|;aBf%YkwHtEIS{rEbWelAF5YgZJ7={ z^A%vt9ecrutl~wal+cIi-|2+C`O^C((B@kChpTI#zN`(r9M|7xF@77(&cj;6uXI}T z2g^wok8F1)sw@y^xBs4KP{#4d$Bw-({Az1p$#2tywqK5`sV6j7v;ZS^lo^l$h3llU zXcyjSo$EhzYPl-p+XYlli%gZ7mvZ3A6^6B3FXh&;ON%Q_j8h*0?+IX)PaVk+YS}LT zh(P#q0uF=|Z@3EAM8oUQZJ2)d-vgDxlYrYhDez3AhY|aG--=fOf<+N;TNygs!c6 zkEXA`U90Y)$O*=rJk$u#)RmsSz9-#SNHPnhNY7?%0S>ig#ur zg5uO%gXT0&RytKB5W5iA8S83a{pBi$W2SIha|mbxUa=X?Jl0BYz;KZ1MfY%ZqE+v67sHEf+HNu4u`s^``z~;BfPB&I?)?CNhBaq5 zY`}{DDSV&(kO_o&FItoZ%%9lMs6N&d5uw5BA9$*#GaX`1jfQ(_C0B6sUmvcYthHZp zotyGeQ=f6K83(0VX?>5>)a84x-6t?vm^_0b7sNRR*0-ms5dJ*g-)}hqnB8_U?jECb z*jcU=zrScrC8j?^N%sMeG@PQjHvlBxWO}=4+G*Uw4Dcw$Suy#y_6*3fuY%pg%^OtT z3bo;Q+qU2&HzaV0lb-3nJ8RplwZ5C#~HUU|K=4@@_m_v5u z+~_BQJPeo?!PahN==tLj=+EgC+#OAyRkSs4Cb|6b&swSu=Gze>?UZhyu{&OByBBGQ z3OfL3Iw*5*76tNtkXMIZMjhMpUnn8W)N;`+`aQTQg75BbM8keF0@=Bd>0ryRkIv?& zrv|7rXn-l-eU2|@h#NN>4S!z1=P0C6asNW4PH&$aP4e%*N?{caJLwyk}Tkfwm^NSGtSCau)|VnDjg&cSjL5ycJi zJhgFY5cljA2F9>7G!Ne_v6pJGeC+lis>#lM_`mA43L)_^*CokN)>5TfyBmYI^VCuo zP`6@^lwk_E*D~@``|Ytu@G>*CRvad=%E)~`3YY%&LdVYvSV@N@7(ZT3y)(Za)BnWO zy+-4DbKN%m@VPK6vp!7l+530T*ua)o0tjQ*Y%9SP?o%j7@ps;|XJcWyPejhNGV8ly z-kkql@@NDd)|W0$H9Vx*&OKFKe{4FiO7B1_(XG&~JvH?C;!Gfz4^@02uG98QtXSY@ z&5#?c-3v>7d&m$K1x3*=PjN|4c--7X_d`tLn)~qjil>>#JohkRlY|e7qq0WSnpbD>mD9ea{&{@9U7^9jc zJ=+YgT`tWh0g(9@vZXFht7)of50}yAIKs9J*}US0wM3n4U7}m*phSU$#3G^6`$2Y2d6J4yev?p(mx%j=FiH zVdf9QQ-dGS>$nxgDjUcT&nEy4W2;k=H-AM}}F zXebxhg|?lKR*EW6c%@)($*;J6GP3`_(Ehvz>7ho@h=oq{-+lZdXKD2vP${$C1R$O!c;J{z6>~XF3>3DJaRo_V@}Lb6d<`>1aY8R( z>`2c&))lx$5@DJ~gn9547wO6l42T$jj-)@+VG!R93B18$zJBAB(FX!Xz-&P)SVXlK zf7KFD?*s2g1>HauHDY=Z>e+8vZCf*X$fQHaG5m~oDId)EO4Kv*fB^j2!k`$SlX+|_ z0FpO*uJbNt8!+)+c6~KuT%mh((}xLey4OP%<+qw+))_uVAPcPA7;EQ9PCben{%!~{ zr+UJoC%Fay`s5V==|{n6_T$HPs&(5ESI`jhQ^tY}(WA)HcctAL28;Tm&*{OB4uY%< z6i}5LaiRU&(}H3hfD^os!~0eN9`mgpZL>8=%@PzSlGUNrx+DC0j0wY&Kah88oCtLT zU($`JNpuFGJuKe>c(%(}PZk#?2)Hnl(_$PQ_Yi13O={kMVw9}Zj}Kf)0x^Z}awzuA zL!DBTg|*blTNBvRCbX7Tx%Nv?GCN~UxB>aUnrsnJE{fV_Wo-Ta%rjv^Z&CCKOSTRC z_I}25_gJNT9+m23yNHJF&SG*(r-qM;YHZrQyc|Fv$X$FZA^1W?XP3>PQ~);okw6$L z$(CKKAGa#+xouvI9P_Rv=i301?dr%Wiq`;p^Oplp3h%Vn+LOTh;t1d!OOqD0X013U z-!A)kDh$ZfW<*(nrj&BF{h)yQin!(j$fxEFpJtRT^uz#GsY;j4lptC~-J=JmmNe2) z(PJg-YQvUNAb;58F+UHKdDXL3%ato|4u{}M4_#M$f3-4wx8q_^zCjUFl60d~p0jaEX!w)OaBdEDmz zoDR9Eq?7x9zD{WWlMArCP^d%P6qUM9aO)O}d)cuY%m(k?^|s&!PfR4rqC_=u?E{?L z#9>DTKXiG@=YRhDYkE`0*p@1B%Vlkhd?)9VIK^lM8uIPvjzKL|hWC4mY;(?< z=e<a+d_yuXF3VfPx1I0YdErw{9`ufyFf95=B4p$jnliCT@ zFG}0&$v5|bs1H-O7U@42u!~sPQ@t}(v|GNuQmXa zs>bEs(mk8J{>u2ao+~3IOE3W{J--z@B+HINV*>w#WZ5jpc{B_h4r3!Uu1Q1p-t>jd z&4hx~xr7w4Cib6~-Q;oO8^*l=uoLVpF1Hp-RG-a1RjoR3_nsTLs0=2NnXqvH50dL5Ba{39rmZE>!cKIIgU?>KpsBT5xn|V_i#ddsj2WT>PmEl{0EXO(}Y-7hW;CK-;X@_P*3@fP)~Qdsm$1RZQJJzqJEV{s^EZq zb6aw?{8izvzO2QH^`yjZV1$8P*3Zc*iaOb?`Fl2!wmlvubILR~i%3Wdu)vlSf5^5u zghJM2WhM;YftOfaa<$kYMcAY>PCe~?eftjCg}$z<^a5ZmfoMTS2+j7@J%Z~|J)^L? za9t4HZ6FjXi*8{?C+4VWNxAu2ZVZ*p?^GePh5|Phv7|0eVvomLTqA>anHQjPjS=~j zc3fGmEQ~TKgjv1k2K?=oEIWofuM}^k9v<}AW@s^mCwbfGMR*d~U zH#xF6J!Gw44V|s9go&exqm)gzMr0Kig#l6gf97ziFE;;L5JbeeeS+`1i-UXP(mxa( z>z^}f7!3~iLGiotxp;+BZFnE73h&#(x~ zd*>lpo30HKn$9D>k3zF$@oI4XbHuA}PIgXZLeQ|*=D-yWQS*;;v8t(D4hcU)r(msD zE6TeEz<;#uCe)6mtkG*mTdbQnA>pq)QNSO0F6!Cpq+rs5c3z(0A^{8w-U-X{9uMq5 z;-|>>eqN*Y4`&Qjo33@C+7jQBY#Vi|TWx(aK2ad@OOHvx#jApn`?=M7^7Ovb;h_x& zbBd0gH;yT)`{LkysM_aW$OdBhAn(WNOa7|M90S`3#@>2NfWwn(R|pj^vL;S^@x69s zI&hW%#0Zwx>{e2P7`28{mC-rBgMXpbm=XQ$&C07vwibi8V$$E7$Iq!5ow()LO0+b# zSvwLKgf{=6O!DH9YbnLA-k1&kmPhdHB5M(bh(SUee1EJCcq5%cBLY_MAy` zS#06p4rlh71pRp|%o0D2LQ4M#7a#r^Q?CzzQOO@R%j0FL>kcKOpZpE9bFloS)lf>S zJ^Zn2@4mC|{cJtNM-z8JuELyDMUH#TC4-|-(0m29c_Kjd^v@pw@?(zt-JKIB#H+@w zRZdnhVIRhu=ofRm^_WS=+QYnz@*k6VP{K>co#)D?1)i-<6|h$~CIl%h0I{Zz9z*jM z-D{vWefb}rObX-(T*E<*S8@XZbfty_zq!G6+4O3JKtEyq74i> z%SdWKQSz!$rB|ghJ-I4$BWbplWd2Nfdy_G6xDbhijoDT(LgfbJ)xTLptu~qf>0bB& zCuMcuGl9Yz6=wlJPyT*p&~&gFxXv49#hDX3Nb1LQddnBOSVY{j+hy>Ti^kO29U zp$qN&R??P|H)^J6gKXLdL~q9gXsw@$D@$NUd)Vy0zcyb;-<=kibJ`Jv ztGbQ}g0!pMvb1YAsU8Nsr{2(QJ=3#S&@%TV8|h#w;AkO4*u>GGS5H#EmGwopr$QNt z3e|l%UO80T`@g;54_sSoat^o+*6qeb1vB1ye5?HakKX=RyKdl|4;+#B7f;rDo*R?u z2ZEj_B?5(h`n@mdC}FI02nYQ*X<^CX7IQ$>aCJ!b*Jnqo{V+f|n+uE;=06+0_|Ahw z^8)7L23$6O13e>*dg6Fpjtz9Cc5{U4#MiVS)o-jR{~Sse@Z|WcZZ6vwUv>eZXOa|e z1N-zZWZ$D_7C|2o$CY%s9e9ZWJc)_zfHwE5nk)B`%OAb4Jhj$MW#;@V5I~{QK=z(5r7avv%|BiF%Q;+=shSR*Eo*hM` zXKR6O_t{LxbYPx00IxIYH6}F+^*Id{tR97oWVt39c6B*7b+yLPCY+F$RPk*saZP@L z>KZ#uPN0FE+mzgL0u1agH;-5E)@V(R+ZCb_+Rp`x9mxIti@F!zPGc>O#|8}lhI`$! zzhCWY{CD-F1E~>s*G0LB&=AT#AG2p=kEo_Bd9vC<$TR7O>Ba$*r!078K+w)?o}vt8 z0CP)5lu)s594-JO?%N=PF@Ye5hY8&2J!HcOEpz~4Ud*D>e+}N+H&nP6< z=|Fu*JN@NJVLDba8Xv7A=J(A@j=byf@Nt~>2nTFaj7cHSfjL+x)KCZSbFa$4$9h*_ zj2bULxg-fZx58-T=A_`D+4IPB*5K8Goam3y`X%kXefl{i%A(@QoI=3WSFvTM3TgJV zc8Huc6^h8b{nLc9KI3V=D+QL>qO8; zVymeYK0wtBFOT14m+stak73)7TupBd%q>1D;I~2M9G(1RqyX6VK(dAwgMIaGtBovx z@F^NLI_oMp1Lh#fasbpoZU^D!hG|-h>{0O9s!04c_pzXK+f4@-(o=&J8lAkaHU_$` zhpzUssPP2ZQBahi*SVgMZ%5?Rn#@%D13(4pI9^sz(1Xl+VS-0zdegh$#($yDA;73| z#gXdM<`4~EM-JlU#KgO)8f*d3`)9$JxeSVW)AzoseK?rRDnkWBixfTVMf8vM-*{@W^uN>vy0sO1BOSyK-fxtLdE{hNYsANcbpa#R zkl6_d;-Kl>kBxVk>QVir?q2g6K@rNr#a4(k9A(H$lG_tZ6~_Q8#h4&JoCr?zf+a~X zVX_uNpBt}rtvuLj8nP%xCNMq{zhsat@X}BaNNIA@Ka7b#=o!HzgB1|Oq?F68z$%b7 zKb$BlnyiLM?=!a!Gil!^SQHC4V6Q7%B?(sdN6%~GHNZ*1jyRHzqG6xGnbcE&S-^GI zV$mQV(-Z41%C*5ZO3G@%4M!*@Wzms9Wjc;4yD-`ZE z@(DPYxmaJ0u>lkIf@nQV{t@&Rj1(e56?#;pl@Bi#>B1D$h>^ZuQ@0{-YO~ z+_qMDVw$!Ci1GT+P}@ZBLz6;P%f1I3qW1oFJsVeLdH9U{HW`VH@NZHPE-1hRmTCK23I} z!I}u-oL^JVjuceet*fa#{YbjUN38m+3~_HfyRE=F z4hHU4doo@EhdR5XZByf16%z^;sMgSSuA)Lm_tAvtkKob0GZ6s4_4z{%W5&V;BRJwu zhP8mp{Oq6{WtL6eW`JgOR=_%%r+pue%W<&n%bA@|<5s+5lKYh*BPk|DL0BMe1WK=m z+uALR?4q433pJ+|l@65?OA-}OcQ-9d4z_wL5ual!+7g`UoE{*$+Ef`RvJHKi5YOZc z+DT+B^ylM6k_*Q|QwE^qU}|hot`#*p`J( z@eD0*%YSe%g2F7$OOb7_OI2ukD1A^P__D+NdtL&l;D@E%@CI(|Wp#PR-?Y=$J#vft zPPK2TzYC(&*85$2bSPPJY@5^1CWfJR`+n?o2Fer|#HN0DTMHO{^T|r-UZE(Jc1dXB z9H4UGt;*Kjtf|0KtWaq9;Ln>cnq}<-dLvLf(~nujCOFfKoq}h=j(lD!pE=#}Jpk!( zw-BCK5crE5r}dQkZud-S0PSpaN_G|z;7kr(#d`e6;vY<4*8J(AH?g-R{1+qSpFX@j z`*buyMBleHA#<`_0-0-;~l3#91o(PJ2hZc^|!A#FEEu+-}}5X z<=u|_X`=o~#7lulUUwG)%uBHzqLQ#TV-D2Ia91{Heg2 z%@6Z{3Gd-%d+x#FoUGzK2%w~;K=C9`QmjqdD|9ryy+Fj=p#y4!wBgkz3u(Lc;wRyHl)289ne9HAF z4doMeRyu#$KAh($%JP@@u4mvYF3okVGwvKOJU`BT7`okSuTc{=Bq@+hS+`n1vxoD2 znoVD8yY%Cw0{&A{C>%Dmq9yel7Nh_U{S_>Z)wkG(DkEFN;Wv#*Y=>6}=;M-AcOU4>wtv#RwateLJiui!Kdr zdyCZE`%b9^-aacke@xKE9Vrb4?Y)En< z=1kDtXT}W}^{cnT!c{ReSa!6$tTjY1oMXG}4=u`E#Sex-R4;X-O?=Fg^yHsiYivLV zo%O~6*p5FBwMPc-FxEy&QXDrtmU`XcG}c14$CkEj(!x&#Uz%5|(p~5U+-sdbzR6H6 zi@0xF>N5hJR*6>!W?F0+D8)3ca*NIml3GBe+V6?0zg@J9pqU|D7!^P68D4#?*^02% z<}c7g=kj74K3r@k@8So|0IG%G8|dV>>#GuJ>^9%Z_Tdx}&pOg?2T^}C}pMB5cG79cgJk7qcEH*0BRhcK8^As|3K-yn}0Zw60yE9kY3feQ0&%B0jSIlS-f*(82I)o5Nn)YvEhQ3Qt{#Uy?PMnOvga6&`$^nwu) z`WqmBootWs5Znvcf$rICp#yAm3CU;`{I|*ojNs0N)d@E6irnbo1lu4mjQKrdp2NNQ zf(@$Agk9UCr+4QA3l=aJm>K8p?{8dnAeBMD<31eH0-SN*P2c|Lb@1sTJYrs!iClW& z9t+;}R#Sk8aM}p!a^? zu#bgZN=)uD8>DJNyR~0F)NYfi^xHqc>w$?Ml>3PBqIXoGd9#rP952;QWwA!La$M??NYaGH>(J*xY?Dd< z-*1pW-o5y-qgy)$Diy-~hIgEwlix^Z`PxRxL9t|D%y>0v;D)W&Z*>LS#No<#HDj## zraJzj+=ZD_go%Jh>nIV{R(Tbh1c@-*044#@+x#n~2xIKNS-JyQgoH0p*5oq>1)-N) zDHv}9L^WiN*Az7Ak0v22oo8l3OcT{?VDp)p97^Oz+i;7>fntzo4S_E1+{qrYanqD! zjtgaX0_LAa%$~7g3!}O-h0s_D#xTLt+e!$H234_3z=-+u71s<(^y4O4N_HdEizYk; zD`G}KP-;*k6j%czqp?GSrFAN>4lyz_e4)QDTq|7~YyBwZcFB2*CP?ccFDT_{ME}cS z)<_wU3vRgPp(yP?DkaGg==m29*gEM{dtVWW@#7xSIf$IJJL5^Os@N%Mda#i1^SSX% zVP>5cB#v}QI;oWU7yq(dlXN;}r|DM5E=a5S^hgn!XY0L^WzV*~8xP1&ChMC_Pa&pA z@d+_bod8NP#u^bMywgN+d`LhMrtBEYyVhO!^9?QyAp0}=r0(W0Wid+-WNfJUp-S#b|};A|LV&*9Pkiy1A*dC2wZ_y#<9Sr`7JFZ4sS z%xNEoh-9_LBpemecgCjC6%Lwq`o3KT4IGS zn?-~W=%qwN%%ThlFYRcLfjs^FPBSbt&iw#lDuv7F7~4b+g1NWN|CPvorlB5uYC{Z? zsP&Y#4ld|gm^Cl%Dgcr$tv6F$+jo?zW-HP6tLYR%E)HyYx1(xz z>wt~qut(*%Q7#U4kgC=^b`@xvtqL7D=_$sx)&Rr&Q6IZyP!`!{Yief$s$><5H8B<~ zl`Ou12Tm-iBe@E^l=0dzB>{F|dj_1o29}3z7NZ9Eg&Gk7Du>o>67{zjpal0f_??j2 zXM(UOhb@m5=j|2634||X%)gKxwCzG9$mS4X-`^kP*Mo*(+*JNfqq`+DhKmz#~J;?WBOoJ-1M7+Okc54vqf5e`HJZfOHq zc!J|VoPI4#g%G--dI!MPlxn3Z9D5O=yjQt&-@!a5@H)2xqSNVAg}6J3Zq#H4XDLJ( zD68w;&-1r$P60_5g8gZIg}P7GBjJE)y_$x&yeu86r$rzH5TF|tmNMT2kdUeyO_2ml zqKr9GC;-OSCVzC}5Uv#_Yk3r!%x`Z3xCT`fX5739ig1fJxgr z(9!1hkv_;)4PVF^CX3J#Z<}2uoxaHi z-8LzlBX;zJtH&Fd6rM*2!c=_ZwK&l`>?iiO)}1~_tQDM`zxZ8N35=$cDaw`%kYBOx zA5dv4ngH7N2%8F4o;w>`CYxOEwr|s!rfoXWg-~mw_=HBHn@|6tI0{;m3^^_zQ)b+X zyMN-Nt%DEvaT44Awy@ryf0TdIpx>w-{j*Q)r&An#FkVV+zG=-SZ>hq#thG=}32ypjQMk6()5>oPW3DZPf6AJz}GBY@WIZ#An8$MU- zKo49(xP6LGJCw&o?nJJ)5)nRO^;uMeMKuH+E%4$@k(*NIyzYR|FD!lsnch-vFglQB zn%GBj?1?!LLld87K#^8FW|0IvDm)63sw5aL{ZDQ@|#}VEH%lGeBKx3W2nI?{ve?KY5~aKuWDb zO?$gERm#l^zSsdR`_Q!~1t`Ld$LJi{-vX`SCQppYd6y}gJBlVzhxjr(a3?k@9GdeA1}*lOVVPD4_+|^^MH~LgD<;1 z$G=J}b10BS_H>31YBa`!N7rYH7TMH)Aw}WnxPo`8@q!ex6C+Gd&t?_gp#u)AD7qaD zor_waj3*MuM5$vL?bJf(_3>-`R#*04c4*#q^$k#@t$ zdf_)N52pZW$46&6oF#p`w(%uu2=^BE!n`E|_SqI@xPC&w%nbTjz0wN}Zh7$e;#u{G zS8KeVbaKAXNliUrd-w}rOQs<(d^Y?N`$AlmaJYR*9y`o>IH?d0m9RG_e~gB2_}lx% zhN6{tD zA0!KG%{Vpw;nJMs81ly-5d-xDx8d&tm;+^}56#Bmk$L)xw-lpeQMi_4n@n^D8Xgs8 zZ4A5?w)5JKr(CFg{^}1&;KZXd?|)Vu|H7)-5OZpeYQjip^9)h{H z)UNnrRb0ragGienVKI*`*^0;|BruWn7c?#8FTNj zsUtGkFb&7un43|ZBu6-y<8CG~aVDi&bJsX&W-T_Oa=N3abg1U08k(h&B)2(|+~v^C z_jmRC*YMb5pKI53z2EQG>-ox{)xXxM;0lIA!+&8^vl=1B6+@zS!b5E}!rvEqueo2q z0K9~(lYjEhbA56aVc2+#EB>R89{cf;#OM;Yxz!q8tbm1mF9$TXF~n6pOFN%!$?=-= z0cx;XN70>#?K^FeIiY{v=)n8IaI8MdXZ+`{iyVch?dqQ|jYK}{3y<>FPS9fTn2&m2 z2XD%~G}Hf)+%N<-GnkJo3NIRY*_k-|{&qs^tFn*{&eQnCg?ZA(REsv7?y%<_HE&uT zUmDF(==^ROu^Wh}lbbOYBV%NKwv8}ElIP(#oUJpQ9b^-*V375{_CL#I;&<>NnK2zLpER-0U@A_VMfy1UrIav1@?V*zwGNn@_|ED0I*syOp#lZ# z5NLYy4*V=HzZTAYB~4IyENv=v%8pG9vtyx0KuhZ$m9;U~NhcFG!eUO7eP*t*uxf@lDuWx`6`#HMuY z*EHCSZ!lM&fVv_=Yl=jrQFN-_+x)%eQNnzZ?%Dz0$mJ7+@daV)_5*nFhBE$&5Vkei z7z35b^ZWG7ucBt=NZqgSh&Jz+Z)5?!me1p(`lL$6Bnx(Yv&yyHdi=i_Xv1xGR`k|& z1*vcxh)D}w!R>qUP8d~Cd9kX5dHN*}5)7-RQG%?0V_r=fURqNWzdjAZ2oCw@QxN^Z z0NJJdPev|He2svtwMJH}8zty>S}uh+j5qt6Li9bA=q1weH!_44!~5-HmIFy=&l|F; zdtW#_x&VByfRFOoyRa_`uWQ*7ASehj_FbdNj!zaj>O5(ey2O2Kqp{93aR$osaE~Hd zl?QI?;fc8kka;D^N^n3E#3IcTe>{_yt{HVs)>ixxMU_qn>D0@xQ~DeOp3Bi%V{2EuY>E8E>XtQRTpT-eg(&IS;;hW;NhH83ob997Vo%!5UzS)}X*Uum# z_x6uG>@M8HKILbiOxq7d505EKK$DD6% znhd^GXf3baqC=?rHZCs*;&;kJ*RY_I?+KEReey~N0S2a*vstFhkSUK&$hW#uH;$?1 zHaHXt`2+S`ppN14sG0eG{2k;&(Lp$^GLiMi74cph_xU{!M+X$w>&!EwyS8VGA{kP< zvk#G%TMi4Ok1=?auv-^Ft>n}5*1U9+9qO|vTD=o5>SY>ZwZSfBHp@$c!jc|=^$J7< zFxRv7AUsxkK>+A*@^L`|?CGO#Hdb6$4F)>b27teUhZUFXOPD9}J|}h?-T|o_DP2ai z**;Y+Z7xW&jI_bDUetAQO3-Dz+@6Nf2cm2z*8zV<5ZnR@GrVw4^*sA8uWv4LWsrw? zX%i#RHKV~}!8a82?=807;Xdhj17d^jvD528ltsOX6Zw1?ypLRJYBKJ@(y?M81rPE| z6J8gCKP$$-#P0ea)ehsTWsp7k`z0&OMGWi+C?DSE`h=fJ>uPhg?_>tU%rt0QK>)MW zQR61>|Hg?4y1TUV!krhr%?Y)agVtHzix> zG2SYAQ%om66xM!wM}ng4a%ukYpj@T z5R#e~HYyMrE05AhMG4_7JFNSJfo7)E>LZw7>lJ2S^iDwa5F0$1K>LD0w)T0l_l_e6 z`pj8McIgSA3yqH{KE~IHT!vDVN2z~xi%eriN{_xh0Y~bR(Ytc7=b_Q%S{R7cpu4Vz z6f0WabkV&(rqQ$_7AOcFQHD`obPk5zj?@;&u+#8n3t)hS&~aRRE#2hj9e+-fRCnU> zb>pcZ*Qlcf4R)y*!2SY3bTn@UdnW_Dqo^o*99+Qg?^w&BM(rR&h;S->xmJT9>YD_V z4|c@p0;Rn#Jfa~<14?sgk)k?)AXml6xnF6;xcvA!HNM#vxDfPwfp%DEqUG>s5jy{m2!|ICky<1B#- zgGw+1*vpg_`0{NAp+NAs?=Q(F^}f7iT|s%JmiI^#G@0~;$}M4%TF{?pceTN2?oa9l zTR*@K9$gtbmkC@GP>Ttf3eEZkG6gi4<(FW1dHrk+H#4c-Dg!6;@30ln}rv~ z0$dO2=K^_!2X}@|dZj$ZdR@NW4=e0@A{Xe#)f5My4gORHUkNnkCm*ETTb{WTIO4sp zaRcb`aJI)!9qMV=7!@z9D|@R1uDU}kgnKD5VeuS4-@k&m^&Fm?n0@%NBukG4TdA%AM&q^LKqpb$E?ADl4rW6;JK5*9c zV$H-ZY!Q5fnzaz=HV2Z)k0+}R-@u@kBL03rP^#mQA3}lz&1$=rXDK%JXYPWoxYYyWbVu*OeO{d4k8Omp;kvGvIm z;!NIi#{VGVZ%e`rM?Q%=oh;e8mn`0$dDgkN!RqqSGfkk^R844Zb1!rqCvDmN&hUP> zhH$F-e~_B=e~@MUn2?hBO;uSkOWkp5MOmNb6b#qSuh%V8$N9Yb=I!}i$YDM|^>tsa zekW4#?7zngK~GO5C~^N|P&u(3W$$g82DjRwaXJ^oDLUu~l`f$E@EyTr&L*53WD(x} zyi`Tbbdzl%(0;dwT`!9pP}y0>O81MJ8$Q2G3{5? z_8aAr-Hm21Qg--O#zw>}0Ov>yjXpUvp*zDEC_(@xgFCMz9>sm2qYP%S4jIT%6$9lhLiPT zp5VmxEA^i__kRif10zBubl@IJI#^oKSCetN0!7YV^r?&As#4$Vq!o$X6&muz9?`3p zY~p2|IL_+8yQGbkQpcv3r@QqVE%cmHoA=#{{_HC|h&$Yi4LpyUU1bzpPTY=Y&J0c*x@d1PQuU3)Vch$+EtoM_@}MM1`XQt| z!wbDuScxkf_Wn-%BZl@ByK5=v^~vkrz5_;C-)N18%!~PhojT$6yu6o@j~@N8|D1^} zEx@*Mm6Dv)Epv(@GDAOigBPk!%y5I#w>tha;r+E7sfqdX4)#mzj_BT8bpHQIlvK03gZ;-V@3bqzfzS1kJ(VX;hsU@L@P-XC z(33RnYTfS;?aMDa;+|68{QHP{^n!ik=DJ4+^P1!x){}`x-$!tS^LJ-FoZ1c~|8#wI zk#^zbVsh_C+LBrN&Y#~pDl??6w3`KI36fm)=qFX|8^pG4-=;e0fob*ygMz`(ZCY2u z{kBifHt}q?{O*(7_<{}JQ#kOqTJK|bN$*aN3ryQ>J8fGeN{oU~+Y1E4Tk_MnKgePRoxzzrH{Kg5X^#RuE*HTJsdp%F%BF?UIj(zh?LJ z&&^X2-5}R>kvV|)S-Oou=>$L`OA9z*>ip!y6hCm_Y{GWL(W!jh8(NA_?}8dV9vLfE z@Eu4PgALaarW&3nuR9b4kS;AZ5F;iW?vwQ40Wq2K=_$zixxxDRP(PeqQT+4c=2IqKob_A zev#;H^LSLFA?dCCJnvrBL0=28scC9kK#Zp#Fi^!YZvh8Q62?Sf%CQEgb>;~TuQJC| z^O~UH>7lCIX?=-weZ0(~&|(tkJm5<)%@)NYO+vdAZ+A)3lHsIKZK+@YcYI<>FdlF8 zuiMvPr3_r_S|Ky^tiy2V@t^+@+gWk6CJsxkZ{IEU(NsxS>yTUn6BBRPs>IIZghQP6 z7$dL)%&9jMPdF+}8c`E$wP{?td%IHg?d;Fu0IgNzm4^T?S5uVzx)}T6%nLoOtk4$Y zwuUc{Z`T7;qf+(^wdQ-+@l;N}?{OQT>X<XE9Hw4bH3eptI+IvbgvTJGZvPD@Jv!XZwiVlmb ziGocx`rS=eBoSn$`Vj>(TlaZ(QNA@!wQs{TNJ6~Wfsi5LIATnG1C#yu-dr@hV= zS@1}Hn-6!9D>;iccMiK%IKy8wL;Bm~g6xj@5YAb;yqp6-FC{%==d2seZznP`R)w*t zdxjaIt*8z?U8vvO4lnM+m@dzeFQw)5E9Lj?KH+#fOlWj(;DOfi7|U$2qg}H77k*~L zuuTalvA3)N41r28o``v`)9Aw=6Og*V1oC8o_s48e3Qe>+KNH~jvZ!eo7#U10+u3N%9cGgb+_`Q(Wp3yrLhauHq3+!kR1Bv=WhZsPc^-lCMsB| zGh_SCk`GMQo%J%y0}(F-3!FqKvvXn|YzKkxQpcM!z8kbg%AxodjYR!;tD4Si$=}-c zq)m3p&)%{#Rprb9KXCsfy5meL$7)A>mpQ64MRZLKp7q|Nc46~7d!+IQ$T z>{LS{J31Y_A<&T3pBCGnvpPq6!_ESz!c8w{s3bpJqdr0mFVq)mqQ{GyV)Z)y-vn_9 zqhEC-hpN)kC-&!Dm2+A68Io=?G(A|y3H6ubDyLM3pkU&(SmrpgOLeHC*qH z1?&ZXbLHs^Y21KZ zqLIc3wg=4{V_JJuzZaa#1KcoGOZjGQ`z(1YzyJBgQE#52WspS>myHEexWY;D!(2!a9Nj+a@(alJlT+{dMBL{Qa;3z?N!yIcoYGgo>b0)ad^fNT}Q)>+O} zJhDZ(KRXdpTIj;D;`o6GBhVxmVr@hlz);f-l8iaO)na?n1`AvZs$<4m$9=H0A%+i@ z*F`BN5bKVgDojB%LWnYo#YnXjr|m0L`O;#PaHvfBGhUYuBtIOwA2-lqceoQB^rOh* z7sz{)hePw*yTVDYgv#%AaDwqh>?}Xn3o=8YS44ei`7ziyzn=s|Y*vAcp@NQ;T1%6} zfK3WWbmSZM&9y9HZ17ip8A-LBV%9z?FW}PyV?BHSg;ImWT6UP-PRQyt;4mK^D`2ak zI!4#!@ItU*J5)?$h?QeZS40+5U5R#1zz&4|)7ie`wL9*LM?P>uN#O+CmApgj-|l%K z04#Vj-8!G03D+`k{z63;>ySyxqhsZC9~9J&j#px7fo?SC8`k+&4i$a9T^P-WTzEU3 zjt=*`zX#9qJfNTtksO9Kyo^k&KiE>#P(Leg}ge)ac0;~;{Z zLoEZ1F9XnW3br3SrnKZuQ0|BFR*@Og=zdvABOYw+#L~g_Ck4A9}Aj1LV@l0`h zmo?z(|6jfS3S-Gs@ONPeyzeJy@CdCV?{q_UQK(kX)ge2`>rPyOFIA zL`|dW|6G<$8F= z`{Pt)Ep4_#viZUxlIq;BTs7tb+4F^Yxl@V)8hx08)us<}!F`W$76aQn9c9m^kIFYl zTShgE{M^bR2U|i4#qq7F%I+P^Q&ux+Os1mK@ih?CZ9l#VVO~8|-6FL%Qh`!bSw`rp zoWqjSu$Vo4(8Oq%OYi@n@_yB0XpTGzz5d)jEjlGg&zgrmX27yq^i0dD`*uFadxE;* zjbEMWqq{c);2$n(Gi3I2zb@uvG#uv)YnK-?^ryKb_Ds?Tu#`&o-_j;DM71ozw}7eM zdIFb4i{g< zqSJC|zix;tRqpi82@QG`8`5%YyfNDKbixw+e~EX zZVPp%je?ZfQM4;Izn-?+Di^=jH@Q2r-#b3tX%p>#5X}Y;O7;3W{?;$me^~zJ%r79P ziiz47c);tYd9z1XbO$~fl~leoZl*Y*2C84x`PE1Gg9BWB>Uyx~H0;vvyFP8OT{V)K zrRO%B@(@uO{{OIpUxH9+x}f}g7rYUV@1+D-vb5&b>XqQUfSl0tx1E}mSCP(1zB)i?t(BQ#%((Y^7i!E$2#`J1}4&y?D0 zha{TZ;)+QHQ^4P4YLoC9C>fGbAVbd@3X8Y_X(znTY9Pp#;eR|l4ey^=cM)wu9jefV~ zrQ)p+h!JjsBSNT6KNWSdS)ejHl^@PzVS#WKUh#c z*Oc`2eT&vh`rx@9f<#Z|qO7$lPP4FgG!ETmFZ1;?XYKbvMnL-?`;dzohw1F{C#EJ@ za$IpBcw_p-1f<|M*Dz_NfM(A=qNKB(%cmaiQVttk@VhH0XQ-Uo@7IN<>NTv=yG6t5 zGePIsk6d;#>E6Bcy-rs><)uhhn2&?z#yMmq0AK7iYOt#ve)jZ1cS*vW`)}?t@3vwCJEz2p+3Q;GxT~M$Q4&sg*&4FH(d^LE7)Qfb52k@}++_x2_dUnhhDLbNz z>5U&99`mA_`A(QWM40=0dD5lN?;{^gYxobu2Z~e-!>wepdzd zyXx15j@a)rUoY3$m$>^L^03bjdTo}og`s7#JL7xUiCB{djDy${(3e_v(9MhJddXaA z+Zn4JvsQI#;(A%51>&Ed5cVLpa;G8Yd>O|TlPlI9Sqm-|>7X$AOt!7?G z%zLK!$^p%C>5tG2|3f51pQSi9l@hdQOEtNC&bcHlIcm4_b7%7Hg;4KPLUeb+wEKsN zxRc`}MK3RJWim)r4^!Zc@oO#fdz&5ps`~3lEQ1oARe$e;mVxqk+ny8Yhhv(id1rro zn!mSza6O)j;xx;`yyDnopIL=dvpF_}8J2v>;=Z%#e8`h}EAIj67ESKCk>LgRq`Uv9a4Tl8hEf?~GQ!SSp9-T~vEQ?#(akBqGw%D_72gFwYgL+xpe_<7M z5tb#nBlkjUe(K+3Pss!6Uh@{k3O=B1k%C6CPn|{qBf!wi);UahoP8}UKNv7VbvM(g z2%DW)`!y<@w8`R#%(3?XFFaMPSY<&}qaAX!<)s z23aWcBSzRtxR`hngekHc!Ew$SHC9yibc~7Yx9lFQp8VP}8=&?e6UnJG-6Jl+c1!e0 zT~`}Tv2(jKG_E0fnhqmzQN}vsXd2_Qu`^!!%Qah^2r&S;^qBz+K;) zSPb++Kr9HAgyYT+0}9)Qm+3Bw_a{i>jcRXBxS&unNMCt@#EJP!WXX=%1;{J2FW5B8 z(YX9|d5oaxkncNb{$4i&8lRjiSz zU1SeC2Y-V?{5)xsnI(0q`X(GDEC9o(t)4NM{^{(tmFhb0)A=o=o1P21Qr?N`j)d=3c2sBmT?FBdR zvl1BjSTaK7M?~g6re7aa`dOQUs3vu*{KDZQbx!p__}vxR#GkfiD_dpt2=DtKH-;ZK zy4!CL^QN z?qK$L0;nFLQ8{dYeNf`2fEocGwq{$GQ6e=9H!=(u`Qw(K5)V&=nZCPD-3D@VSV!;i zI1Alh8yII3cg5UQ%I7wW+*=5Ci5lfGpDHC^TI|-*2Z=9tXM*%Y3gk8W+Pe}(}CxJbsIbE z(~Kp!Rf;^^rCLq~BxSZ9L$Tm{IWU9^(B|nr%^1sN(a&qaX$YhDup=?-)2)fw_`2nz zf^x_vb+!}x4z5G@7d#Xb@_@f_rzyiHvMDyS5H{6huiZ{f%c^Zd2@#p?qTV6OtI@;4 zg+Z2TO7=`(Z<>SDjt86bRo{s~=C|S=wu>VH+6!GMnb<+5>96QsZ%~%_Ojh z@eIEU;hs`!U0Njx54UT?u@!w%M=UkJbk^kOn#!rZTauJ0ITg; zn%6`Z(0&Eq7m#YM*#wxD5BPrXb&$#3LO4MfZ(;`wuXxe$K49L{c9#ETeH%F#M;IRQxTu&xE|Nt}OR$)n<~i@8*v_BZ z2B^h7$kCQa5GXdr-(b2?=>VJl?)nJtQU2nJXkS-k9n0u!Ei$H<83N1^9Y7U*_tv-( z)`1J^IjO~FPO(Dk2%>(Bvoz=1YGGADzsmsh#ZKT8=o|>@Cem7zSlUdUCrEcu0-JTQ0FaPit72=!NL7CvKb>T~C{!joGko{G6}z;^c0eE( z9MIwtdUL(V5AjGPga$@wr(2XyxSHj(t9%@VYf%RH4zyN|TrZd@S21GvH<^ebCtBw_ z_qB#S=ZS*_*93SRbdtxcf1dztmbzBgj<5{E;AQg4mq6Is(9<(RW-bCLpK71J(C)p8 zi@s|bk#+>54{i$q*GDrXc55D7u6I@!Ibao(^61g;8I7x%jhMsp{w-TZfd_M=`wM+Z zlCsIPr>!4hwBLJcf>6!a?v+uZrf&h+Q@i5mMK5c#!XNbpXsGWEzEf0OYWe+3t1JoW zO%Wy&|GjqdaUManQqQdQ@RzUbN@v1j9?#d}P&dlaYuvs&^Rv*a`fozGhXZOmp?<7& zDDC;K(T4>j`0pDEHDFc$so0 z^@GJREcA>VNb{XzQ(8dU9nXUiD77X-u?M#_kAzs zUyKw&8U#D6h=lG#ob`;F;zE{^cY@`*JZkE%{K9k$NHf6*Jqii}wcW>fs#r!bMt|k} zxtXA?@{4Oh#uYl@ff%L5#AFjZ;svfJ)xdkWHeP<|hy*T$_8$O(oc}?$ubUhZhxSc- z(_r-Z>T~*1Z%b6qY%shGxb~Z;ts~-2O+M|(S3EyVa19&0H*A$-a6T#Gh)Y$p>v6Z| zrg_d`yJUBMGNrS*uKT#ezCc6mCCmnPTy`y9=I3!;y@hm5^YbM8o>7mPlKLY}X^4M6;i!2%x;-UYG_ zBOQ32>_`j*VrK0JaCK){dM8XFhWn`ebDdJml4!yp)0;S>0|2@Yi?J}Va=#9+#4`NY zbP*7V9gqD)vjeU!@sLA@P<5_L&MH9RT6=0(8=<`NN|Ypx*7m+dwVz2%)(lK(K?>C@x2Q>uwo}jMq+?vc z_#j;;obt#ZbbxISS~J0qv^QYOzR@HX#2B!=Z*HCSKjve_89Ubqx=G8zlE-|e99R^m z%8jwU^X_2O9JoR09LlXc2Badxri!6yu0MyT)&X!pk0%!VIrN})DmY;GFr+Sz5p8yH z)vtL+U;z+==c)hO(Pw9K8+;I4ulAtrdSg5@1UtJot5!9?XfZ9jZf!roTr@+nc2lUG z>iRGAXpK)Jd3b_)S#R`k&8X)@Sueh>>Gwh(^6jZDXiiv8aTe$_{r+!=$ql!DKPf{q zb=x(K<1b?Vbb2-S=0QR6^FE{GQ|+g{GT*&u^f$du_kJkNXMhAL__O`!gVW{jX9}LZ5v( zD@(kY|0%n1<$nM9Tlan|wuw1ZlZe=|c+(uhRpELZJT-9(VgXtjdotHldbxzSNfU=7I0+U z`lSkVdd0jnWb2OGs^|KWK)e6rA8Aioj?}+VWzzT09_=Y{58Xhrn{M2qdG@iry!M?B znz!Nc&Iw-0oPQruMQX}1$eI>B-*^Xc3@>SX6!j%|t6siqbZPMeeu}*8cg`XO-eMX1 zxgjJiEz8l8o@!XG_~-X@2hQ|AVpD_6v+D1i6`Lxe)9hUKT)l}jJzOVClv{c9s|2=; zNs5kS%mm-tY4%VF+wU8;2o760CiHeQNo_+PZbqcF)`xs(9>=nbhU<{SBGa*_Jkxdb z4yhsrSt{NgGnAAdx!{qv=&xyc?c0i3QrDxe@BY!)S3Z#+@LM$_AikrENrGK3%^6@6 z1n4Zq4DyGy@|xPSj$)Mu(m{NJJ-0w!%HLc7#I0qX#`yZ%X|yAyfF*Q$Bcbl*7%w7* zID?#Q6>6&ab<79-TnI;pn!EBcft)nfCCJFe90Y<-%%_U&^EdP{q12HS8xLqEFyTRc z$5&cgTLSFi$6`Vq*-1+y%o5%ICPC@0(LIAO*Rj6>RAPSVJ$>Q8Lv|Wu zo$Fo0&jp}cG=2S~mhsx^IM~sv@>1E=h;^r3rJ;OJ&bQA{u*B( zv*U1r4VCF6wTIeb@yqmJ6=fdO@QsBOQCpS zEp&?+BFGYY9-DW~6sP-kAm88Li@?nkk6}`OXXn^TYW2f8o<|F!4EpiRJ;GQ}B%QThl-I7nn_6_$Egk9-ze{J6QFHyt>~ zqdG_l-YTZX1+Jl|jIhF}iO|Ye5I}dW!<=U^O#4PL1rSMfRY;Rv(mE#UF=B?gEdWD; z<>7gXD`SvPQx~dkZW(XG?btmiApMK6@s%lUJla|}Rox#~<_>PX8fUnn7&NS>PKnfk zTu#0R07Oao1%s8Uc(F#%(;%y=w<9dW)zil1ZC;>W|B;t@UT8eUS)%ODK|+m%1Tl0o zx^zVe*l*d;F8*@u6a9%%MQz2Zh~D3Cf61O&KaPAJ#SBGtgYcV8PI|g144cXiW0cTtYM(~ROv2k>+}dqx3YjoVkm;!za<1Fz#b=({C*?H5mqI;K;gf0Yf5Kj zIy0=$?xA&~&EGnWN~U4aFCA^4f^g2_mm86N%L^Nq#i%}VUbavzfC!W(HoYLC z53S@J3|C?~jFdClg;D7=Cc0ngI5=&O>42pBpXF8e;(v@6i4AF3e{CXGU-!Rdq4J!( z)@^we5BPJ0PLM77;22)```s~Mz;AI=z_w(IH>`!ZT$iFgP!eK)jni+hYnZy&siS(1 z4j5twXYb;~9rM8}La@`EHwlLs-k=EqPDm9rn|z=AHB2F}^UGc|drqN2np7L)_fH8E z)@x&zE#B@;f&94w@Gc4(NhY7fKLh*grs-mugh;!4-JMC=F*4(KSAV4zzb@AWBi7lz zaLrgCk!bodzzckXfdz$o&J9-a1=zgktT__w=z@PgbxY(MnttIrfXa@K96!h9r3Q7L zYv>RHO8DMX&wzGSE&(q@7-x&$f~Z^h5*y;??g&)*i|xI8L6R|FIURQ#)x0a_DNd5CR6S<1Ri(;X=OOG3Q`d2TnK|+zuN& zL6ku|BFif~Rz$;(zSb01V_;oh*#R+P4aGYSb)y_w#Tf@;fixVC>oiqq!CfSM8D7_b z0Ya#s1wVeIR6FK97JU{b+~_O^jR2#k0DJ4!B0|W)Y#6?SAX8qa#vT?6RWv(lU1OTf zK40|7Q$WX$2-EPAc*|l1S_Rxx^}EFyh8M_@mq}k*anRrgTDzSqo#Ix*>jnC6m{}AJ z+Wt=tN~F9h#si};X!n!ad#1_^0rplS?G;cbb$RxH_CXJFcYWQlAd3Z|O2i^!+9%1l zDFbalZCO=iDb;WV%bSava)SU8A5C@C6x49t-iAY(Q91ps+uVVvSlk{7K9hX@v=|#X zo==J*jkEpW=$Mb7bj2%`FRuW=f4;|AaOfCSr8Ya}&F`-EeMm=tM-HytrIXZ`C8fWz z<2|rX!9qMRiQ}}d)VWqMm+Q_~yhy;oEC?xJ3pr|B-anj{fadUk84j&T@^-lJ;AHn~ za4HHg(+`)!25<{j`-(S&#+kljGUccUTL&I=-}_OI*t1N2bMT}4;_WvL=4~BQte>+v z={l3%V?lwmGjtSTn9xvBUgz^$I22KIZ$;GJNtc@Tg$BCw*Ms4%7slp-{nY!O>@A3j zTB%LgKteGdXzwa%^u9Yz{IBsuk>@zvNVY3MK?nZ7;N$95Q!}B&`vXPijfy-z_lhcZ zvo7zQzvJo<3z8V>4iWJ6;ai9NX2J#GrR0JO%#`5LE3GS^>r}mW=3-uRrL{ZqZ&6g?Z|NQ85cgKgsrt9UiLsx6a+6EVXy1=hQ zOw~D9bA8?W^ek^J{_@`;HmOzkOHTGwah+t*DfE5^=3p1t4D?P*k)J*OVO9Yhmt-h^ z@Iuo71^USr`}pD=9&HU+Rbc({46zI|+7Hj6u1IP=x+xa-+PBM;%v^(WvAwT|*m5Ws zj}i;nkh>2v1b~ezYmtr^q4F$AY@Sb2*+CBcPb`H{LSBGDB#5qgUZwJuq3_mwFjsm& zUfwo#X?>9KvLz++8tXcXhz)Ai{Trb0%4FFN!z&io&ew6v0xdY7>o{?ui*_ou_U>pP z5h&;)M;YEV2pAXd2-kR*0Ck~|Cr8d93jDw#z;x1r+DwsH0VaeBFRO$^}?8&~XDw zgEm|ohu;rnuRm)NB*@U8g(~xG55~~B%!JWUsJj#EE>bqwOMoMxBekeH(5XGLzChBR zD(7nx>OS_7Q{Dl}KqwY5r_|n1ul8e!S<4n0ciuz)IHwaSK^#V;QXKKvQxi%zJDbq)4(}J3nAQdYW z%qTS&@-mtU#5!~OmCAU|8MTeOSVk@DyevV8+}otcCr}vk{RMtzTV95vVxIH(ZMC-# z(}nKEy5_j8KkNKI&S}q6Z(bmFtUnJXzOQMII2ifg=I^D;1D6kSFW{a>JR3X%%Os|J z{OQ3zV~HVS+d32l`z1rA?Co1|+PAa1zQx%*HMVX!&RV#f|La;UC>g3ZE+k%POKg?s z=(%IJ{d5ifLQiH8A_rFK)|JbCdyWkiy(zX)Oa0Kb12Q2$5uth8w>nwgm-lT$^u1-L zem-%!B))>PDvciJX=8u2P4aT%g`Et&5FSI3Xz$v*{}<}m8I>pUo;cq{MgE8=|BCt> zTdz1iHHMgQE#gJ|wa|ZUu(3D(Y-w?<@&|_PbBq6j3M=ccWY^Q#2mce=9{hDQwW65# zufqAT=%G@YTXWf2o$!DQ;oDuRwuZX~H*7OaWA5=?e(+5nG-Vne38P%1OM`aa8l5oM zzSY)Gyz>VqaL;kWs)SkJ)pOc*^34JJ_y6j|+^#b5Z0fvU37@I3@NeyPz15(4GyTkQ zGrIq`*37^o8txf(GyB|+B;o5%+!KyAT=qQJiW-K;)o?@q+S}XjRt~AQ9EVyj+yC~P zN6V|$#2f!^-+kPq(O>)8!mZ40I-bD6o;tU!{nf56!OM$>d-8`q#TYkDEm$}KYOv%; zy=L8T3^M02R^;%D4xyiu^hZB?Qj7mqZucWHf zr_rk^N=uFeOutOx-|Fks)7OK`X8qp&VEp}w+syU-y-&3^?C18`z+Lkk@MNWL^ZETI zxHErPI%oP1VkwUS5;;KSWKP(Vi-))+NZyDs-5A zn6)o;=d_8mUkOF%LR7B`tTXpSd@9%I{Z3!+oe9?%1{+K**fIABmt5ZD0$^c~)fw%{ zn@c*(J=4wZX#Uc^Ex0HLQ{nNU?*g&GWZt7IY=5Yk-;NIe0TZ zK&;B_-@5&D8bSQFIWKZ$-&6R!0mFCKiWH)}X!D*1?4p|AEl_nX@VB`3ybq-brZJ`p zuP@Q1ez(~qz~kQ`uR7~x=N5!pC?LU-*d9KOjwN_;x72RC$E(>j#TESLOV98#q0<R$v|)ngxm%_-oW z6B1{3jpZvH%WYs9WR5rt@faMCz<5#-c75*9GRO;bpqh>A3)T0!E)AUxs>lLHBcJ6u zzdMolVw&Ol2TGWGzBE;i^OP2N%DXMEBCBZ^Z}B1v^G~~@#tNgDa_kyQ_{*7%sYzi) z8^?+Dy?Du%wFD&uXUkaiv05=6%?o~grys6nSh(9jy+^XqW>@DnN)(*ca14NA|fwiuv-Xi ze%lCKe{@NFDz4DX8u!4RNZVgz{GU)YU*@O3JQFb6-4JDAEoWJ^?^%_^6bEAArpT(L zZy0^9n;j5^x~5}w5Z2<|Y1=wPO16H>4R&-^cHKBcJ?jHF;am&$$&$`GuC--G{c}xl zp4}^^Jq|l8$v$wx<`Dt}sip^#YS9x4F};8dp#YBVl;A&^xze34(RO3}-VF|ToR+q4*EFREaH;w08gv5Ky)NzIPjR_C_a(+m;2W+yUu#qw z^w`lyUyATj$c3>ZylCQKY_N<^J$k@ZV8wMdx;?HsC}yXpCjNu?9p=Y3H9@7ONh0l>`=~;qYa4lFoqO0jZCAd^#C`v zsy>fSK%P|P(EpF4YY%9t|Nm!OwMrLhXjwO-`Ibzc%mQl8pEtSPm`D)N|_ zZj?#Jwl2tZQHVk}o~c%fkW@;Nq>@W~e{aA4{2_MEPM`CBy2ZVR6X8leS$v zv_D}+Vwo{copUZ6iz}*^z+OAr-w57PDuYUd;=;%t6HzJBrb;`LlFqX!<37c~9UumP z_v~PKoz%*_5(5fV)kQbqrR@%m3noZj`9#!9NixY1WLsiYYpS%hiXcL?=O`@IM`1e; zCPTU|+b4&G-3k@WkQYnY8R9*BMMJy?_CJ^`g=$6FUw>YZN=7!gH|H-1U>Y&e?k$7u zcP)$3N9sUYdMtU|``P1Y=+`5yj%q_^JJyv&#fW0PM`%EDnG`E8vHAZJ7@q+^c#O@I zVS|I>V5?$IM%iz>E0p6=1GVd28WaV| z5tKV>etY25vi>omEIYQgbN5_t+#y%em^9M!tDMHChNXFK)wRK%ya! zlu>j3%;F-QZ)#`j99=q{zq0BNn_3LG+Iy#09KPYulXHEHs{1O3g)$%Uezp-IuGz;& z7+fx6dc@C0bF!z9fp*kaQivhcbYDX^x@JeZ{N}KkYX#EB?w%|o z{PwgvLB}LRgjuS%1XiKZV&y#W+4|+Md$RJ~t-i>NQpkw-V2ZN1y{oW%RD=YC@re+H z{R_Uv*0S)#(p0tFYqY1|m2tmHdB!1_N(YNQsYVs64--VYi;h1_fw@oQW$)HJ`;e0n z!;TW?SMYli_Vu>?E2Zd5fqBiPjrEOAZH_J+Dz4XMKv<>kp3%U(h7}3dYprlC*HHttT z{pEpj%C9=ZygBE{@5|92pNRWP3SL^gX&yQsb$Nx3c9_F_@S?Ywst#0)Zu+%piHp9% z^iD3PWNN(1-V`)OvkZqa*SjGn)m{sywf31cuIl^Q@!yMxeNv18;x^_Qx(StJAoaou zr>>m-$R4B4dHgN7394yvQ4^|hXw=_)xdFj2&#M_YdG~mvG6{e3 z$Wzgda0{je;rXX4tA-~^?GU5cpFxW&@6k8rxCQ$c?mvyZb6xsuRk@~9c}8Ykp2e zERU%=Bu+3#OG110cb-OiM>x%Ww{EM&{&V41wJ+cBo$*IPDGGc`cp=qQ`50cfOuex4x`ht{)_jvybQd#70ie2fjmeVBx zzRXTUdX(#qAM;j`HL4P~hVS>8+?FBU9+gi||CoFjl z6u+a*{PDek4{^5&FiVMv7I3Sw8i&MO}mm;_vB;*G#-%0SoP$t zOG{_&OfI_yX{fkom+2>f$m%qUDLI7HJCE3)^^Cwrxtp zrxeklh!Yc8_WAS_9KuLe*oHfa5a-w&wr_oZzQGVrGa`eg%=yAZ1(r)kz1YyL`Z2XM zHy~8-9f|J7V+pumg)?j_6mP^+aMbe8;yOF)avIn1rhs$oiD&M3*46 zK8?N=etHSkQ9+uPDiDm?DFIj*EIBlHFA@~w(1U5X(_*ZqTfynYu(a2G2vC^gl>(WD z(vQq>5g6Z<`m_VgQk3TsXEIVGF)EkA;!epJG$#I3qMVeB8S#W4x)4_YK0o^n8lqK0f+0TQB#N9mTMl~+nTs-6W(8Y?!S;MlBO;2#eCDaf({yMspok}^ z8opCf+&i!|5ZZx<5z!I20W9JW591@4q)y4iD&r04QvwL_Bf%@V5{JmQxA)}nj9Raf z#U-U88t~U3$H?+;BAZkqF3zN8@&i`EKFbS8zHf1mYIzbq+EhRYt?wrtnZ4n`Z&9E# zUU%ki$qQ{c$_Y9i^!as6xYU0MM1Ir4u;)mgYrFeBW{~;5xSGFE^dRd5WpSlvYV4&+ zE5#`P$*AXoIfb&CLk}Q-Y}K2eSC-~3&H)uEOSHl*BsfoFG&qr{Sf=`v`1$lJv)K=; zS4PeD)bv8hN9Q-6b8l zbS`mH_kgEuk$pZ@eFgJTGWmURoXpYRI2EjN=j@E94nZ=eqll2!;dea=m+hH9>Qi%v zU${SRB)}4_rlXBYw6*)G9Goe;w1?7at)`u(q?i)}U=cfL6$l&7YSDv#;B8!n!eU+Bx49dRj18S$RzgS)fz z%}Ljk3e`SS;q-dX>W)KNfq=xUc8J-0mHH#Hu&-OMozFdj+i0_KgUWP8$hMF1M~^nW z84`Fq11u-cX~ae@b*Q>yytH&tHTj5Xl5E@SyR|8;^t33{?c$#slD`p>E6(3#roxG} z(I)b1v-zGT*~&s~(V>sq%U{>5)NU9lh|&sTj7>iLBKES;m&SX)W`-{|BA1CI8sx5(%{eumre2=S37 z1cD{jaYjwV7HZ>%F*59UG;bk+2%FUsLNG;&Nw^V4xCF$)iMQ|tQo7UqSJOsCm;@zi*cm_UdlH|GuAG22a z;h$~jBR#Gam;|@Zhp&Uq$q@Quwm;B~Z?e1`>T$^HeNz<+wMd6aPpBY#%pdh&OUfaje@5yo(yYq=nW#I^vXXq4E*GpT*y}0_+h7Zgbkq4cL zkdq|j0;CKLJ=D)wJ|MHhN%#pwOP*1&k{c(|#T`-daB$8IGGajkCULGl|F1a?T;Aq$ zME0T@kb`< zn|E2Jid7xO9h`jkzqTb#S0?8be-{SCfC1bo@i6QPa4~D#sX8rUW6*|?Utgz?dHZUY z##CB8h}T%0AzY3q_C_vjJJ16yp684=ez;%tG|%VCuSfG#IyMduy=t&jJsa|PK{#b| z6z-OjPNyjHrrNcJQ0&ugeqRy`U!~#qI`6siyM;{)dU~a`R_C1wowIR!qjIz#RR*#u zS>3y<(8D7UYmQd-7;W$yF5!gAU!BVQyb3?s0pZtgA9H73M17SUIWQqC*6a_x*Jv$> zz3ZQ_vZj51mw(y(N9NNQ83SGSQ>g}ZN|D>JqJ5`NjQFWqo_WxlzmLQ!h&=DNv$y_%!FVbyk0 zwNPD()x4n=@uwg&05(OPmjlkOF1%~;e9KE*-N``+$SW-IDL){0CLl!gHk)2U(dOlp zzURMJKbmpM8^>Y@q36D@OL%_r=m#c-(LufK#BH@&W8<^(sqYH%XLExfc4WrN>xMp6 zA*bdv?D-$^<@iU>mW>m?S*VTo4;0+J=DyOb{pzEGmv(CIV0H6yqGN=8A(#+q$N5ex z%dozKx|M4lpVEV^%JReUF4kp{mEUt3>#-5f-xBOLM-?ZmIg4=2ZWF%EH+96X++ev~ zfMG0Hu0}*}`97jR;;yO{>9khJv|*UywV7)=^c359nmb~4A$n$~{BtvXJ}v$4=l*Y( z?yP$#`Y>x<>W)(zYg^EmxI_PO_|5o*fBcr+BK{8vN1nBAqmGpjd$8eNvkeF(@iTVK zVIR{Md)A+*^gn;&tUHDm)_Z$4xJ-O=Qw-I9#9Jq{dyk4SiD9Q<5o~z8j66H3uv?h2 z&8MT%G?E(?l8GdO5+RwZ87ZMOkZS$FHk8}M&BWXKwq5@2gE z{8)AqH@UwozX2g8XxsFwQkBPFJX0m@i`XyK4M%d6`SR@@KB|c%dY%_>_4Qhig6;Vu zAVm^`I!9-|K9J2*t2eJgc(0X2i{HeT&(hj3^-~eSw^jJ=lS!_mjl=I9e2`Xa=oF!H%{lh13~;xo0J8Sac&>+5#o>Pri{hSu58 z=G!d|P(HBsy&E6%`2<}hH1CCO(O%b!_J)_X^KWhL6}8l6Q%Y6nuhQ7Q!$cRuoW~($ zYm^suL@By)TDca@tz52}b>Yrmp^5i5{its6UxZPZF0uGr2Qg3K)W6bbSaWc%6Jp30 zA4N*xuF>Jo-;%Mb?^N3^AShI~GA zreXD%I+))Vx|GOy@9&QXOwT>()2r>A8q5yuCBSHqTk$~z1aQQprMT_o26m0m$7L61}*IT?Tz{f zhd!1oZ{M@5sLv`_d9ug+iIBN>S2bp$eD)RwNL{rE%P{k-;KzGt`00vGn_h4=*}IM) zA=Psg>N%%8PQ*WWH?`GqxcuUZCy#d#(z*(dM89?GKd^0M*MSk4?Y|%E`$9A)1LFO* zhn?bjuobF_#CnCJ5^sQjTqdpKb&PBk&wzHIpmwAw7uxhbvUtTJh;c8|%kb9mM6AqT zGh$qnJZ1x8WmRPc@m~9;#gN5T13fb#0p^4(FSiJ2ZCjjQ-d~vS+ZKdIiPqY&IOpP5 z{c1x;=S+avtcfC46Uxg@#78LgMQTV?EMMC;9AVDLn#jQ-#A&b8C6_t`Yu|q(2C7V( zI9~n+YP^L;cR_ybr5n&$q%hSfIHEF14vHl2EqbsAUKx+~f zA$2H)DWSixEIi0jtfC;G6l$3cRE=&CW!$&*ofi{jKTfMvJH?x8@}w}!r<>0pB1R() zz9;Cqbi~eq7EXe#OO%2%Txo6cgokRvon=~)rFsg)D%gEmvgV{pTn-Z<1gWvq8*F!T zqezafB=xDG6O}~p}HuH2JV1rjmUmtLyHUy9CGFvYsqopQZtT zS%yR-Vk2`d1K_Ef90~TahcuQV*(=W_hzg^J>R|ab5w&8&gYycn6llj}FuxW`hxngvg8Ai`&7rZ|_S36|)oHImpu%W-`8d zi)7TdUaX=foGQ)7i_wMTA{pS|Cbv#TI~CBS#sEL^rF5K%jfaqUC3JxX3%TvSxm0y- zyO62CAp}EvZ(VXtDSCTzt=0xB_{dx|*;cktqIum9;7$eCh?b zCRMz`=zttUMM)~8mY}=EOZx@Ee8j<*ujcAlcJUt$RZHSXmLGw80^2&C13c)+Ua73X z6f((#{zfCt!Bg;2tCpAZ56Q3)PZWUP1U?kgi!H7@IF72@(=Uy>xvSpJaHV8b*Rx65dtSp7q>yfEqo<-%%X z*Oj8U>GJgQpKS}5=wAbTa_SdG3w5yoQlFpNrd9uL=bunu^Mf@V6sW|1PI8(n{I1WR zzIa-Jy$VQ$z6GK*Gvl3fajH)>+i%Y|-T3djn)$xu|Hq8W&5eFY*Ln0_%JKyo%_=H0 zH#l(rddf6eUkK>&R5I!zTem@j)`1`gLciDn_@LDCNgdWk z8{e!>hJ8Y|L|@N$XmGL%%A_b`bd3;&=g6@JRge7-S;g8vAYyH|;&07` zCayi_L@I;BRSt8ytBAlH>!`;6XWI)g(&AYyL~HJ`p%tmMFrvN==zJ-DMQV+BUgl{)I%A}OF8H(PvAuVsuO zz0}#|)n6+0*=+F4(97W&{6u&KNsr%+d5Cs_DMRN6jzadD^(gj zAZUN$7X97a#c(y+pRpnRD2v{IK=)M%#QP9%FbKnd9Co4uzq z|D%+5u}4BKPsCVNF4OF{GaabKn#Sh&9&)9uT;zN1%Y{eP{RbjC?7^V(`J2=_pgz&l zvBKOc*`In!RoM z@RsR=pr@9m|7J#fAM4cvA|&A8`kizS_tb< zZyv66#z3UN=Bx~5KDI)P%3n6_r#Eml{2V2G2OO4VdvP5dB8R`~l!txOlB-+_`A8)* znxsG&N~kjxpy*ER&zR4cC{OH&m%kYq**kP9uTX~3hwzS$6Xd&l9J+Xm@_U66rLat- z>k7L6Dw8J}eEVD~EKvt)Euo_msu*-Y51HXj5idvpUlb!w3``vZw(M^5EWG3a_)@?X zWw|1ql>Why5CrdYJEp5WT^)if+t4vxg#?DMSlJf?!$o^5`LwC>n?waphJX*G>N<%j zNHTEZ*!;zU;$K1N>W0N9La1(~IZ5Bd7?#ylTuc{`7HMs$xBRuvLxm$g9lboAS{6-a zT(CU|t;g3zXYf*Ge{ACscTuEMu)vrb6c9k|=jmK1Am})rXA4<%e1sJdT+H9{a@2dx zQYt7sIo*8xkxB<=?j-a@@juG_qkENY7B8T@_~~vVe>2L6GUHTL+P_z(HJ1E);M3G4 zW8==Z0eA2_&5d`g&S)^}+iti4({4NU$~wXHEh%w@LFi@^wapDqPt89ggP4fJx2~qe zo@e-J-rDb8(d%t0T)P@Z%rkbY@5NkxH2?OCDE834ruYH<(j6Wl?FpY^etEXP9$&vO z!ykF~IYrj|%+n$7K*2pxv|0ko2aerE?&E&IXI#m87#7_-`1qq+!s*L%?OW#GeYb0Q z!)?nV`pK8?Uu?RV zfIU0jqEI{XH50-JI-K63d95ym^HOix$4-;~6UxC@-(PeXOD_6RZW68z(}m6n`h2_boa!vwwJ{g_T?ZakMxNjM*P-2V7UE zzk0I9@dGj%5&Gx+s!DFR!=UcFt@QHqDbp1+BNp#nz3k2R5YN9Yqda2+{nl^?1+6+x zhJVJP9@RRN?F0tgx#^^uy8fK6MEnP1lhX-P%1b;pe<111rcdM2CF&}JCgJDYyI%R{ zWG~9QGj*ydG{f)A9KAN^uW0qGi<$hw8eN*(jlLW9rR1zxpFp(n-l74wcLhH_(Ht$BWkfvJGrS3Mg z+D(kRbKD%(qn5t44GvuHqX^Q+tGAMya*txkjk{jw`$4ViinxkW9o_KVm`asArXI=s zcDP1eV@i1ztfs5#1oKiA7%T0MtW8HF?>$dC8?nEJd_Tqz8F5+puJgefX7$d)e@w#P zcJAFAD?gaCU&0?GU!Ch)ikbCLmUYhV;*_t8y1mubWr0+U9?%7?j~6ol}|c?+jYUFR*wIQ`QxI?7Hb-)`4E+}KyR>U?D!o0k$_wqYm@EQPfjVe zeyQ!$+xq8!2r7`XMSiR2K>4{O6XOEZF!NQm|1xwFR@B=yYrdSOxcRHLfm3LlB+s@o zu@h^|EY(^~=l;j1BAoSk{sTkO6^C4src2fG=jx;x`cDWFE_b+`crGOUiG#MX=QlH# zU6O66*H%^~1?qUjG2=FvV^%(W_2vxA<7)5NmS-o*jrRH32jZIzmea!hbSx>MMZA=X zfA-`K<141u=)8IPW_6CBJ^$oIY~XXe@cUK>rbi{0VF?}g+fVgNlOo}Y8??ge6%1ClpR%?|-A$FW}ap%ilrT@0* z%rPeFS=@9vlE-p?jZBRO&NExHLcs0V)Mk82;IR=46g3EF4AaPM9V*uI#kU z-WA^c{*k>AOKaF&MGZ?SOE8^Oitpd7>G{wk2IJU;nc4`l%i?MX{c&+z<=PI^TU>=xO^Z}D zqcxa5ci6@7O827dh5Y?~pSXHdUCq_{B}-SCRaZom_a*v!mO!NZlC4-7<Uxkt{IAq@XDE`qqzd{Dyn7)3?6PFL7CWBNreK(kY{hfS|Z}R0*0cLW;XP!}q zdg4c@SmlXf_fc;qiqmtp zc*391-t5+i$ZKXRNRUi+OY%R&_-aYY71Q4XqOob)?^F8;?1**eQo0Y|-aS6vV8xAO z)8fY*9~Do~$h8!>ahX7rqwPf+uxQoq5f(`MbKw0?-Y2ny0b-v-h6Z*>jVU20+t5L( zHq2)`o`})! z53Vf#C5TmIfn3iWFGh5q@YR@>Je|PBIm%z{R1KO3EyUAQE3M7mH-`j^B3gK4=V>wK z2V=qqke-Abdy1zOSBX0<19M0~?FW}OPofx|Pp*t zfXIp;Wd>-<-F=ighwtG@BqKfh)J_1airDj=+CgUG6b#Ce3p`EdwDw;3N9FQZJ_6Mo zy@g~T+Twco_Iy#Lt>4%H44f=z!!FE|<*lTXAYM zI5vyg(luCSFw1)Q!T7?(c0PzRU3euVRQazU2cVK3y3<4s!P|9yGn)Z#rT49G+nL+g zugXvu{l!RE+Us97cyI{uHCh^%e?JBoI4uLL@@dCa|2L$u6M{mi=I2C1Kmq3p|BBakrsMa)rnITUw5 ztu3-og`h--^=77=hyD!A?I^}-b926iD@ns~8ma?UdAi!E`L9#tD@CmtFip!E1Ifku zB2=Xo^-Icyf!AvUFp zhZ&`*<>=ihgd!CMe#{5H`36e|I*HR-g<$cx2QqRAn@(*FN1Pdk!F3AQA;2C&f z4`L-toEg*P)1Y&g2cHN>)qyI3R*O*ziCEyS7apFG!Ztgrs{oAgD$-W+CqbGL1Nrw0 z%N1rnPT;Eh3wPHdmO%C4DNXT_^0-Mc2CD5O`CVE0--FOP6iKl%PeV#^2~r!*CNryi zBK})(bn}hxHr=V|-?3GR;Rh;`O1D4cPS4+VFRc7}{Q-IQUfsT@(TTeBqqF*pRt@H4 zjBJ!N+@2`5YniTyo;ds|wN+74(piO}e#d177R9gm?mo0MarRqz%tTdx#p}Dro&V_H zl^QVJt(2$4+I^=B$JY#OnA)xBtKI+93>7kcQPRXI(Si%gy~lA>u`s~JtG+eb;+5O9 zvuruH+gJ^$NVqc8X`}skERT!nDM~Hnk>JSb;A{Tsm>bP=pd;hn-06NEuJcyUaYg>> zmxIL373CT&GnKz|wbF0BiJ)lF7U$!SS^w@F9y&WYT?!n~mR0(S-G#k(?E8*HbIh>H zGgg@i=+9v-z~LMCTzpHLNg#v>uOF<}zg`*pWmhk^BdoqBt3@7VTWN?o=~kJRYLdVF zaTz0 zOubU;wAr9S$nREOx{cxYeu)ux`dAG$;fkK+bwA{$7hsYp9@($L_K{Jk^emrZ#a0u7=K=P%g)X@R++c{8?u&|Fr&F`P`UFGP*=Q&t8ED z1H;|BGSK08YS$pHa{O%Oa zL(REc;t|5vig0DT;gVyiw5V;3B-G<8=P&<2LH-eLUxVlW5Dm3b_g56-st+M6@10uF zqG_Wnt4d|BxtIH($0f*qX5s3Efya)XZaq&n?XkNT=JW3Cg09ccON+2MQ`oB5(VyIr z?kFo%O_R(Xr{+FfiLaj)|9(A9?xnVfJ#PA2RM|L6tpq`+XQWpXN|CFRk}{mpfUzFA zv-fA^lSlJPxaa7fcJ0@XQ`dG|)b08)P-`=-jAfljUGB9buj^h|MdX(1Y!6&iA5n^B zmKb;Fn=KAVUv_N5A}D-o(XLKYBWmSamDhnuyUnT#YK%0l?0dXxXve%P5X88bZCq8h z_u3`Z>6NmpDF>{Vs)$xmjM7JK{ZEP?{99o@guc6bJ~7|p?d@Vsq%UjuTzHVZ!rqSC zQSe-yV*( zS?B*dz}CAk;L`Sl7ApC#X&_R#H+{j*r5Ei#sk&{|Vl8spyRP*r*JgoBTeCVjlgc&T z(Z2cN9lxTUm|1P}6lZ!)pObDRvwGuX>piFbGWqk}JI^iXSUs?%vqYogDJg6bZRfPK zQ|`LBQE?_-%7a$O2%Y5a#+f!V;oix>ip!T?HP+rqeX+~>Me@bR*}2!8CuklG_BznW zT|%m9jgRO9p_ijEB7LK<8la9Uix$dH9v&aq^d)*+G$x&}$(2#(scjWsbxuH)-;Z43 zz<6FaWj63;)eJADw-_}yOb`T?CtL?6Wi0iT~1_&$=|5WY)IOe^KZ+q#(d0O z;XvSo>UDQ{F{k8t7W5MM<-H!;q<(B=RcXqB)<@S)4_6Pulie{kyD7%CJ9m!YU`prf zm_vrEv#lTJebU1A`*XMtj(@(o>B0?T#=*2M`90dZ(&uN!*nW`#&4Ke9-t;_;vFC4J z`lyzylD+TrY)jG2eU`<^)z9LuTYMo}^A;2x4WPMJWQP=^*}iuP3<647G^h4AEA&Z8 zm9ft-?w*M1KcqI=B@6S5ng~?~hM{1EdA`MHUv3k$X28sZm%uD<|mcvrGdXAG9#0#-7j(fT%>ZcV*?Jmp2AN z*!Vcr{0c<^TfthijBdhsD4Zbrmr7 zj(RyLl8d8D{4FUL3cfPup6(X{8~`#SNns$XYp@EYVnfD&uu$B=T5016+zoUeO#rov9GYsd-QRYQu8rV=Jx%pN$R0WM|LQ-`(vguQF~nW@ zQZ-6Q>E%r_EHGK($>s}%KY|dZm;s?iqR6tvIl;xyX~b;CWdogKW+1W9WVnvhz;NzK z`U&KVgAa{s4`7RI=+xeQH69Sl13FQG;9J9;f_wM~MIxLsPL;T{p(lFsGTbR*457UX zcrZ&ILTtc+xed`=a~32gT50V)!cwRmH<*%=vf$oi|K zGzpJ93y%wgEqg98fOO~!SyW&!6-$f)amiv;scaP77@8gpHfg~?s5*&48qjd>HHqdz z(h4Rr>RUnrv{o&ZkV|=(5pXQv!7wOQ;Kgm|Z&Ha2q8_=y_)Y^jeSXBmh_{MGa6U9l=~4d<|Eg$M6sd}f>E+=J-Q)$* zxLO21B^&io3ZS_Vo=&=R8*0oG%}22FIe~Xpb&y(K^;Z05)hXi#{N7gM-J+*fV&ldZ=)NF7ptn8V9Wqs>RiaIR{7Bf- z@3t)XO_3SFbM&LVgxIKx)3WCWd#6DB109LH*WOhy;UoMZM&~5S2ovtuD#TKoXav=6 z;Q!RmQW#Zev=A71{s_jkobZ*)NNax!?RNv^;`Y{H;5v~uhX5!)FNB8`HZvQ@hXA1# zj`}nOO!X5{SyyYF`SvAeS%1Y5J*eq2{Z}|%Ot~2O%KWpphVo5vUV)H*wf7XdD3icT zkp+-F-_fzmn29t?3`%F3%MxK7gUP23XLuddj4VKEM>Luunb7MEu{w|wNtl))C9^?T z8G^drb)r^*uLnpD#5eSLUjUui-a+MrCHN?|?y;ngFx#7%6Cn5Vr zs6iwurJj8HBhf%rqoZ~am+_UhSU`x)K-D{n_2hh~Vn$zd#&KMA>pNPY438}iLO`tO zl``Tk!Kr%)GMzV!J>YB0s@Hiu=(=_%!~lXemwGsPO-SFdZ(1`v2Tq5n_A3%!8jL86 zqlbrZm$4grU6% z?a{4drlh`HxsKVGa#2N#z4<<_`o_(Z(qi5~&3VB~P>cPpjK3Q;bE&P{Y*lXB&IXI4 zLRZz~8z~RyI-vpVF&~(bV{@O)9td5C$^NzEp+IhfRc7C;4ykT@@iQ#BWQo!W`+ke0 zr{LpHk1vo8*0%>uIavMnq-|AlkL>eFZOM-`j9%liqaJxF&-}^gJ}#S1aoH=ia$(q( zlj|b1CjG5M$tk5nIdcS|wTo5Hh5YL@7`HOy>dKgayQ}u)KE0i+OGdp)x%DSan`nK# z=P@exMc7%>yiHv`5!bDsrFBc+Zf>~B>bEyl5ez0@iAbS;bACK9Lmgf$jSJ0C<89RK zySnndl=DI~b(Yy;oE`1C2KU>)ds?~nT|%q}&YP3p+0}h~{6f|B&7tO;3I3VjiCgE& z>e|DPf4)aFT9$RMw)%O3zoH{A(0ycm)!fGzBxFLwY&K|cOk8>hT2D2~bVy~;q(FQ; z=w0`lb=jinXV>h5}JMu^P=%RW-_ z?Ab1__q)zVoN`THR?{(yFMGMpa4A&9#;KVh@N2EKJ1^W2(%8X2=)S>BV*m#`!+8kA!nuHxUZ>%v!$Tdx(b|#X6zeuwVzg0M<{_%^dhURpJ)A1q!_xr zYV@dq>92Ap%~I3}tuifV<-5tV(zNKDVv{005i%mzo!^4Fyzn6TVDHqGlA+dsF6;je z*_v0T3J>f|zGU~YJGW_&=Ad%nkW;ZdKih>V zQghaf7+NU?MRuMlgGsU$HQu6UEUq!Ll_L9?$PGf{Hp>X);))vNZnPm}kJbP0bmE$`iI(>pTYtv>Efk%FUhpvr7YZ0Hn(9_IV8 zMfMPP30mS&pByMXZAu{4xt61xmB#Y>orY>EYl=TY#`d^bi5GeNffdG#HEWKE_gTwj4Dz(Ie87zz1Lh zZUNoi=2X+2Hg9COvil$N@0O95yfTbQ|n!VL0wj8EH`G;y};I@0=*eoO#=ZI zP_P=O%v|H7jqFp{>AY6z!>>(5Y(5$C@Le5zJ9>pM)t;@9sp$EXklaCipU%XOdr@Td z(5w`M8l{<+k!#Blyb%iumssv%&G1{Em%4FpQ)aGoZ&>r;#MG4~_b0B*k{%shBxE*N zFG((rQZ-__71Z?e=6+@U-H*qSk$vKc2FrJRQ7`sR4$bg=t2ch`wMdy4|-;xlfEKf$LHjeSjA_bIb>FT&L1M2D0gaGR(YF!cqa7nk54> zOb9q;wg&?csY1*M5GW#{h*iXV2FT+O4=>nd_QRM;8KH%TJ3wH@p=hB$ZhISgSS@F= zmF@@vu9dj7AgCqg>jsbDUbPMCBzRbf?uA?>!vG@E#D%e`gbg3Usa;v< zc!4*s0G9uIAsJd^PY*Pn%H-)lCF)cy%eeyqy;&d*?5++XZPn}WJmSTgejEj{9|9x= z!Cj}|yCVgXjRDvZ9dU%VzZw!8V}M*Tg&0>~IPoxxs+L|Zp^SJj5hLcfCycs5L|6C& zVx<(y6NzB#DHcVDfFFo0Fw};0Fy}V5dp}SK6SVLUJ{@>Ay1%uht3P6jD-)@rc?ffq z7VcyS-!nlulru?HHIu<*8G1C3a0SSAZm_$=tpE|n3Sx6io)wk=65{wOuvh*nU^E(3 z+I?Xqk|e3ZRO0GDN?4Zb(~|>QKOxJhT@+mHr^1inFJO&oFSq>tgXUH_Mls~VZ*thumzQz5fMO%s)Rz0 z%?B?4$JeKX<;N=+d+bWuJ(ILD5Os~HwSag3oyT?0uxNmaiWn=wwKyJr=* zp~2x(G9^K0w7~O-@$HsbCNcqf*WPK<-KG=>3yJ|oj?o+ypbmxQht)0k{-9M!Vkvlc zy(?!Ai%muK{Zej^6M-0J1(*@4Tj?m-nv*!>cu9d6O_4QQ=9?ON%)PH>@AXN`c)IG{ zS+`}Qr#6M`8QW}lHESSAclRTmUU`I$^Jie4CqsH)Mr^9l_j+VB5&FEAIk%q`Xbv?@ zsp3EDwE_Z_!faty7mqo25NW>j(Q}_&%0Yio&*Jtg?5^syFNT$6e_qylKKzerq-yDK zYwH}=75Y0)(CtGz-w2;0ngiLj}et7i+`cXI=lexhmq9Wl_=AH^fgI`hd%$meU$)R+w(+Z#gvl zBdomkxM1|=%s=Uu974XNS3X|%DteVs6Bl_mKb1oH*>S|gc9hZk;%Us9e4@G0?|2(K z^-{!fq$>wv2~xk|KFtF{KiyM1i|*=gItm?D1int1mM9{wHnQ!M{(%z|6s^*9pdjLsde0?LP*Gr^<9uk8Yn1NM{KKgv@yzg^x+=#@@3%&Xp2dl;T)vYuj&^7zX!KPop$N#G6%Ou~0_ z5&O3;pz0%j+(UY@dd1a*DO9xbWB<$97bmMft0pD8k#7-?C3OnpdyoDTwGI6}? zR;fDer0zlP=8Rm9>8a0QShJ%3Gj*jGRKZEGC%(=@DGFo1>M5qUw7nJ*F>SLX_CMO0 zjRO9oij`*u6keSVuRU5UFT2rASaKQ_njG=dkV$xd`n&p}E^i=z~ z%l^A%M{tfArEBrn=708XwBc_3W|LK-;&qxWX0l)1JbcV_J127QjR@MLMP+LwYp8$7 z^Ko8{@#zcytt-rS*k}6he%?f|UM3NDu7NMcxQ~?-%;g#eK`4SL-mwgN#dA1xNICRK zd8SHSByKAR*~3*fAZ8e$v>Fi&N;;v>o7f~X zojXv4mD%EDQDCrVQ(Z!`0}UkTA}z^c=$}{(N(c?vNQVroEUbmGr#QASm$nsJ=T<;V zWK6(_H@ptS(GZ0xhVC;{D2<xJwVQw5II8TAcSc$Apfi@xyLj8zi<3Kry<80IiF)F zlF1y#)W`XJh;k@VY~*~N4o*>P%{iLW%I1)eB2tPphYB;Na!SY{$@!4f_xI}eXODV3 ztlHkY@B6y0=VgauE3QiRSH7m6rlK_WvgquM2X9zSsp41Mt{?VIJE+?tZ3~X9ofo~z z#d69(m&U9E-0Q3D!QO9%-5Hi&iigV4tMv!-XU_Rm$FRmtd4M>cxh7k zQB3z(Atke@RFGHrt=!Huz47_#x|MQct_UNpwe@>|#WC8;{L?(Y`s{E|q%Yk4No}$) zeMS7VM4bH|-*RHbUd~e$+SGw__~a)57#%@E%Z9y0Tp>vtj&gKk*RtS@UGvHkB>e6k@_m5n(pKaqOu#lT5B=8FnZkF#W@cr*KaVUN*iR4kpH$INM`UA z;2w9Cu2P}RQlbO0gCCOP%0mGAn9!OP{}IVpV8R;C=r8J9ANP{IwnD9RfVQ%^ zJBLoblRAmw``lnIUlzeMWb)vNJ+) zD<2q)G%Ejff$pnRYl7f7 zD-6UcdXOMov0n)|NqTHKq)loP++7z$S8o&~nnF-8B66CPJV4a$Y=QU}vGdhp<9bvlu`t^&WP2;h zBzweh(xv%C;v#B=!%|u1+mn(Bs(>4zae}TSd_KwWNO{VEC*CnN&hNI5;|FNbGvZ5D zQ_cfAdi9?hJH3w7-ZlI3MP$19{?Opt;lPkG-pGSAHdIbp{-TdmZa7scO}8 zZi=aqbs-}g;AHu?lq&v@b+d|32gv6dwLZv{r6ky_;}p@~b1z#$&Ze~!2)CHpGu-w0@9$0WJxemQ z-FWjRAl+qkvLNWP(g!g<&hUAL1pVXKtLc~NklV#d3;Wbpp9Oo}D5nkA&Pwh1J&#;D z&{u)={{kHK1csO*nKSw|{AJ*+Nl|09Rn=RwKbMa@Yr~wybN9dR}M7M;lql zt^t469>&~uC_8voPkKrJ_uG5g@11wZ^W8+MK>v&oamMeif7d?R(p2FDY7IB7>_j})$3*5sM4 z*1NeTP5X_1JOQU){NnK^YuzHv6b18K4>OrhG})rfAX=R}I8j@W^R1tF$)8MEz1E&n z;i1J-QV}My46L+DitFFN1Vyv6aHsg;?S`)lTeqHjA1y6)rREoeAhC=;*F3-JNxX~A|>yyC}Nh+;}33-*SC$3 z09t0~mAFQIH*N2#$m+T^o{j7fO84+Sc5kx(+Yd)uPcy$CPEVjU+YVf-G`|CveSn#~ zS^PkPH1h1QnG2>D$zHlzn|h=Etmk<8M6^tU>>bfw)`CgU-S!#-w~Ic>c1(!m&(l_` zdGDOc%X{bY^Nt2M15Rdg2Xab^CEDWv0~H-{-H-yWPbED*Wj{$ko* zpeD19+U>lPPCvtJ;JH#e;6LG;)G!v;Y{!SpNX!8-I zXw*U8rOgr@jsr4==O(?=cQ`S#&~c;|1!L`+N&4s9PI^|{Q7~g`=+SH~b)G5+v&>f^fRAIB*V|0~hxA z^++K*ExT5f04Jm?U8zN`-}j!&s4!%*n;8JH22c~}npRM-txAI-X~U!wun20unn0{9 z%T2^I7a%akBFc&^ld(LL8A<$xg6Hegt!25>MCF<; zgd;))TR_$vI+;z0zE*iW-hs_=aDX@)MFRt>?=aEX$}F~wJyf41fbUc!Y9(5LgK!su z&zE>yl@tuox$@-xxoxmKdZaSJ!K%C(P35!$#y-VmZtaM0nwfrd!$(6m~j?&8A=xL`$ z3-J+h?%TyZ`cUE+&(Z3A9Z8S#6TKgw7@eJ`TZ@lUS0DdT^h|fJ^AGyk`GP3ZKXSd*7Y}TB^Z$(N=Om=(@))2AmunP#hA+)G}bL?}z z8-7YE>hTxRK&`gGZS`o*l2kPU?|bGe<2Y2N=gmL;dK19heLodq3`1= z2bMe(zi!rq4g}FdkxMelw(f1mqOY$;sWf=c$n4fUzG{>q{V@1ifX4;)K92W{R}5*> z!|Y_ZqC$dRO&VkLiUj_qH%XcBd&CfqXazC_umCZ^Y*&oGwkQU6-jpo+CejxZ6W7zJ z9I~gq=5JDo7$y4?NcJq-h)IQ(XM;Zn-OUh{?Yh65w6QtE;#7L4RhxN{GL&SVbfzU_onet#Bt=+`q@>gJ==hO4I^ z{txnaEEhrxIA3DBof{K$d{mHAPT*p%Y|-ALdDck3KZH zS?}|2-)SpF`68gAg-!%*$^i#6suvz5Q9Q!{2auur6@ zMYF}}3%C`qmrz;r>ohi}aDwwomT$}PL%;g1$#^6LCTP+eSh19ms3Ak}1IHGX zgB<~kdhXl|2sO(B>KHhsq8y3m2ca|~k`7Fb9CJiR23NvCBBwYoI3j#ek)b_tm?n1PvqPgxR2$WMm$o^02U802sTdr zEO0G9^c+b?9m4W3#7alO$Jv~NHAe;!9VZFBGPapv(2e^~O~En5OTn&}MsWu_rreTs zD+1h2z%Ll$R1ORd6@`ife+UUzAOatw5FkdBm!r}EdlnJR;9x=k%P@TRAqYVc|B1g~ zSKjX@Z~6c!PkI5k;<~6?)Z#%0*s@t{DGI2i+6*YnO5c$M7P0(tf54)0;FOvNzX2Ii zw18-}Nn_*nbR1YRZ-K;m!~h5bc;KMOa)rF8Gv7PeGB)gPE#EhuM8JlPZuOGQX)|00xR60AgizXdJP?(yUOlcd+}w5K+%ZJ0jhmAG$Z2|) z#3$tl;%yP(r_vwI)Km|PRUNDpTz0g9z&NRW>c#uQ=t|-3)i@E!KR=uz#-|fhHXvEu z^Fxv+zh*n$gTi`r!Xt`v_@cks_M&_ETf54F#dfGBB#NCm2)Q}7O()(i#&e@+-u+pD zr-gY?KlVY@E263MIu7bJg;QW`so6tvO=>l>U?Y4iRJ3NND>(`aG7W5S+zU z%)$vk?gpT}j&UXq@Q9&!qM$pMsfIR|C%u>CXch`|-DEq%fe5gKk7k#2q|@1xDUf!i zxnhg7J<1#Gj~iIp0#XVH#Gm&oVI035jNKjJ?vUMm+KM%Bv;y5qzZh^nBCJY5Arlg2 z!Vv30$@Ui7Sx{kS_6^d44T@5Q0?2I?bX;i;(8@WcZz3S3DH>!VBY|@M8rh2s&@aY` z`8L48Rf~2-z*bIwg$s|*j#Ep5pNe;!mKC=Q3LSoK#UpjNs#r8kqo_`@qOHdGTwOfI zDzxqSd@{K~QD2ZJfRI`mYx?o!L+M9(WMbzv8H2&U>>T5k+1X1jul!=Z?@nYOzjJw| zq)O&b*c}gi^U68y;WN{bVB64W(WjG>oqSqZ+TDzpy;6<52PbFhml;|z!R3)848P@Y z>8*8--<>-0GcCsj-3`Lb09IjcFr;3d4=8~>Uj2a}m6<7{v z&^Ah4RP?6H2T@I1>LKz4+^T%*IR*&_vg`vkWx4PN&$<73bRoB-sI29=YkEc`Qo{{6SV^w{x*WX{W*-mG;F`eJ6 z``kI*yK*HqBpWtA; zM&!|(0S|I}E4>vH@E9%JAbw_|46d1`WLpmG21-)P8S$9nt9hfgau|+t3ltpZe%+ax zec!TAY#GRC>4X}tbN(-{tsA@nOenSM-Zvy%#Rn#@VqfNqBtiEV0(oa#2cuxW7sq6h zsxr(aZ4ENCNy4Tn3XGR42d~$!CALcOx>(+~j=KVKF`!lCI3i*=L&hEnSz8M_l;wH{c`Jl8}C2@J?)1=a^=%hH%C-rIf^F;mAN1>o| zt_j&OwAe$4e7?Q6OdgicNgrcqI!LF9)QKh%E5#ygPMF6sqO=1wd$W6ILYAFtm$MtW8}bmmnE{ZHxp^m&e%Fd)?( zQoegb@FTNsn~fSM`5=804;lX$d4!pG9Q{8CpNX~cS8@%AefD2AsrG`E2Ao|cFHVuu z!+-ygb&Qfuq+(lw!nO`Xp_@%)Wx+W;^3nfL1?Rq;;DA}HONG6!8wNl4Wv zQ8dFLZeXff{$zeaYeB%)+?oWSg%|^?VvN1bdTw22Iz5ciceCa(EpwXdKX{E^1u8_? zmX^Wx({AhY+1)Dp7Kf)z9NRkjRKssyDVlF2Rd+;Wez-mE=E@_yJZsjv=fd7jF~>O! z%t@yo7tHNZgJ=Jg_gzpQJp6XlE@GBinQhIwLOvt!{1ewiR&1A%Xl{9FU67*nHqCHL zhZ#Hk(*T3Sa`TNNtK4gen}3e+R#m-5pQ*(RZmHi5SQxkK48CrA<_Dr)AD}Mr zVL(J`vz0o}BF%`?*tS5wPt$%yhph&NpIwr)VO_Xp39y%8*aNZ$D?980Ukt2mU)pm! zghNco`E~?0K6Q9A$Z3009V)5`WIuP z88W~VhT-I(h%wE8{Q(lFkCI~QPbcXC7)t`rLFRCQ)O);huF^CJrcVqBnKY`Q0yi&eL>5HOQ1f&emc7n zR5q7OMBi-b6cg zBA|B05koSr(-+HaDuzf?Yyk=pr-o`ML_7Ki$pgpd6fmt87k)f!%}j|b+JLt_6<%Rqrd!OYzZu8gN!P1_Tequ%VU z5@y0Eo5umcEX$|}ucwGuuNNl%KzAf2gL}g?x@@})e&(yRhPX)ieO+N@@ld7l?fTya zCC}Q~8qq4!-;KFHNDmiZ6wpmDQ_pJ|`1xbYDeK+mFNKMn?x~K0yZN{4eKqe@flPl$ zyRIXBfru5CO$Lxhlb62_h^eS)d5)w;zLS!!T$q5HKU;b zRh`Qw5^+<}OI~TIkxLihg$q)?N|ZWHVtt_HM_Y;gayT)w5Z2JFbYCH(G2Aq8fl^b2 zyZ+&zme~}W(Q@R<^dEuKAFY2msSqROKD+Q<3&Tp)-U`}6mbndjgg1^q&h(PK({RVj z?Y&3zxafT((q+|*;HgIv7c zzm7-pnSHVPv>KGt-{t4}XW-gb58c;NtPqtGqIadX=Bwok-S(CCsB(9b8E5Y>2;EmY zRecwiy!2L!Grp{k8u!u~R$t!_RQQ1Cf0j;g=li;aaauW(qw`749Bb-4*IXj?U-9MU zcaOMxEouV&IGu$42{pz+0HqH4MOcOH7}Xb`l+7T7`@3l4uzi_lMhc0qxIyPll25~{ z4V#A+iCx__X2)cHEi;y%7{K-5l*8wTM2*##HOYm8pQ?)Bq^Nqb@sFkG`BI-}S9Kpu zv<$kPMN$F$=H0dR7n$A1z6VyW{tvq4f7Yp6QDEl57S&{?^Vjk~l|aM$;d?D%QDIlz z7kP!5uCXE(HulvjglIj`o5dDI1;$vWYl0++sX966i{cIj8mQNcZD7_L_sLtdCW2K( zd^j)Lx|NzdnsBJm%6{GskQ*4#5orPoY|U)Z4ESiC(ID9Fg!q`(IK|uOAllWzsbb}b z6-Plyq(fGr`c7L2XaK8+SeTBJ-)z94{a+#C*?HIZ(5Eoe$uT_i_iYY~b z&FO>z;1|S}u{koNZ8hycE%hqS5bTd{tRSu8`XW_3Kq-KkH)tTi2b%$}7NcVTvm6hk z5EX(TB4#>A0@Stv2NDoN`5cGy=)lRSMbYk&n`HrmQchDh)qxXI)&eHe6(H-_M3@t} z=SY{BFd~c!py}+WoN^*&0R}GU^nVeo@PNN}e420Whh`HWlmt90o0OFNG!KW_s{o+W z!Xcx)GS0?amy`NK@aHU@gB>SrDCIRCH4VXy+mAhc=S1E&TeGuos4P09MzLc3M*lOi zW;6Bo+_j6e?U(+jpM384(%~mhK5K`K)=#bFemr;ZKP45?z@Qn5S$|M${9>o0WBkya zs^l00JF^4P72FI5{vHh^gDXEd8Hn~a5!Qg~P>o1U;M*{ZD@D{pMW*cHJsjIK5DQbka{s%#$CzDOq3RJ6uYFVj&6vRCTC=Ml4??+-mDij{Dbb$If%?;Y}3W!)? zaFB9V-v8q}B8IaOwCgS~0CpsJ&Vi*i;gHfMy+`HxzYvod-ri1?6Vhx^jstiBCfg;y zbh;S8!7o{l5cxrNi4Gqn*Edl)+EDp@h;kG-+L0)Zt)(Cz_z)mC09v3$6By|Ug1|;l zEl)ROg8YpGfi+FeNieE^6}D`uiGU5_^D+ZYCb7)l<#(t}=6Of{X3?$HV`_|4Cr8g@ zX{U+|;{GBJHMi?u?1T1(!%Yv0@z*?W=SZ8CwaPxjD*o%T-YK^p=x$x$XWXJzJ%$>q zHd8kU(UnD3vOT9M4%AO#Ar0=WnuC@cDVgI#e?mO+Tytbxk1RCYAFd}JV#BvAS~66w z2c*D>jwGijR1B3<3Gn|Zp@5j}voqa*%CE5;7fRWAG#YqHMFuz15ao!vLNznPJW_gp z+blNJ$d_2F%Ex7J=J)0iH|cBYm_g!4W=cXQt5osWcTSsjbj8^W3j{fc_W@j2e_^G@w6^jn-?icm1tr zPc(c701srjW_65srhsSOi!vTQTsgJ5&vanD&_12c6|W zf_-CU(#=551FNo7WGRB3!t?L6{(>WNt{`)lwZ;VAgD@)#cDBrG3t<0-@8Sg_lFb8v z)GcXes-e||N-6%M8U}E8dZ?Q*x(#q(Gyta;X6(voCq580fTLnSAlsh;7=;N!Sp>%< z%tB{?oe7l_qh;%xNer7QWL=wK$WsWSHKSLiv=kHv-F%b=qM8zaU0Cu&5p=!{8u_Sn z4?m)RW6%nO-)D}99D^<1)3<$IC^l9hxi=*FhTr4M#4ncapqs*LpnxS?*zs(pHV;yT1zh?_Qg}^g%5B9lCtKfuAsCHejGt8L}A{+2lL< zN~WxmyQ1o5xu82X94X~2^wLrsgnIFo(>J|X|4fv145gl%uEr*|dB`y_A@gd2+3oUC zI#KqRr6Of^KTyiy0`j(te+44d`%wLWwNSX=jE9UkYry&C81g-NXr%FGWCwe=TIT4T z#8!`hoqGwlXjkHg5~qtSZ+xqhY4{9&f2Q-pvc*-!^28mN5_h-L=sQI*4lliGL&q?W z3Ilk?E&VY)F?~~GW>A7!!pqkeheOTL#Xk~&o5a(-cGUwbao;yxZL$}smA+d05U|lF-S)E zc&zY=XmZ- z{Sx=ioUB)FrLp#+qsRMx#N-=Cqf zo`|=$Eqb{n3htq(Sv}tau4ub^sas7nym~S-d27>FBjlIgUE4c_9D#4sDuqnA?X*FUfqlctg$I`vd7&B+t~i~~y9IXSz0Tz@z9w;z4)g3~Tkhxx z>%@Onax1jqh7HFOP+aX*FqPaG&f^kxT=5hTqR8Atja(!ZbIPIW-w1myzR^^cA8jr$ zmTUTj4t&~kY9c$~ms*P6klaV^}w7;9pf5ul;_Au7Nh5VEGHk4bev)XlVpHEW-PYo(~F?NbAFS)^~XWRTp;)0s2r28$+ zgTRX>UG;EvC1tw5aNKUJY8y6KMVOg1Usa>N5RvZ~f-#w7`?HRsku-JkJ~pfmBc+z@wLVS>Ss@(%do<2}l` zvnKP{tUo-Uh%xkShKmvt(vgqBDXlG_&5qt(*D4txe?p+9TP}lk?zC`-F*@hU5g$5(%sLK@$LCe zskh~&e*`S*duHg zSs60Bbb!^7&6TWx0}L_|3RRk&PVVZa^@-6y8;v+8cP$g~f8h^4r=>>SdzEn$Y{1P z9e@*Imdhp`s#?HRNtc7Pfn2Cch%k$BhPX(4tVD+Nf>??W4h1CH^GG2WLmFK=4)P#S zGPwGaK=R#$Do8*hLhb1g$E6vRlmNe^T?82T(6mK6hCBjtFs}qVX$~YhLlcHjY9L440ib{@+M^&uwHmaHdpNHL%a z7nPRHFsMZdku)T5PWNG;2j6#`YR=$vMR&3dmEgFBXpcPd~?PVD|01(Z*bT;CPJgjKf^dUu<=AJ!MXmSM1#NR?RoYq!k3n< zros+yOWb_o`uf`Jp8-ncH};;$zq$rW$+D3nXb(k&O74`-$$}??t-)ZRPqg6{#q=Vg zbmI24D=erqkHnv9&y?e&10$@~f_e)C@Aco-8fURzXpeSt9gS~8T@n)D43D84+l+Dg zA5{8gQpwvbt}He&>a6>jG0BQd4ha>&TbzW2t)Na3<$g+-lrx*$@i@AgeLyt$kN$>$341zSQSvJrMXHP;xYIzz zt+NaqW2^T<9P#63n~HLR*u^p#wQfBz%y$*Qqk|m)j)sr*O@Mnb$ofIuq64HL&4=hI z@lT8V?T7`6;SrVL!Z^faJ#@**xjC9<$OH#BLBzuxGE#hCSM;+=bLNBzJ2^m5%Hnp# zQ7xn-VY*sU6=&ko#AyyiqYTu1GvL?qrgve*6t&XT4 z4LXn$rDpCBShm_p(&VB(-%0Bso=&h|9h@{AP`JtZc^vV^Go~z`e0s%^qTNZCR!zvC zBYHCRs3w#X1P2gT^XAwttyWeIV}POgArS_wxT>5QNcMFgv}U)UNg#($aFW3-S>a&% zAmWz+$bW!^iRJl%lKBUTFRu5=qbrADc}Px790q#JM z!IsS8s1$TB4*DKT?JvBBgj29$J=OvJ)o;bkoDGTTsJlci0#}}L5njxt$w_uMf^~1h zdZ0mBNk2*7r-_3c$1SX$bxrMqdF5xA{6S^4quK%UZ#8&uC998r?Hj}h|4!%^yd5&l z9ud4ZoLhUxSMjj$)Lwsu)aLNI$Merz1B;Hb)wsaJPcC@=%D6V#$2gl}=#*WYlS=gA z<<$6>el%FK&K!44uvGzHzr{GiYdAw91oUl^gPpyVgSH6*O4z_Ekoti=g8{Ea)fWNY zu(_49ObZK3X1RlhX?^E8wLm1`5{#T#YQFvG+g2!`jUm2stLGR&cC43RwzK6 z-twFPfoz4GC@vFQ{QAZ8TKEg?VdM0gD9s z`VN2-DTh-GHHr#%>gdoFcqQCLJLB(7JYmn)Y-{{hP)&W@XnZ5l!cV&6fzDUXPu~bG z*#SG+9tUFd9KQ@VKlmD`WMH}I_~vHyts_cs44i%pnu*(_Rrj23&mP^MnX?ZbBELrz zIRloY3+h^jm zEunASaj@cIC*8m9$3&pnVwkA9PV%Aba}t@|bV6Hm?w0d3wa&a|KR(&H@C}jfpKBw71Hq(-@w~7P?K6_ZUw>(zA9Yg+y}S30e7hM zK^Kk6j;XV@0IdNBz?Hk42&mOm_Adxu<>Wnw*trD3YI#%>6V{M`uhxTGU!q?_y=Y})BETg8$I$%Sb_NvR1#^1 zj+~9a0x(X#9)mX&=Lha8;%%jG8YE+>!25e^lLl?0qL!@mrvP9Fe0EF4x@q0}c<{GQ z7P3>N6El;&j8h}43_jf%M=C|@H2oDFxI7Dl$82wNIzmSF#Eh~0+^Zk&W$Psn_8i*-p!bTFyCc&PkH<9{11}QP%K#8 zoaVPXK`kuwX}%%VQCU{yTcb9Y{WU&Ff6Bh?B9qd!Hd&Fy@ytdG6H0Il)kAl<_>?pf zeePB@HlUM^a{>|j^b319e7Gp-di|q#2d)$urPmE9rjn7;fhX~EuX4+a?pVZA#MojN zPy0Th3fyT6dHOBgcaXLF#-qu4i_EfpNtkIlyIPcX|A6Hi&Z@)j?!Eou6xNiz;=~y> z(jX%DtAj41Fqrs1$TKwXz&LjmvCHU+ZoNetxW8he$;I>llO+?G zaR;{GKE$K=Xlj^c;rwWV#8Tn(%Rb)Tr+A-}=oyi>LSe27Z<-s@FP#wK%HVpz5f;Ge zJQU0^52P{!N8-fmk7hi7)xLc=?DHeMvl6uwnAVVEQ7$!?A${ZaKIYiwo4`h?9hrvP z^j&}Fd&`!#naRC@;q4uvYCbdMp@0jLi%c`C!opB!$>LJ6$~}zcX`$r%V!lh~uZgu9 zZdJM0Jd3LJ{Pnc(zml+PYE2Mnnj++HC~>}}iN;QG8jmakpeLI+ZZY;_qj?YKnl-r> zWP_W22eJNsGy0x6GMS+MY1i-E6^Zp9zfOy@Rp8t&4mZGHvdzxj)hiN91;*1CaP6|4 z(vBg5{&PS}_c+(B-F*EZYWBa9)5Mwm3p$M6nkChsJ3Hn9 zH&PUOYpRR2EcoBZ*hoyGdIbY!kHuwasZ5M(d+DZscEyRl2%5?jqY9d3^-cyItq(Yv z@|`noiS6m}O?0g}OE{Ip!@=LO)VpHwx4+=x`=1Y>Nq*&t3B2VDTi!iMg0oOtA1}HT zoZ$M{@-45d0Z_yys(s@)`}$Vt$3E%r!?$3A565425F{YW&G@cr#yHVc(`Lwr{aQb9 z#Ss7eizzQPoQpG5xQlm#!Oas?GygQ-l0E05NAx86b)rSs>lc5%QG;R8k{Q>5Ad)rYOIABH(_>N+JyFV{UH%`m>lUtIUKr-s zz54enR?EU;M8{=!eLqsF_*doOX_4uR?oz+!?qZq!R}USbf1-SCIzQPE!=U&*c*Kwr zJw1Ejl^~(p{?x_4fb4!CDr>`T#8344}wi#YjU^f4dZ~ewYRHq!Yo_y+d07#k6tM zOWRlsi_KspA}IX`c7ZcY-{)uZ`M~FmUYUFZH!kFl_H>nXnjvqPCuzW5=WR51+@eL? zvh_=LuI%hC>P0fT)VK%^z|qSfJ;X&|hUz=GVHR2y5@0LVnZ-GPr|gd9V9BTgwn-Q$ z{Ly1@_>*&Ffdpx$&x}I|{~PY&`>pgN>3a@Z#u6YD3SKLEk0~eTyJVSe6=O0$v`YqS zYb-Uszi^<5aa>4><$w-wZkwk=(r{E72gxb7j{t|4gI#eEK&I&&z}+UMoReXT+;alD z4c8)|mt+^K0%NUkHdw1yK=&>$WC!977@V9R$$P#5;sAw+c`bk+2gnDQ) zZDcafaHavE(;#leOcSs&&kg|o9~&T4fVL|zu`=xlPBXy1i-e&Oeo5LrXc%zWLOV3W zHKOwfS4LqZ5Q@xM>D9nEH8lW%=~rLEnIR|iFW%pa+XvM2z5ttaU{Xc`q#)mtbpqUz zH6@TEjOB(=iD1#zkEU{!1c0t2i_%6b$o|!}1&~=1bQiE?V_&i2VQ6wV4mxO^+Ax_1 z$H$nfL?3OPuYW;m1@=C^gH*>DsQupqsSYk(R z>&AGJzVq|Mo~ZMxtSie?huk|3eHnflX%HCri*)eM%+9rnCh-Cjan!IwyldIZQfUS6 z2aSr?bBhn7|GKoMm46_sp7Ne6U+>x;%)XljsYErue`Q1-SR4 z&mu-dJVCiZ@5Rh#-RPQ;cK0*QSfxz3O;-F61CY1)=RfV=R3C^|3)b$t-fev0S$FzQ z0OQ{MFvBT@W(xsxF>o1h9X?v0-#`1@DSd#5>WQ!#I(s1UOn!6+vPtU3EyXuy(w5ty z(8ub>k%}VB-R5b_mH$p?2wY&UNNR1#=D%1KqE`yp zOv~;<-h&>!!p1ASP(99uvU#Gl*r>kx!G$~ns-7Wb>D4hl zeEF|l>eQh#-`0Mp8oWDZhM0-gXNiA&o<=zSA6E^&SoXJdP7|qenQ^6@?CmGN{FXfK z+wL5FS&Uku_6<-V$n_IjQ8}Oill=JQpY5LS-wT(l6gHiX=_;Nylku913`Y7&q&Uz5f<%* zOb*{!0}5{kGjEtJnL8*I>^t< zIR|rJT%M6!K4Mz@KsehUcGqv$-_1*TpJ$h7$5X8-x%l|JdROYgc+~kJzKa#RbLYNZ zdTv{gs_j|&%z{yv^=>vB90T0pC>FbYdvwRV^KHbXWXkvbxTDtPox;Y(kefLDdkEc& zr$5}>y`grgy2dar-P`VO{kzMsM6-A&qA{U|PyndPLj?LQi{h<( z0;h*)!E*xvrEofD6Cnc#oksQfn-57cg8-1(i|nfgmRe~L6a`k3CF1$HZ2CW&28+bx zL;|MxHY%TiCfaNOj1i!WOQ3;N%x0?22ZnX1sA9<~f|U)<2^4(^112Y=fk!Ty>@nGX z1GZnhQzY&9l6{h~Q6#A_7+n_6QqKvUsnDYeo+7GC!|l&0nNEl+)t=rWt_Nq`J!F~gYH4yse^cbmG{<3r zVGR1$6K&7Ao6*Cgh5u>s+6lH8B%a^?`1xc16p66&Xz0^!N4LnqCGko0c8xv-^xsiz zHY|rLI1O3Ci>3%}eq~Vdun&+erqZ@t348!C(}nN{`C!X|22CqY04kLkjOhUzEC;Gd z+E&FG=w?&E49-r`1A4CMF_i#dX2zQtFbARgs5iaY3VG$j2{6E+yac)gc&JhlW?BGh zS`01>?toXLvO(=9BEkNxkTU?b9rJ2LggH?JJR zonI=NM&(=t7U^mgJ0uJ@k3lX1Qzc|K=nquaH7qBnFIcp;;Gp^=UT|e4oV=*SqQgXo+k=Pl}^bBnErRI^5f@a+n#d@S-|xn z_ODdpJaPt=bT2`)da*HS_(tI+iOMgO^S}B~`Td^`|CwCX?g%t;vNYN{nXX3}IC0Wq z`a#nbJ%-N}B5M`9WSO{RHE&4qi|c``iyr1g*lcKbsD5ajOH7p2MHzcKop>HjaMD@? z9Jg7BhxWyCaKp;T87m}AjxghyN2P%BEd5BG2e)V-qP1f3t6~|;YZv#BoNNTFS`M(u z9Dl@}9wep$>ib|qzY-<=513{&+QJTQ$bf~l1@fp{R8YqO@Kd0Xj|zNb z5nwk4Ic%tq2`gb`{B|mi1YqON{QK~QSgAX6*zf|on#w5p>M z<8i>T2(jO%qafB7TplGnq)6b4qsxDEF@3VB9ERO#$gGGKcQY}+R-_~SV1P;qdb-MZ z`|h>`_D4}u*#XJiuJsG)0j}b4R~;*A&uQDssK0hhdPnT}Ez$=4I`+%?>g|Z+L)Tr- znialQAEc2UzSf?6#uKM>;yq)cIosl5#*Ggr_baD_^u&xd!Ak`pxOtNas5igqJ^a|c@XyEtCyX%#{=1;X`9t6LO6UCHOu&(BlwEB zDf1S2yAYWXqf=*0M0I&q8Gpp6iS;5E+$z=0e1VHhn?AAeyKH9kW%{las&wPBeYKIM z@S^iYH-96*C4$xeuItO%-E)%kNRcd7Iq=<-aFpg#JtmdjzcZ>)B!3i>HxOQPnqY9Wv@t z!#fRQ> zJ9^FbG(Fa@(=ucHk|-b+-Crxeb#wFW+s_*kC57b8gr1>&^Z3fi`=VxK9b<*+sA9pP zvOzg5J6*&~*ovp<#GOR1mE`)bm%Vq6jyH9~8~6m`8Lmq~m09J{e=Rm2Q_b|GhFXFb zyuN2WEv!I4ikl(gP8Guu{@>;@W+_^kHk`}k&He>d&@Uhe5@=F0s0gBVVqusqe{sAM z@Oe7Ht!$FJP`+>qWPi*f*`lr@#L<4KwG@q}!p^u9I{l<^_?0wDn?fjoQWR~^s+(lrGM3DE5XmIJgy^k~}h2B{fiXFMcc;^YP|4Opm6st-L@lJ|Z!5IGajy zUb2A;VV7*#k75j1iN%{yM$-uf$5h&zBkxrywS`ag;Lhzj+t)Y$c0&8;3t9o%eSIyK zSK0e8RiyOmAJ;uE6URAa+36nnBJzvUyLi!)SCNm?Rz)hs1k_qS&Ehlbq^j-icvDgoe+%CoO5`%g*0<|(o&jZH8IG@FF0+E@N_ zP21&^XWIFQ0pX&+5;#tm%$+Gt$Ky(=fP}4wh?*UEo+}kYGn`IQ=5R*yf+rZPk;6T+~gX? zcg;(Ic{ARBKHi9!%wpF?GdSjwTd9YJD>&#<0>=6Rf+(s~7%PzP=}a3xM|`ffIK3T!I87#d}gcQmj0aJQzdeddlaEP)mpvD~~V} z1hFkzM2`xpC9aGKfqY{r=*%%60Nimj4SkFu1|B|GxZEDo4wOqSkCwayQL{-GpX?CS z!3e6zH zwv!~ck~;=Z-fF@TlXkWS?5Mk*D&DLUakP$Wm7n_b81ho!yGLw=@_!tidpOhm|HsFi zbEr%?pJSs4lQWYHa~@GbLNOxe!=xrjtr>D&!@`U-awCT*C8Tm1QBKKWS`tD;_5Hp3 z{dr$k*PXgPeLnB^>-Bs-9$qF%EPv4Y%IG%<7g4h?hG4uS{}mUWkih9yT^$y-0`fj8 zMirf41-t9I9n8gtceU?{MuQz}#cH-aaLkAT=9~(im0cQO8)+GUg36DM400Ek^-Hk{ zqLA_j<<)dH0h)t0+yXd6Zd*X^oQI`P6c|=M0Xgkx`|UJ2oXK3FkTb_t7w_8 z6mMyVXiNg2@R(K6L9i3W^gwR17{wt=%zyiUh$C#CZJ`U#r98~$u7`kMI%27CEunEqcE4)=MIade@uEm_s zcoK_;#+RLA|2SpYN#PhAfR-SG7=cc90pQS$gK!kr$%WF8$Q3jn=tOlS(m76(GdJK+ z&GY|*WUAF#I>wnR@|-d1mFT6->woziXBa7yIbZ^MzjV_mgiaUK@r;oIjhrI)*6v=V>TQaQ$2t;#-K8s#OngI@W%1$WNnDBfzyH(}+_BeM)A;-Rp>_2qf3;)M z?+ucP+*EBkc7KL?6MEoW3dV>{zlj)>{Yevuu$d`;ZuW3Ir#(pO;X>Fos^}=!M6mds zqXNe^Bou`f7WhWouIOc!s&7;uYmy3E5ZM~Z6>U=*FpEiC4x=|C=0xswl(wD8dlx|sb zYiJl5c@OOo)N3_1v|!)n^G_{7EGLzOA|IuO}s%x7%-7KPy-AASWL{AD^aNq0m`5v5}7n{}pd6EA=8o8Py3rmgl)?8T_P&qrnN z2@yZ7J#rOzx5V4a@%(Ojj*(a?<#xnmj$o(~WFq_PpxDdG#u2rS+*1J;)mtS#8W0Q22>cz$iLksPY#g_~31#qs^ z|Bso?>EbZ#lmjGg+f}sQ&`422x1uaJf=~oF={5t2)k$-?DGg*t2wJyy(bPTdKOIWO zHQ!vFUthTHRbvr*Plq5|_T4%2l6w$K#hWwEN!V`oWO6cg&@F*ALsw``2&l zWo|v@D+h{hrpAxqCvTy%cp+x999nZodA+0{2Ik$v6|O_251Dh5UZB|p6l&t+~{~iBWG{E+HQ@#{LDF%|V&|Fm>S;Sxoa2^g8gQzLdSWl7t$7i0Ue)2NLzT+8wEOeF3dd6N}cHns2RQEDr&E2blcEDojb15Roc!p3I1hHBE z9HaM-^lF}4G*1x$HzNfvE9yE15A<~Ws#wrkx54|#k5=ZGr5%F+TUM!;0RYuvB7atZ z6`?>P?ME;jT#v>`E>Xciup!5>s{p_$3NPs79ImFXvhITkCnyq?Go=P$F-XWYlx!~m5B701v{S>N$z1ZA4J!w9vs%0!Mz*={W*b9gb1SQ$ZGMc*@vGhznEd1xdhl4}O1$ zsG~OC$q_stFaY@m+y?hRmK3AH8|k=s52L`U4N(adX^*c)-aloVVraB5{IJsY>bxx1 zS+jnRo`83DavW%mW56Jka=MRW8_3xmO>IKAi-w@!T<@+~xoGo`(JuhjyA7UC#I|)% zjfyxeczK8itHe*94$&D5NixbNn~X%*j5X?T!(~}P9U3LUT&O{qxD6iQzk?{=q#+w* z3LxOceUBvSEn|vp9}xa2b4H>|Hsi}v0Dub+l24kjmbeLr8D36_^cR=KJ0E14x^O++ zC^xK_?u8MSK+;f_OL54s90z480lchka4+m&9Khm0%4>sF0AWz2)JqSDeej?u6<_`f zk3Ftfcylj4z!jD8=VYpy+JRX)qdqFBxJr9V-0v(E+B}XKNteA&3oCH{sB}4x44aZ zhtz8hCKw|B2W~2afy{$`@`mk{FsS;&AhErM{jq;VT5lMBjDnI|SG{k)P^RYe?fZU* zcz|O^T4_I9{X#CayVc9)OH&ta_*e6RSLmpzp%qnDky_Nkw*HX^ALGSExgR6BI5-hz zF|ncSXh@TnnUzCM*80X7dH6VQKV`D3g`4l|LP6Y&Wa?IRQ|IQVMow~kFFK}qDaL!8 zqR}F?Az8|VZ$uZb{2G44UzsRvti4C+Il02DoeF=t1-r42ou4nQEo&{G^FKdqOjt9x-(Q!n+(eu$Wm9vRi4W&7Y7K@G_ z-hA-$=_b=e`P&yS zYw2Zrjjo@#%U2q%mKL|+AR2ADVGT)#9XozlbqIhesMv+?lVb$wb8cJdn4SluqW+lW%T3ZWA&2IYgZ%+zgag5 z4jgx;!`Ci;@>rgzbmo5hxDPoQp_hV^W!=#IIFN|kJv(G+(jyZ$LSn@}+#g63h0~(J z>(0ztap55D!;-#!Q(FIoUYFEG-^#50!mm#s=J+^VlOmcaF>P^aND?aWd?tOX{OB-AEupr#bFBVuP>bRr zvwldFSmku;-v_|klJSUP`s-liif>*t6PN!(a*9#3>ged^kdryR$7&hW--C5OA2q+0 zTjEYz`B4}!(Sh{g=prVDQ)WsScAC9;{fD9NuC^ZL>FLC0V-Bn9e@tjgvW;2Q;=X@p z#oPPxThb(!5tdkfqwWeG%f-^Qs2!jH3^vT_M{}NI0&y; zu`=G-(6tg?{O0JDAPBjnxY7`pNKfQBXw`XT!*{3!7PsN%a;x9QnA(p&A+>Xc5?`LF zwWZs#PPr<7*CaG8nK-dNy_y=1*uSLGYF2|$5DoFHzjem;JpR*KdRo5w;pV$f+p3w< zR^7&z^0Yq|7esAAn>16FJSoT6rJ2Smjk|GG;vs9so>jg2mCfO2PyF||&TRZBZ`xiN z3GHN!?5x1MzUHvVsU^8w6hCMQypKC~o-QTHLEQvJJa)l%syt4H4&A?DKRJ=hcOe(8 z!f{`9`*P~hC< zb8bSgv*uyt7J1yl)TAMtGsfY-I3P2oW`>+ZbeXri_UHf;16Q{;Gz+J*GXw~%!Rpii z;(5GPDHAHv@f4e8^poM|r@*=a=>4tVc=;viQ@BS79F#N#@~(yBgBQzh6%*6iYY%{q zJ(Y#DdluiF+C07|_>y%HfoJ($Y9T++naQbUQ$KL*Ciw{aNkA~1i;WTmw2_j=itS|# zAZ8Q?Lple&OaSZ%+3}}}3qm*a0*>Mh8ZUH+rGUpmpkp^gASY5Wi5!e40Fn}9U5r*S z2p#ZUnIo4|#MH=BLl7fLprUT+#ekhiKqg-A*KpRqA{d2V*6pbuC^!Ee;u-?33VQ%; z>4xmHo1`F&sV>lhlF|LnAy)Sy5M72~B{eU<2@%|;|765gLZg>$6X_}ua|O5x?m!^3 zi-0S#b0~iN6rXl>&KPM4EGUS^(%8hxAI2;QZPu;DteQci@hac%H&Fw?eaM9Gb1%>c z41z(PjTUyB7(Hlnfr4c7x~r2}HDM}fqCoOXuN|5Usj+if7V%en{OoJ8?Ozo0|G`g+63TgVR{4(|bF#0`{`*ZTh;RvKbni>B?u zOKY{w=UtA)d#V$9{>8BQR3e98c}GNA@|+kMK)k7tZ)Dzx_DMfJ1!ref8Vw>>T9GY% zkk|^|cmV;`z<5}-X;$%&NnNETbGQ*MXW*@98kpljZ&{nZpNQ4?E}!ciBpaK7;@Vr< zuB`F>ioJca_SH|drxr~+p#=B9(fX-`YllA_aL1z}-Eh&IH$1P!AM)q;q8u^YJ{io!Yol{aZ)*3FWLzG`u!s<5gS1WCeGewFsf{R=5S>qwTBz zLA+w?Zg|W%Q}6J1_!->MH=Q3;pUJm45b{G|8soj|qT<$VoLpPF2-2jPGu?iEB-zpJ z@$LAhjvq|%#GA62m|%Xt$FG+yv1ooTchD@z_KbK#;n7#UDC0%dlR7$$kPU+MNlZ#E zxaho-4*G1r$K_akMR;vxKUjcylw;G)w{h`wO>RQF!nLa1frhHJ@qBxMB30lOoS+tv>U5 zuJU4!nTFB+Af7E6&vf3e0`twP!ofC4MV;MO5j&w9oSisj1Y6%51%EAk zy^1O&O=^k#$9tQhOam~|;x>ws&c;V$F?gZ7;N`mfC-`yzx$$#Go)m~YUom2r2=p5; zA?v8_fPMqj;ZeZHg_i>p9+?5-s`>ooTewz6$Lji@5suHX@swkMkjeh$51xXr{oH5C zTjdJ3^=(hAv>3`|*gvT z7NXi)G(Z*`G?q)T@=%Hb%U13zurPMDML2Q`6Ak%DE;vFu=qmTo00Nv|mIFw{OyoH+Y4* z+PHzGYGWZgSIo-_#jAi3;Y&#aS8_cf>g#s0b5^9!Cf)a@l?ikl`R8v=xa?a{aeI zmTxSi@vee%D3$_ox<%9j1vh6er?P^Xlz&fVPS+} zc^%RpMEthqh$rD-xH|`$LvsY^I2j<#bueHb-iy3+!#P9%y!b_fIo(huKe#Se0q;6o z7Rv=#>t(=Z$Xh$|((A|Tvau)wQJAL$<=#wuQ?9-Ege=+3 zL!g#&^w+M(Y0%Rzdp=@%N9E7Y2Af8|^}) zjpM3M8bTwCH_Nj;;m>*MB3-3D{9iRG%{A`NO=W>>D+PQd4Z)-VaO>Cv92|BlMqWWX z|I%L5mD7+O8L4j1q1?oCcYcK|u8GOd^mh1f>!YC}JryXxO0YjP_6ulkdXd5jQl@S{ z^j2NTROzg%J%q!^t`z4Nm3gbES8~am*{o|#fc)M;2`n{%cwwY6#pvI24bUf+gJuZ3K^T?GMhZ~f61xqktWpY9 zA~~3={qaw_q%W`zg9fqPpjuW5=#Un_5{gR(MP%WaRa+PfEEoVT`6M5H9TQ1(2wb|89fBH%&r(T`Z@;9BYhYcg1~r5 zN_7FKHsFxeULKVHp|cV}d%EHgMR&YqrVJcLUoN zckiUQDcnr6X;@)p_h8r4ozUF(rWVn`t7vsHI*j zSCuaNu>#|H4q9k@-cmxgQCAk9icHD65K2!`Cqp95jj5ivIXZgS3lJ>n{t0ef&Enq7 z_wN#J#`K?GXVdb3-no>%x&`BUW%GL1z5Rd#N&>~0tFp{yQ5nI5_)8FEAUPQ7HQw#% zk^Lw{_s}DCvX+bZ!onDz(RO94SvA+h7hkou_p1l_(gpC3S3FH^<}Q>OTTA~U<1;aX zYpNQzzZv-_0x`;=Rpa)w7qDBwb(u%h+0E5Cy`_rN@*+_tDGRKFKv8j&Kc2DEudQ`R zEi*jn{d1U)w_2fNWmMNa&4Kc)F2z7YmFIN4RxW${Z5tP{77 zZS{~&1wVf_@Qn|G3{2h%8f{2}R&Ulm@0ws17ZWSeqCe@s7Vky&xRo%Lt)RoR#lc)L%;<6 zT*i?Bn>@;o?r^>KRFwM8#p?rixxS(um8j6g?+kNBCFgO&(c84>l$rLY(F(tUKY0(i zH}CdIW-A!hN2puak+1+&wMememSXCavk&%gBO<{|lE48AtLN z*e8AcdI$=vwMYalem5WAwAu+A#iLI~lwY~^61$FbO}g#<&>M62tu306=Qj7xpZ4T; z301nk_FhRhUBCF}epJ}qaK!22mM!EhwK3?QfNL>N7bLmSgJaDx=LLO2BJWhF!3XQS zhVFNU9n8102TtghdZz(i1M|js5PLCicZ)w{*lSjg>0-5==l}B7^l$P@zP~Gw=zA}a5M=g$L6x|o zRUa|3wu%8RbU%bKGuQgzJv}x9U*AdN&3u=Ss-hQecGg&(96gh>SYlMW)HS2f#0s;1 zHH#$a+Q&#JuJh&Tzi3ALd<2`8C5L;A*RS2T`=N@S(abMH)2G74p}~W4%W0lnc|D2U z$nTJA7glYJiIFmil_@O?Bm2{<2QgMZC_=wSTfnThus>wS-%Y;k;kneaMLlqGz!iAD zIgOoC+)Fxok`hg_sctL}OrDn<}zO44)-(@x$$(%D{4u9Va%P3u?AuJv@syQ99s0{6QXyENE z>Yc|ldqzBtE#6t7pzv3X59M9Hx{xNSNd$JSyl(d7adTgpi8c%2Pfr-Z9&RLIaqqg@ zjq8lBPABIKjN~^J^-iwo*-e$Qa_<>c3^=+FVwRX7DkxmixYA0qvV;7=iOw0ai?d`T z`}Y-p00%dL7qhI(y{r78yn!v*fp3s|xc0S zQMpzvfeB9#lv<0|?Z*jlMJRd|12KfjCs&zDTY;>dPCpMel@y6_5z_5`WKIsHCaJ3T(#>YINAu*+-0_M_Eg#y6ENWtL zntFiiRp)~wiQgfMPa1Ghh7mF|kHbv$_4^+es6rhIx;%BFWu(8vCm{3Qu#`Hkua4^0^lFT!@wF);=tFtX4ouV^bt+DP9TZ^5TZ6(NmP_y7$%QXh6afQL zNC->51@>)&m9@bl@XB|<%d)`i$99eq>12kWkw9rs5OyF7@o~_~v9}J8ipal*segjy zaFP8G$$5%HI4Cp{1!zzi=dmnqN3DI)0MP5e(`!i6HUe>kB_zb3DCU4A7lC4D0g!^z zKv3?U@dk=bGze(oKG6090jLBE>?{{949Eun%>=YrbhDgdNfh%2n)GWbe)%>XQFmEw zbq6+Z-fd(rGs3G${rE)J9~#DP)-!nLB8LU}VxS*SKant{SIS zBk^W)eyr)YWA(YO*xsM$H^jH6fALoV$noVl>W2YH--Arph&GK~c(B4GQ^XZhS4?1+ z>-H&3o8dSJ753hA1|;U$!T%^z_v}7^hMV;J_O)j@rakC{`K+zJDx^ud8r4Sr(#-qb z^UiVG_oNoHj;l{p1o=J_9We-4ai#tb(&Tn70W&r>YbWwDFji{WTe2k9gcUSc8}hz! zH*evkVfd6SK5&R{JL5B%xB1MCrDvK{Qo7_9>tnlLeS?XAiin$Pl&mOD`E{CLiFN0`u|k%Pp>nw}7k`KCIcjDPL3kD`NkPT6s!Xw-rkT2jsdc~EL~

    HkyYQQEjM;5l0>_U*ov7XC8b@yT`{@I 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

    aGbXRP<6h4`+UU1wnFl^-C@ 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⁣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

    3C7B-uF=QK?!JM?}D3n6#;;;F1UOK}gH|)(8UB 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 )$#U@X}V_H$hIpMOmCURV4KOEne85$n^P)OdV(I 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≫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 R2aMEXo@!MD;P#|5|Jn72#!&zO literal 0 HcmV?d00001 diff --git a/pubspec.yaml b/pubspec.yaml index a83bfbc..85a0d8b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -33,6 +33,7 @@ dependencies: google_mlkit_commons: ^0.3.0 google_mlkit_barcode_scanning: ^0.6.0 flutter_image_compress: ^1.1.3 + image: ^4.0.17 dev_dependencies: From 43c1baab1455a6412024126d7b941a40d8a4e6ee Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 26 May 2023 22:17:53 +0200 Subject: [PATCH 128/150] TODO: Update --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO.md b/TODO.md index 11f739b..1708baa 100644 --- a/TODO.md +++ b/TODO.md @@ -2,12 +2,15 @@ * [ ] BUG: BundleList fail to refresh after: autoMetadata download * [ ] BUG: BundleList show autoMetadata from other bundle when some bundle are deleted +* [ ] BundleSelection: Delete some bundle +* [ ] BundleSelection: Show icons to represent how much metadata is available * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty * [ ] AdEditing: Disable 'Mark as published' if the title is empty * [ ] Search with Selenium in headless mode # DONE +* [x] ISBNDecoding: Show barcode zone, add and remove barcode * [x] BundleSelection: Automatically update bundle list when a bundle is removed or added by the app * [x] BundleSelection: Allow manual refresh if the bundle list has been modified by external device * [x] Launch the scrapping asynchronously to avoid waiting for the provider (notably BooksPrice) From 97f7ed6b5a35db3015f4794b985a6e0d96c8a4a8 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 26 May 2023 23:04:46 +0200 Subject: [PATCH 129/150] BundleSelection: Show little icons --- lib/bundle.dart | 17 ------- lib/enrichment/bundle_selection.dart | 73 ++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/lib/bundle.dart b/lib/bundle.dart index 7d4dda0..613b333 100644 --- a/lib/bundle.dart +++ b/lib/bundle.dart @@ -47,23 +47,6 @@ class Bundle { File get autoMetadataFile => File(path.join(directory.path, 'automatic_metadata.json')); } -extension ListProviderMetadataPairExt on List { - @Deprecated('Use MapProviderEnumBookMetaDataFromProviderExt') - List getPrices() => - map((e) => e.metadata?.marketPrice.toList()).whereNotNull().expand((i) => i).toList()..sort(); - - @Deprecated('Use MapProviderEnumBookMetaDataFromProviderExt') - BookMetaDataFromProvider mergeAllProvider() { - return BookMetaDataFromProvider( - title: map((e) => e.metadata?.title) - .whereNotNull() - .fold(null, (best, s) => s.length > (best?.length ?? 0) ? s : best), - authors: [], - keywords: [], - marketPrice: Float32List.fromList(getPrices())); - } -} - extension MapProviderEnumBookMetaDataFromProviderExt on Map { List getPrices() => values.map((e) => e?.marketPrice.toList()).whereNotNull().expand((i) => i).toList()..sort(); diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index ef6fc62..7ee9e03 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -214,11 +214,16 @@ class BundleWidget extends StatefulWidget { } class _BundleWidgetState extends State { - late Future> cachedAutoMetadata; + late Future>> cachedAutoMetadata; @override void initState() { super.initState(); - cachedAutoMetadata = api.getAutoMetadataFromBundle(path: widget.bundle.autoMetadataFile.path); + cachedAutoMetadata = api.getAutoMetadataFromBundle(path: widget.bundle.autoMetadataFile.path).then((value) { + return Map.fromEntries(value.map((e) { + final providerMdMap = Map.fromEntries(e.metadatas.map((e) => MapEntry(e.provider, e.metadata))).kt; + return MapEntry(e.isbn, providerMdMap); + })).kt; + }); } @override @@ -261,9 +266,9 @@ class _BundleWidgetState extends State { FutureWidget( future: cachedAutoMetadata, builder: (autoMetadata) { - final firstBook = autoMetadata.firstOrNull; + final firstBook = autoMetadata.iter.firstOrNull; if (firstBook == null) return const Text('No book identified'); - final md = firstBook.metadatas.mergeAllProvider(); + final md = firstBook.value.dart.mergeAllProvider(); final priceRange = md.marketPrice.toList(); return Row(children: [ // Text(firstBook.isbn), @@ -282,6 +287,7 @@ class _BundleWidgetState extends State { Expanded( child: Row( children: [ + FutureWidget(future: cachedAutoMetadata, builder: (md) => MetadataIcons(md)), ...imagesShown, const Expanded(child: SizedBox.expand()), Column( @@ -324,3 +330,62 @@ class _BundleWidgetState extends State { ); } } + +class MetadataIcons extends StatelessWidget { + const MetadataIcons(this.metadata); + final KtMutableMap> metadata; + + @override + Widget build(BuildContext context) { + if (metadata.size == 0) return const SizedBox.shrink(); + + final mergedMd = metadata.mapValues((p0) => p0.value.dart.mergeAllProvider()); //map((entry) => entry.) + final allBooksHaveTitle = mergedMd.all((key, value) => (value.title?.length ?? 0) > 5); + final allBooksHaveAuthor = mergedMd.all((key, value) => (value.authors.length) >= 1); + final allBooksHaveBlurb = mergedMd.all((key, value) => (value.blurb?.length ?? 0) > 50); + final allBooksHaveKeywords = mergedMd.all((key, value) => (value.keywords.length) > 5); + final allBooksHavePrice = mergedMd.all((key, value) => (value.marketPrice.length) > 1); + + return Column( + children: [ + _IconStatus(Icons.title, allBooksHaveTitle), + _IconStatus(Icons.person, allBooksHaveAuthor), + _IconStatus(Icons.description, allBooksHaveBlurb), + _IconStatus(Icons.manage_search, allBooksHaveKeywords), + _IconStatus(Icons.euro, allBooksHavePrice), + ], + ); + } +} + +class _IconStatus extends StatelessWidget { + const _IconStatus(this.icon, this.isChecked); + final IconData icon; + final bool isChecked; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(2.0), + child: Stack( + children: [ + Icon( + icon, + color: Colors.black26, + ), + if (isChecked) + const Positioned( + right: 0, + bottom: 0, + child: Icon( + Icons.check_circle, + color: Colors.green, + shadows: [Shadow(blurRadius: 1, color: Colors.white)], + size: 14, + ), + ) + ], + ), + ); + } +} From f9a19ac8f30d6402ee3172c14374b5c903b58231 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 30 May 2023 19:00:41 +0200 Subject: [PATCH 130/150] FutureWidget: Show exclamation point and error in tooltip --- lib/helpers.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/helpers.dart b/lib/helpers.dart index 97272ac..da816b5 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -57,7 +57,13 @@ class AsyncSnapshotWidget extends StatelessWidget { return const CircularProgressIndicator(); case ConnectionState.done: if (snap.hasError) { - return Text(snap.error.toString()); + return Tooltip( + message: snap.error.toString(), + child: const Icon( + Icons.error, + color: Colors.red, + ), + ); } return builder(snap.data as T); default: From 5720387047ede1ae32c476a5e1eabbcaa1985404 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 30 May 2023 19:01:05 +0200 Subject: [PATCH 131/150] Rust: LesLibraires: Parse price --- native/src/leslibraires/parser.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/native/src/leslibraires/parser.rs b/native/src/leslibraires/parser.rs index 73be4c2..02e5117 100644 --- a/native/src/leslibraires/parser.rs +++ b/native/src/leslibraires/parser.rs @@ -35,6 +35,21 @@ pub fn extract_metadata(isbn_search_result: &str) -> Option Option Date: Tue, 30 May 2023 19:16:06 +0200 Subject: [PATCH 132/150] TODO: update --- TODO.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/TODO.md b/TODO.md index 1708baa..ac5c6cc 100644 --- a/TODO.md +++ b/TODO.md @@ -1,15 +1,16 @@ # TODO +* [ ] Better price suggestion. Take into account shipping cost * [ ] BUG: BundleList fail to refresh after: autoMetadata download * [ ] BUG: BundleList show autoMetadata from other bundle when some bundle are deleted -* [ ] BundleSelection: Delete some bundle -* [ ] BundleSelection: Show icons to represent how much metadata is available * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty * [ ] AdEditing: Disable 'Mark as published' if the title is empty * [ ] Search with Selenium in headless mode # DONE +* [x] BundleSelection: Delete some bundle +* [x] BundleSelection: Show icons to represent how much metadata is available * [x] ISBNDecoding: Show barcode zone, add and remove barcode * [x] BundleSelection: Automatically update bundle list when a bundle is removed or added by the app * [x] BundleSelection: Allow manual refresh if the bundle list has been modified by external device From 8f950f75e644235d325899dd5f33fa2bc4edb0f7 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 30 May 2023 19:31:35 +0200 Subject: [PATCH 133/150] Add weight_grams in Ad --- lib/bridge_definitions.dart | 2 ++ lib/bridge_generated.dart | 4 ++++ native/src/bridge_generated.io.rs | 3 +++ native/src/common.rs | 1 + 4 files changed, 10 insertions(+) diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index afa3194..8e0b4d3 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -38,12 +38,14 @@ class Ad { String title; String description; int priceCent; + final int weightGrams; List imgsPath; Ad({ required this.title, required this.description, required this.priceCent, + required this.weightGrams, required this.imgsPath, }); } diff --git a/lib/bridge_generated.dart b/lib/bridge_generated.dart index 4c15677..dbf52bc 100644 --- a/lib/bridge_generated.dart +++ b/lib/bridge_generated.dart @@ -349,6 +349,7 @@ class NativePlatform extends FlutterRustBridgeBase { wireObj.title = api2wire_String(apiObj.title); wireObj.description = api2wire_String(apiObj.description); wireObj.price_cent = api2wire_i32(apiObj.priceCent); + wireObj.weight_grams = api2wire_i32(apiObj.weightGrams); wireObj.imgs_path = api2wire_StringList(apiObj.imgsPath); } @@ -650,6 +651,9 @@ class wire_Ad extends ffi.Struct { @ffi.Int32() external int price_cent; + @ffi.Int32() + external int weight_grams; + external ffi.Pointer imgs_path; } diff --git a/native/src/bridge_generated.io.rs b/native/src/bridge_generated.io.rs index 09a6361..958a703 100644 --- a/native/src/bridge_generated.io.rs +++ b/native/src/bridge_generated.io.rs @@ -93,6 +93,7 @@ impl Wire2Api for wire_Ad { title: self.title.wire2api(), description: self.description.wire2api(), price_cent: self.price_cent.wire2api(), + weight_grams: self.weight_grams.wire2api(), imgs_path: self.imgs_path.wire2api(), } } @@ -142,6 +143,7 @@ pub struct wire_Ad { title: *mut wire_uint_8_list, description: *mut wire_uint_8_list, price_cent: i32, + weight_grams: i32, imgs_path: *mut wire_StringList, } @@ -177,6 +179,7 @@ impl NewWithNullPtr for wire_Ad { title: core::ptr::null_mut(), description: core::ptr::null_mut(), price_cent: Default::default(), + weight_grams: Default::default(), imgs_path: core::ptr::null_mut(), } } diff --git a/native/src/common.rs b/native/src/common.rs index aa37092..9a9c271 100644 --- a/native/src/common.rs +++ b/native/src/common.rs @@ -35,6 +35,7 @@ pub struct Ad { pub title: String, pub description: String, pub price_cent: i32, + pub weight_grams: i32, pub imgs_path: Vec, } From aaa5575cbcaa5498ca7a2b55269467527fa3b5d3 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Wed, 31 May 2023 20:58:07 +0200 Subject: [PATCH 134/150] Weight take into account wrapping. Price take into account shipping cost --- lib/enrichment/ad_editing.dart | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 4194b46..53cfeaf 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -55,12 +55,15 @@ class _AdEditingWidgetState extends State { description += '\n\nMots-clés:\n' + keywords; } - final totalPrice = metadataFromIsbn.map((e) => e.value.priceCent ?? 0).sum; - + final totalPriceIncludingShipping = metadataFromIsbn.map((e) => e.value.priceCent ?? 0).sum; + final weightGramsWithWrapping = (widget.step.bundle.metadata.weightGrams! * 1.2).toInt(); + final totalPriceExcludingShipping = + totalPriceIncludingShipping - _estimatedShippingCost(grams: weightGramsWithWrapping); ad = Ad( title: title, description: description, - priceCent: totalPrice, + priceCent: totalPriceExcludingShipping, + weightGrams: weightGramsWithWrapping, imgsPath: widget.step.bundle.compressedImages.map((e) => e.path).toList()); } @@ -127,15 +130,15 @@ class _AdEditingWidgetState extends State { controller: TextEditingController(text: ad.priceCent.divide(100).toString()), decoration: const InputDecoration( icon: Icon(Icons.euro), - labelText: 'Price', + labelText: 'Price (without shipping cost)', ), style: const TextStyle(fontSize: 20), )), TextFormField( - initialValue: metadata.weightGrams?.toString(), + initialValue: ad.weightGrams.toString(), decoration: const InputDecoration( icon: Icon(Icons.scale), - labelText: 'Weight (grams)', + labelText: 'Weight with wrapping (grams)', ), style: const TextStyle(fontSize: 20), ), @@ -179,4 +182,21 @@ class _AdEditingWidgetState extends State { ), ); } + + int _estimatedShippingCost({required int grams}) { + final shippingCosts = [ + _ShippingCostIfWeightIsUnder(maxWeightGram: 500, priceCent: 349), + _ShippingCostIfWeightIsUnder(maxWeightGram: 1000, priceCent: 399), + _ShippingCostIfWeightIsUnder(maxWeightGram: 2000, priceCent: 499), + _ShippingCostIfWeightIsUnder(maxWeightGram: 5000, priceCent: 649), + ]; + final shippingCost = shippingCosts.firstWhere((sc) => sc.maxWeightGram > grams); + return shippingCost.priceCent; + } +} + +class _ShippingCostIfWeightIsUnder { + _ShippingCostIfWeightIsUnder({required this.maxWeightGram, required this.priceCent}); + final int maxWeightGram; + final int priceCent; } From 52730fa003652bd38daac5958fd40551c2f8ebf4 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 1 Jun 2023 01:42:30 +0200 Subject: [PATCH 135/150] BundleSelection: Add action button to invalidate all metadata from providers --- lib/camera/camera.dart | 2 +- lib/common.dart | 2 ++ lib/enrichment/bundle_selection.dart | 31 +++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index f4f3e62..afc25d5 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -43,7 +43,7 @@ class _CameraWidgetState extends State with WidgetsBindingObserver Bundle get getBundle => Bundle(getBundleDir); void _generateNewFolderPath() { - bundleName = DateTime.now().toIso8601String().replaceAll(':', '_'); + bundleName = common.nowAsFileName(); } @override diff --git a/lib/common.dart b/lib/common.dart index 39e71fa..10fb504 100644 --- a/lib/common.dart +++ b/lib/common.dart @@ -8,6 +8,8 @@ extension IterableFutureMapEntryExt on Iterable>> { Future> toMap() async => Map.fromEntries(await Future.wait(this)); } +String nowAsFileName() => DateTime.now().toIso8601String().replaceAll(':', '_'); + final bookyDir = Platform.isAndroid ? Directory('/storage/emulated/0/DCIM/booky/') : Directory('/run/user/1000/gvfs/mtp:host=SAMSUNG_SAMSUNG_Android_RFCRA1CG6KT/Internal storage/DCIM/booky/'); diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 7ee9e03..cb74810 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -63,7 +63,36 @@ class _BundleSelectionState extends State { } }); }, - ) + ), + PopupMenuButton( + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + child: const Text('Invalidate all metadata from provider'), + onTap: () async { + await _listBundles()?.map((bundle) async { + if (!await bundle.autoMetadataFile.exists()) { + print('Nothing to do'); + return; + } + final destinationName = + path.basename(bundle.autoMetadataFile.path) + '_backup_' + common.nowAsFileName(); + final res = await Process.run('gio', ['rename', bundle.autoMetadataFile.path, destinationName]); + if (res.exitCode != 0) { + print('stdout is ${res.stdout}'); + print('stderr is ${res.stderr}'); + throw Exception('rename status is ${res.exitCode}'); + } + }).let((futures) async => await Future.wait(futures)); + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('All automatic metadata have been invalidated'))); + } + }, + ) + ]; + }, + ), ], ), floatingActionButton: FloatingActionButton( From db500b650122036bc2f3b6549403f207a6889c75 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Thu, 1 Jun 2023 22:43:00 +0200 Subject: [PATCH 136/150] Rust: LesLibraires: Add fixed shipping price --- native/src/leslibraires/parser.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/native/src/leslibraires/parser.rs b/native/src/leslibraires/parser.rs index 02e5117..3086f5f 100644 --- a/native/src/leslibraires/parser.rs +++ b/native/src/leslibraires/parser.rs @@ -36,7 +36,9 @@ pub fn extract_metadata(isbn_search_result: &str) -> Option Option().unwrap() + shipping_price }); Some(common::BookMetaDataFromProvider { @@ -58,7 +60,7 @@ pub fn extract_metadata(isbn_search_result: &str) -> Option Date: Thu, 1 Jun 2023 23:12:31 +0200 Subject: [PATCH 137/150] Apply minimum price after excluding shipping cost --- lib/enrichment/ad_editing.dart | 6 +++++- lib/enrichment/metadata_collecting.dart | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 53cfeaf..54eb5bf 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -57,8 +58,11 @@ class _AdEditingWidgetState extends State { final totalPriceIncludingShipping = metadataFromIsbn.map((e) => e.value.priceCent ?? 0).sum; final weightGramsWithWrapping = (widget.step.bundle.metadata.weightGrams! * 1.2).toInt(); - final totalPriceExcludingShipping = + var totalPriceExcludingShipping = totalPriceIncludingShipping - _estimatedShippingCost(grams: weightGramsWithWrapping); + + const minimumSellingPrice = 100; + totalPriceExcludingShipping = max(totalPriceExcludingShipping, minimumSellingPrice); ad = Ad( title: title, description: description, diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 03d658f..6ce309a 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -134,9 +134,7 @@ class _BookMetadataCollectingWidgetState extends State<_BookMetadataCollectingWi controllers.priceTextFieldController.text = ''; } else { final minMarketPrice = manualMD.marketPrice.min; - const minimumSellingPrice = 1.0; - controllers.priceTextFieldController.text = - minMarketPrice < minimumSellingPrice ? minimumSellingPrice.toString() : minMarketPrice.round().toString(); + controllers.priceTextFieldController.text = minMarketPrice.round().toString(); } } From ab0d4bb76c8131bcbf45afbca4dac4192bf79f1b Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 2 Jun 2023 23:01:53 +0200 Subject: [PATCH 138/150] TODO: Update --- TODO.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index ac5c6cc..b978839 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,7 @@ # TODO -* [ ] Better price suggestion. Take into account shipping cost +* [ ] Add the ISBNs in the ad description for better indexing +* [ ] Show LBC weight category instead of weight in grams * [ ] BUG: BundleList fail to refresh after: autoMetadata download * [ ] BUG: BundleList show autoMetadata from other bundle when some bundle are deleted * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty @@ -9,6 +10,7 @@ # DONE +* [x] Better price suggestion. Take into account shipping cost * [x] BundleSelection: Delete some bundle * [x] BundleSelection: Show icons to represent how much metadata is available * [x] ISBNDecoding: Show barcode zone, add and remove barcode From 46b20efdc9f79fce20aa76db04305fc6dd8fb032 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 2 Jun 2023 23:02:45 +0200 Subject: [PATCH 139/150] ISBNDecoding: Use pannable image to show barcode --- lib/enrichment/isbn_decoding.dart | 101 ++++++++++++++---------------- pubspec.yaml | 1 + 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/lib/enrichment/isbn_decoding.dart b/lib/enrichment/isbn_decoding.dart index 8d22e81..da706dc 100644 --- a/lib/enrichment/isbn_decoding.dart +++ b/lib/enrichment/isbn_decoding.dart @@ -1,14 +1,13 @@ +import 'dart:async'; import 'dart:io'; -import 'dart:math'; -import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/ffi.dart'; import 'package:image/image.dart' as image; import 'package:kt_dart/collection.dart'; +import 'package:vector_math/vector_math_64.dart'; import '../helpers.dart'; -import '../image_helper.dart'; import 'enrichment.dart'; class ISBNDecodingWidget extends StatefulWidget { @@ -56,6 +55,7 @@ class _ISBNDecodingWidgetState extends State { children: results.results.map( (result) { final isbn = result.value; + return Padding( padding: const EdgeInsets.all(8.0), child: Row( @@ -73,8 +73,7 @@ class _ISBNDecodingWidgetState extends State { : TextDecoration.lineThrough), )), const SizedBox(width: 20), - SizedBox( - width: 200, child: ISBNPreview(imgPath, corners: result.corners)), + ISBNPreview(imgFile: imgPath, result: result), ], ), ); @@ -154,69 +153,61 @@ class _ISBNDecodingWidgetState extends State { } } -class ISBNPreview extends StatefulWidget { - const ISBNPreview(this.fullImageFile, {required this.corners}); +extension _PointExt on Point { + image.Point toImgPoint() => image.Point(x, y); +} - final File fullImageFile; - final List corners; +class ISBNPreview extends StatefulWidget { + const ISBNPreview({required this.imgFile, required this.result}); + final File imgFile; + final BarcodeDetectResult result; @override State createState() => _ISBNPreviewState(); } -extension PointExt on Point { - image.Point toImgPoint() => image.Point(x, y); -} - class _ISBNPreviewState extends State { - ui.Image? barcodePreview; - + late num barcodeWidth; + late Vector3 translate; @override void initState() { super.initState(); - Future(() async { - final fullImage = image.decodeJpg(await widget.fullImageFile.readAsBytes())!; - - /// Add some space around the barcode to be sure the text ISBN will be in the frame - const padding = 50; - - final topLeft = widget.corners[1].toImgPoint() + image.Point(-padding, -padding); - final topRight = widget.corners[2].toImgPoint() + image.Point(padding, -padding); - final bottomLeft = widget.corners[0].toImgPoint() + image.Point(-padding, padding); - final bottomRight = widget.corners[3].toImgPoint() + image.Point(padding, padding); - - /// By default, copyRectify try to conserve the ratio of the full image - /// But the barcode zone ratio is has no link with the full image ratio - /// So the barcode ratio is computed manually then given to `copyRectify` through its `toImage` parameter - final height = max(bottomLeft.y - topLeft.y, bottomRight.y - topRight.y); - final width = max(topRight.x - topLeft.x, bottomRight.x - bottomLeft.x); - final dest = image.Image(height: height.toInt(), width: width.toInt()); - - final rectified = image.copyRectify( - fullImage, - topLeft: topLeft, - topRight: topRight, - bottomLeft: bottomLeft, - bottomRight: bottomRight, - toImage: dest, - ); - - final rectifiedUi = await convertImageToFlutterUi(rectified); - setState(() { - barcodePreview = rectifiedUi; - }); - }); + + /// Add some space around the barcode to be sure the text ISBN will be in the frame + const padding = 50; + final topLeft = widget.result.corners[1].toImgPoint() + image.Point(-padding, -padding); + final topRight = widget.result.corners[2].toImgPoint() + image.Point(padding, -padding); + barcodeWidth = topRight.x - topLeft.x; + translate = Vector3(-topLeft.x.toDouble(), -topLeft.y.toDouble(), 0.0); } @override Widget build(BuildContext context) { - final barcodePreview = this.barcodePreview; - if (barcodePreview == null) { - return const CircularProgressIndicator(); - } - return RawImage( - image: barcodePreview, - fit: BoxFit.fitWidth, - ); + const maxWidth = 300.0; + + return SizedBox( + width: maxWidth, + height: maxWidth, + child: Column( + children: [ + Expanded( + child: InteractiveViewer( + maxScale: 4, + minScale: 0.01, + // Allow to scale down so much that the image does not fill the viewport anymore + boundaryMargin: const EdgeInsets.all(500.0), + transformationController: TransformationController(Matrix4.identity() + ..scale(maxWidth / barcodeWidth) + ..translate(translate)), + constrained: false, + child: ImageWidget(widget.imgFile), + ), + ), + // TODO: When scrolling over the InteractiveViewer, both the InteractiveViewer and the surrounding SingleChildScrollView handle it + // Ideally, if the InteractiveViewer handles the scroll event, it should absorb it and prevent the SingleChildScrollView from scrolling + // For some reason holding shift while scrolling prevent the SingleChildScrollView from scrolling + const Text('Shift+scroll to zoom in and out of the image'), + ], + )); } } diff --git a/pubspec.yaml b/pubspec.yaml index 85a0d8b..b17320f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: google_mlkit_barcode_scanning: ^0.6.0 flutter_image_compress: ^1.1.3 image: ^4.0.17 + vector_math: ^2.1.4 dev_dependencies: From ffff9b892007bd57ced2a0879c811ca37688f7e0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 2 Jun 2023 23:25:47 +0200 Subject: [PATCH 140/150] BundleSelection: Refresh button refresh already present Bundle in case their metadata changed --- lib/enrichment/bundle_selection.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index cb74810..35d261a 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -247,6 +247,16 @@ class _BundleWidgetState extends State { @override void initState() { super.initState(); + _loadAutoMetadata(); + } + + @override + void didUpdateWidget(covariant BundleWidget oldWidget) { + super.didUpdateWidget(oldWidget); + _loadAutoMetadata(); + } + + void _loadAutoMetadata() { cachedAutoMetadata = api.getAutoMetadataFromBundle(path: widget.bundle.autoMetadataFile.path).then((value) { return Map.fromEntries(value.map((e) { final providerMdMap = Map.fromEntries(e.metadatas.map((e) => MapEntry(e.provider, e.metadata))).kt; From e46decb8142a186bc6bbdb3f3c25384e33057dd0 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 2 Jun 2023 23:26:07 +0200 Subject: [PATCH 141/150] TODO: Update --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index b978839..bbb009d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,6 @@ # TODO +* [ ] ISBNDecoding: Check the ISBN checksum * [ ] Add the ISBNs in the ad description for better indexing * [ ] Show LBC weight category instead of weight in grams * [ ] BUG: BundleList fail to refresh after: autoMetadata download From f8248c00c179230ceafa16dbd76503ce5dac4139 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Fri, 2 Jun 2023 23:29:11 +0200 Subject: [PATCH 142/150] BundleSelection: Better padding --- lib/enrichment/bundle_selection.dart | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/enrichment/bundle_selection.dart b/lib/enrichment/bundle_selection.dart index 35d261a..78cc397 100644 --- a/lib/enrichment/bundle_selection.dart +++ b/lib/enrichment/bundle_selection.dart @@ -191,24 +191,24 @@ class _BundleSelectionState extends State { Widget _compressIndicator() { if (compressedBundleNb == bundleNb || !Platform.isAndroid) return const SizedBox.shrink(); - return bundleNb.ifIs( - nul: () => const LinearProgressIndicator(), - notnull: (bundleNb) => Row( - children: [ - const Text('Compressing '), - Expanded(child: LinearProgressIndicator(value: compressedBundleNb / bundleNb)), - Text('$compressedBundleNb / $bundleNb') - ], - )); + return Padding( + padding: const EdgeInsets.all(8.0), + child: bundleNb.ifIs( + nul: () => const LinearProgressIndicator(), + notnull: (bundleNb) => Row( + children: [ + const Text('Compressing '), + Expanded(child: LinearProgressIndicator(value: compressedBundleNb / bundleNb)), + Text('$compressedBundleNb / $bundleNb') + ], + )), + ); } Widget _bundleListWidget(Iterable bundles) { return Column( children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: _compressIndicator(), - ), + _compressIndicator(), Expanded( child: GridView.extent( maxCrossAxisExtent: 500, From ef95f8ce190a74c886c13a2c5f8a6f27019108c6 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 3 Jun 2023 00:13:12 +0200 Subject: [PATCH 143/150] AdEditing: Show the weight with LBC category --- TODO.md | 2 +- lib/enrichment/ad_editing.dart | 69 +++++++++++++++++++++++++--------- lib/helpers.dart | 18 +++++++++ 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/TODO.md b/TODO.md index bbb009d..462adf6 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ * [ ] ISBNDecoding: Check the ISBN checksum * [ ] Add the ISBNs in the ad description for better indexing -* [ ] Show LBC weight category instead of weight in grams * [ ] BUG: BundleList fail to refresh after: autoMetadata download * [ ] BUG: BundleList show autoMetadata from other bundle when some bundle are deleted * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty @@ -11,6 +10,7 @@ # DONE +* [x] Show LBC weight category instead of weight in grams * [x] Better price suggestion. Take into account shipping cost * [x] BundleSelection: Delete some bundle * [x] BundleSelection: Show icons to represent how much metadata is available diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 54eb5bf..60a0acc 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -104,6 +104,7 @@ class _AdEditingWidgetState extends State { padding: const EdgeInsets.all(8.0), child: SingleChildScrollView( child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ CopiableTextField(TextFormField( controller: TextEditingController(text: ad.title), @@ -138,13 +139,17 @@ class _AdEditingWidgetState extends State { ), style: const TextStyle(fontSize: 20), )), - TextFormField( - initialValue: ad.weightGrams.toString(), - decoration: const InputDecoration( - icon: Icon(Icons.scale), - labelText: 'Weight with wrapping (grams)', + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + const Icon( + Icons.scale, + color: Colors.grey, + ), + Expanded(child: _LBCStyledWeight(ad.weightGrams)), + ], ), - style: const TextStyle(fontSize: 20), ), Padding( padding: const EdgeInsets.symmetric(vertical: 8.0), @@ -168,18 +173,20 @@ class _AdEditingWidgetState extends State { ), ]), ), - ElevatedButton( - onPressed: () { - final d = widget.step.bundle.directory; - final segments = path.split(d.path); - segments[segments.length - 2] = 'booky_done'; - d.renameSync(path.joinAll(segments)); - ScaffoldMessenger.of(context).showSnackBar(const SnackBar( - content: Text('Moved'), - )); - Navigator.push(context, MaterialPageRoute(builder: (context) => const BundleSelection())); - }, - child: const Text('Mark as published')) + Center( + child: ElevatedButton( + onPressed: () { + final d = widget.step.bundle.directory; + final segments = path.split(d.path); + segments[segments.length - 2] = 'booky_done'; + d.renameSync(path.joinAll(segments)); + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + content: Text('Moved'), + )); + Navigator.push(context, MaterialPageRoute(builder: (context) => const BundleSelection())); + }, + child: const Text('Mark as published')), + ) ], ), ), @@ -204,3 +211,29 @@ class _ShippingCostIfWeightIsUnder { final int maxWeightGram; final int priceCent; } + +class _WeightCategory { + const _WeightCategory({required this.maxWeight, required this.description}); + final int maxWeight; + final T description; +} + +class _LBCStyledWeight extends StatelessWidget { + const _LBCStyledWeight(this.weightGrams); + final num weightGrams; + @override + Widget build(BuildContext context) { + const weightCategories = [ + _WeightCategory(maxWeight: 100, description: "Jusqu'à 100 g"), + _WeightCategory(maxWeight: 250, description: 'De 100 g à 250 g'), + _WeightCategory(maxWeight: 500, description: 'De 250 g à 500 g'), + _WeightCategory(maxWeight: 1000, description: 'De 500 g à 1 kg'), + _WeightCategory(maxWeight: 2000, description: 'De 1 kg à 2 kg'), + _WeightCategory(maxWeight: 5000, description: 'De 2 kg à 5 kg'), + _WeightCategory(maxWeight: 10000, description: 'De 5 kg à 10 kg'), + _WeightCategory(maxWeight: 20000, description: 'De 10 kg à 20 kg'), + _WeightCategory(maxWeight: 20000, description: 'De 20 kg à 30 kg'), + ]; + return LBCRadioButton(weightCategories.firstWhere((c) => c.maxWeight > weightGrams).description); + } +} diff --git a/lib/helpers.dart b/lib/helpers.dart index da816b5..fa1d145 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -4,6 +4,24 @@ import 'package:flutter/material.dart'; import 'bridge_definitions.dart'; +class LBCRadioButton extends StatelessWidget { + const LBCRadioButton(this.text); + final String text; + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: RadioListTile( + value: true, + groupValue: true, + onChanged: (_) {}, + title: Text(text), + activeColor: const Color(0xffff6e14), + ), + ); + } +} + class ImageWidget extends StatelessWidget { const ImageWidget(this.image); final File image; From 14c31436204a3cb296347a9a1cfceb66ec32fe84 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sat, 3 Jun 2023 00:37:07 +0200 Subject: [PATCH 144/150] AdEditing: Better alignement, better color --- lib/copiable_text_field.dart | 24 +++++++++++++++++++-- lib/enrichment/ad_editing.dart | 38 ++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/lib/copiable_text_field.dart b/lib/copiable_text_field.dart index 10c164d..817bda0 100644 --- a/lib/copiable_text_field.dart +++ b/lib/copiable_text_field.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:super_clipboard/super_clipboard.dart'; -class CopiableTextField extends StatelessWidget { - const CopiableTextField(this.textFormField); +class CopyableTextField extends StatelessWidget { + const CopyableTextField(this.textFormField); final TextFormField textFormField; @override @@ -19,3 +19,23 @@ class CopiableTextField extends StatelessWidget { ], ); } + +/// Used to add some padding so all the field are aligned, whether they contained a copyable text or not +class NonCopyableTextField extends StatelessWidget { + const NonCopyableTextField({required this.child}); + final Widget child; + + @override + Widget build(BuildContext context) => Row( + children: [ + const Visibility( + visible: false, + maintainSize: true, + maintainAnimation: true, + maintainState: true, + child: IconButton(icon: Icon(Icons.copy), onPressed: null), + ), + Expanded(child: child), + ], + ); +} diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 60a0acc..9f5d63f 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -98,6 +98,7 @@ class _AdEditingWidgetState extends State { @override Widget build(BuildContext context) { final metadata = widget.step.bundle.metadata; + const iconColor = Color(0xff898989); return Scaffold( appBar: AppBar(title: const Text('Ad editing')), body: Padding( @@ -106,7 +107,7 @@ class _AdEditingWidgetState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - CopiableTextField(TextFormField( + CopyableTextField(TextFormField( controller: TextEditingController(text: ad.title), decoration: const InputDecoration( icon: Icon(Icons.title), @@ -114,15 +115,17 @@ class _AdEditingWidgetState extends State { ), style: const TextStyle(fontSize: 30), )), - TextFormField( - initialValue: metadata.itemState?.loc, - decoration: const InputDecoration( - icon: Icon(Icons.diamond), - labelText: 'State', + NonCopyableTextField( + child: TextFormField( + initialValue: metadata.itemState?.loc, + decoration: const InputDecoration( + icon: Icon(Icons.diamond), + labelText: 'State', + ), + style: const TextStyle(fontSize: 20), ), - style: const TextStyle(fontSize: 20), ), - CopiableTextField(TextFormField( + CopyableTextField(TextFormField( controller: TextEditingController(text: ad.description), maxLines: null, scrollPhysics: const NeverScrollableScrollPhysics(), @@ -131,7 +134,7 @@ class _AdEditingWidgetState extends State { labelText: 'Ad description', ), )), - CopiableTextField(TextFormField( + CopyableTextField(TextFormField( controller: TextEditingController(text: ad.priceCent.divide(100).toString()), decoration: const InputDecoration( icon: Icon(Icons.euro), @@ -139,24 +142,22 @@ class _AdEditingWidgetState extends State { ), style: const TextStyle(fontSize: 20), )), - Padding( - padding: const EdgeInsets.all(8.0), + NonCopyableTextField( child: Row( children: [ const Icon( Icons.scale, - color: Colors.grey, + color: iconColor, ), Expanded(child: _LBCStyledWeight(ad.weightGrams)), ], ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), + NonCopyableTextField( child: Row(children: [ const Icon( Icons.collections, - color: Colors.grey, + color: iconColor, ), const SizedBox(width: 16), DraggableFilesWidget( @@ -187,7 +188,12 @@ class _AdEditingWidgetState extends State { }, child: const Text('Mark as published')), ) - ], + ] + .map((e) => Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: e, + )) + .toList(), ), ), ), From 473ef041afe54e54696aba5a4470362d615a7977 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Sun, 4 Jun 2023 22:18:50 +0200 Subject: [PATCH 145/150] Camera: Add TextField to manually add ISBN --- TODO.md | 2 ++ lib/camera/camera.dart | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 462adf6..741a78a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,7 @@ # TODO +* [ ] ISBNDecoding: Delete an image +* [ ] ISBNDecoding: Pan and zoom on an image * [ ] ISBNDecoding: Check the ISBN checksum * [ ] Add the ISBNs in the ad description for better indexing * [ ] BUG: BundleList fail to refresh after: autoMetadata download diff --git a/lib/camera/camera.dart b/lib/camera/camera.dart index afc25d5..1a158d3 100644 --- a/lib/camera/camera.dart +++ b/lib/camera/camera.dart @@ -545,6 +545,7 @@ class MetadataWidget extends StatefulWidget { class _MetadataWidgetState extends State { late common.Metadata metadata; + final additionalISBNController = TextEditingController(); @override void initState() { @@ -556,6 +557,7 @@ class _MetadataWidgetState extends State { Widget build(BuildContext context) { return SimpleDialog( title: const Text('Add the final metadata'), + contentPadding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16), children: [ TextFormField( initialValue: '', @@ -575,15 +577,31 @@ class _MetadataWidgetState extends State { onChanged: (state) => setState(() { metadata.itemState = state; })), + TextFormField( + controller: additionalISBNController, + keyboardType: TextInputType.number, + decoration: const InputDecoration( + icon: Icon(Icons.text_snippet), + labelText: 'Additional ISBN, separated by space', + labelStyle: TextStyle(fontSize: 10), + ), + style: const TextStyle(fontSize: 15), + ), IconButton( icon: const Icon(Icons.save), onPressed: () async { final managePerm = await Permission.manageExternalStorage.request(); print('managePerm = $managePerm'); + metadata.isbns!.addAll(additionalISBNController.text.split(' ')); File(path.join(widget.directory.path, 'metadata.json')).writeAsStringSync(jsonEncode(metadata.toJson())); widget.onSubmit(); }) - ], + ] + .map((w) => Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: w, + )) + .toList(), ); } } From be5ac59cfd8199305c11538a2669f92139c7bbdd Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 5 Jun 2023 19:07:10 +0200 Subject: [PATCH 146/150] Display ISBN in AdEditing --- TODO.md | 2 +- lib/enrichment/ad_editing.dart | 45 +++++++++++++------------ lib/enrichment/enrichment.dart | 2 +- lib/enrichment/metadata_collecting.dart | 30 +++++++++-------- lib/helpers.dart | 2 ++ 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/TODO.md b/TODO.md index 741a78a..b433bc3 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,6 @@ * [ ] ISBNDecoding: Delete an image * [ ] ISBNDecoding: Pan and zoom on an image * [ ] ISBNDecoding: Check the ISBN checksum -* [ ] Add the ISBNs in the ad description for better indexing * [ ] BUG: BundleList fail to refresh after: autoMetadata download * [ ] BUG: BundleList show autoMetadata from other bundle when some bundle are deleted * [ ] MetadataCollecting: Disable 'Validate Metadatas' button if price field is empty @@ -12,6 +11,7 @@ # DONE +* [x] Add the ISBNs in the ad description for better indexing * [x] Show LBC weight category instead of weight in grams * [x] Better price suggestion. Take into account shipping cost * [x] BundleSelection: Delete some bundle diff --git a/lib/enrichment/ad_editing.dart b/lib/enrichment/ad_editing.dart index 9f5d63f..c2889f0 100644 --- a/lib/enrichment/ad_editing.dart +++ b/lib/enrichment/ad_editing.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rust_bridge_template/personal_info.dart' as personal_info; +import 'package:kt_dart/kt.dart'; import 'package:path/path.dart' as path; import '../copiable_text_field.dart'; @@ -30,8 +31,8 @@ String vecFmt(Iterable it) { return 'de ${vec[0]}'; } -String _bookFormatTitleAndAuthor(String title, Iterable authors) { - return '"$title" ${vecFmt(authors.map((a) => a.toText()))}'; +String _bookFormat(BookMetaDataManual book, {bool withISBN = false}) { + return '"${book.title}" ${vecFmt(book.authors.map((a) => a.toText()))}' + (withISBN ? ' (ISBN: ${book.isbn})' : ''); } class _AdEditingWidgetState extends State { @@ -40,23 +41,29 @@ class _AdEditingWidgetState extends State { @override void initState() { super.initState(); - final metadataFromIsbn = widget.step.metadata.entries; + final metadataFromIsbn = widget.step.metadata; var title = ''; if (metadataFromIsbn.length == 1) { - final onlyMetadata = metadataFromIsbn.single.value; - title = _bookFormatTitleAndAuthor(onlyMetadata.title!, onlyMetadata.authors); + final onlyMetadata = metadataFromIsbn.single; + title = _bookFormat(onlyMetadata); } - var description = _getDescription(metadataFromIsbn); - description += '\n\n' + personal_info.customMessage; + var description = ''; - final keywords = metadataFromIsbn.map((entry) => entry.value.keywords).expand((kw) => kw).toSet().join(', '); + final bookTitles = metadataFromIsbn.map((md) => _bookFormat(md, withISBN: true)).join('\n'); + description += bookTitles + '\n\n'; + + _getDescription(metadataFromIsbn)?.let((d) => description += d + '\n\n'); + + description += personal_info.customMessage; + + final keywords = metadataFromIsbn.map((entry) => entry.keywords).expand((kw) => kw).toSet().join(', '); if (keywords.isNotEmpty) { description += '\n\nMots-clés:\n' + keywords; } - final totalPriceIncludingShipping = metadataFromIsbn.map((e) => e.value.priceCent ?? 0).sum; + final totalPriceIncludingShipping = metadataFromIsbn.map((e) => e.priceCent ?? 0).sum; final weightGramsWithWrapping = (widget.step.bundle.metadata.weightGrams! * 1.2).toInt(); var totalPriceExcludingShipping = totalPriceIncludingShipping - _estimatedShippingCost(grams: weightGramsWithWrapping); @@ -71,27 +78,21 @@ class _AdEditingWidgetState extends State { imgsPath: widget.step.bundle.compressedImages.map((e) => e.path).toList()); } - String _getDescription(Iterable> metadataFromIsbn) { - final booksWithBlurb = metadataFromIsbn.where((entry) => entry.value.blurb?.isNotEmpty == true); + String? _getDescription(Iterable metadataFromIsbn) { + final booksWithBlurb = metadataFromIsbn.where((entry) => entry.blurb?.isNotEmpty == true); if (booksWithBlurb.length == 0) { - return ''; + return null; } else if (booksWithBlurb.length == 1) { - final onlyBookWithBlurb = booksWithBlurb.single.value; + final onlyBookWithBlurb = booksWithBlurb.single; String titleAndAuthor = ''; // Even if only one book has a blurb, multiple book are in the same ad, so we need to specify which book this blurb is about if (metadataFromIsbn.length > 1) { - titleAndAuthor = _bookFormatTitleAndAuthor(onlyBookWithBlurb.title!, onlyBookWithBlurb.authors) + '\n'; + titleAndAuthor = _bookFormat(onlyBookWithBlurb) + '\n'; } return 'Résumé:\n' + titleAndAuthor + onlyBookWithBlurb.blurb!; } else { - final bookTitles = - booksWithBlurb.map((entry) => _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors)).join('\n '); - final blurbs = booksWithBlurb - .map((entry) => - _bookFormatTitleAndAuthor(entry.value.title!, entry.value.authors) + ':\n' + entry.value.blurb!) - .join('\n'); - final description = bookTitles + '\n\nRésumés:\n' + blurbs; - return description; + final blurbs = booksWithBlurb.map((entry) => _bookFormat(entry) + ':\n' + entry.blurb!).join('\n\n'); + return 'Résumés:\n' + blurbs; } } diff --git a/lib/enrichment/enrichment.dart b/lib/enrichment/enrichment.dart index 475ace9..2d642a6 100644 --- a/lib/enrichment/enrichment.dart +++ b/lib/enrichment/enrichment.dart @@ -19,7 +19,7 @@ class MetadataCollectingStep implements BookyStep { class AdEditingStep implements BookyStep { Bundle bundle; - Map metadata = {}; + Iterable metadata = {}; AdEditingStep({required this.bundle, required this.metadata}); } diff --git a/lib/enrichment/metadata_collecting.dart b/lib/enrichment/metadata_collecting.dart index 6ce309a..18b8670 100644 --- a/lib/enrichment/metadata_collecting.dart +++ b/lib/enrichment/metadata_collecting.dart @@ -73,20 +73,22 @@ class _BooksMetadataCollectingWidgetState extends State AdEditingWidget( step: AdEditingStep( bundle: widget.step.bundle, - metadata: controllers.map((key, value) => MapEntry( - key, - BookMetaDataManual( - title: value.bookControllerSet.titleTextFieldController.text, - authors: _stringToAuthors( - value.bookControllerSet.authorsTextFieldController.text), - blurb: value.bookControllerSet.blurbTextFieldController.text, - keywords: _stringToKeywords( - value.bookControllerSet.keywordsTextFieldController.text), - priceCent: double.parse( - value.bookControllerSet.priceTextFieldController.text) - .multiply(100) - .round(), - ))))))); + metadata: controllers.entries.map((entry) { + final bookControllerSet = entry.value.bookControllerSet; + return BookMetaDataManual( + isbn: entry.key, + title: bookControllerSet.titleTextFieldController.text, + authors: _stringToAuthors( + bookControllerSet.authorsTextFieldController.text), + blurb: bookControllerSet.blurbTextFieldController.text, + keywords: _stringToKeywords( + bookControllerSet.keywordsTextFieldController.text), + priceCent: + double.parse(bookControllerSet.priceTextFieldController.text) + .multiply(100) + .round(), + ); + }))))); }, child: const Text('Validate Metadatas')), ) diff --git a/lib/helpers.dart b/lib/helpers.dart index fa1d145..2313b49 100644 --- a/lib/helpers.dart +++ b/lib/helpers.dart @@ -126,6 +126,7 @@ extension IterableStringExt on Iterable { } class BookMetaDataManual { + String isbn; String? title; List authors; String? blurb; @@ -133,6 +134,7 @@ class BookMetaDataManual { int? priceCent; BookMetaDataManual({ + required this.isbn, this.title, required this.authors, this.blurb, From 8c49f6bdfefb25d23a8e619c8d834d00ca50d576 Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Mon, 5 Jun 2023 19:38:05 +0200 Subject: [PATCH 147/150] Rust: Booksprice: remove lints --- native/src/api.rs | 1 - native/src/booksprice/request.rs | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/native/src/api.rs b/native/src/api.rs index 6b877b7..a4423a6 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -10,7 +10,6 @@ use crate::publisher::Publisher; use crate::{abebooks, babelio, booksprice, google_books, leboncoin, leslibraires}; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use serde_json::json; use strum::IntoEnumIterator; use strum_macros::EnumIter; diff --git a/native/src/booksprice/request.rs b/native/src/booksprice/request.rs index ab65fdf..97ca99d 100644 --- a/native/src/booksprice/request.rs +++ b/native/src/booksprice/request.rs @@ -36,11 +36,13 @@ async fn extract_price_from_url( ) -> Result, WebDriverError> { c.goto(&url).await?; - let wait_res = c + let chart_element_exists = c .query(By::XPath("//*[@id='chart']")) .wait(Duration::from_secs(10), Duration::from_secs(1)) .exists() - .await; + .await?; + assert!(chart_element_exists); + let source_file = c.source().await.unwrap(); if let Some(cache_file_path) = cache_file_path { @@ -70,7 +72,7 @@ async fn extract_price_from_url( .await .unwrap(); - c.close_window().await; + c.close_window().await?; Ok(prices) } From 282a2992b6d026fb3a46f9617d40bb8d184cc96d Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 6 Jun 2023 19:36:22 +0200 Subject: [PATCH 148/150] Fix lesLibraires test --- native/src/leslibraires/parser.rs | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/native/src/leslibraires/parser.rs b/native/src/leslibraires/parser.rs index 3086f5f..a6cd630 100644 --- a/native/src/leslibraires/parser.rs +++ b/native/src/leslibraires/parser.rs @@ -35,22 +35,25 @@ pub fn extract_metadata(isbn_search_result: &str) -> Option().unwrap() + shipping_price - }); + let price_with_shipping = doc + .select(&price_selector) + .exactly_one() + .ok() + .map(|price_element| { + price_element + .first_child() + .unwrap() + .value() + .as_text() + .unwrap() + .to_string() + .parse::() + .unwrap() + + shipping_price + }); Some(common::BookMetaDataFromProvider { title, @@ -85,7 +88,7 @@ mod tests { first_name: "".to_owned(), last_name: "Isabelle Filliozat".to_owned() }], - market_price: vec![12.00], + market_price: vec![22.00], ..Default::default() }) ); From 86a543950e5ee14c7f6f620ba8c08358876e12ca Mon Sep 17 00:00:00 2001 From: Julien Gautier Date: Tue, 6 Jun 2023 22:07:23 +0200 Subject: [PATCH 149/150] Rust: JustBooks: add new provider --- README.md | 25 +- lib/bridge_definitions.dart | 1 + native/src/api.rs | 6 +- native/src/bridge_generated.rs | 2 + native/src/justbooks.rs | 16 + native/src/justbooks/parser.rs | 108 +++ native/src/justbooks/request.rs | 18 + native/src/justbooks/test/9782298086294.html | 721 +++++++++++++++++++ native/src/justbooks/test/9782953189018.html | 549 ++++++++++++++ native/src/lib.rs | 10 +- 10 files changed, 1436 insertions(+), 20 deletions(-) create mode 100644 native/src/justbooks.rs create mode 100644 native/src/justbooks/parser.rs create mode 100644 native/src/justbooks/request.rs create mode 100644 native/src/justbooks/test/9782298086294.html create mode 100644 native/src/justbooks/test/9782953189018.html diff --git a/README.md b/README.md index 00883ce..00c6f54 100644 --- a/README.md +++ b/README.md @@ -35,18 +35,19 @@ BookMetaData { ### Sources | Source | Metadata (in addition to title and authors) | Notes | -|-------------------------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Babelio](https://www.babelio.com/) | blurb, keyword | No API available. No plan to build one.
    Babelio seem to block the IP if it detect this bot is doing some scrapping | -| [Decitre](https://www.decitre.fr/) | blurb, keywords in commentaries | | -| [GoodReads](https://www.goodreads.com/) | blurb, genres in english | An API was available, but GoodRead does not create new developer key. [See this](https://help.goodreads.com/s/article/Does-Goodreads-support-the-use-of-APIs) | -| [Google Books](https://www.google.fr/books/) | blurb, genres | [A real API](https://developers.google.com/books/docs/overview) is available to look up a book by ISBN
    Some book can't be search by ISBN, even though a search by title can find them, and they display the right ISBN | -| [ISBSearcher](https://www.isbnsearcher.com/) | blurb, main category in english | | -| [Label Emmaus](https://www.label-emmaus.co/) | blurb, genres | | -| [OpenLibrary](https://openlibrary.org/) | blurb are not translated | Its is based on physical books, it is not really a book database | -| [Chasse Aux Livre](https://www.chasse-aux-livres.fr/) | price only | it is not possible to parse with Selenium | -| [AbeBooks](https://www.abebooks.fr/) | Seems to have good french blurb | | -| [Fnac](https://www.fnac.com/) | blurb, second-hand price | | -| [Librarie Kleber](https://www.librairie-kleber.com/) | blurb, price | | +|-------------------------------------------------------|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Babelio](https://www.babelio.com/) | blurb, keyword | No API available. No plan to build one.
    Babelio seem to block the IP if it detect this bot is doing some scrapping | +| [Decitre](https://www.decitre.fr/) | blurb, keywords in commentaries | | +| [GoodReads](https://www.goodreads.com/) | blurb, genres in english | An API was available, but GoodRead does not create new developer key. [See this](https://help.goodreads.com/s/article/Does-Goodreads-support-the-use-of-APIs) | +| [Google Books](https://www.google.fr/books/) | blurb, genres | [A real API](https://developers.google.com/books/docs/overview) is available to look up a book by ISBN
    Some book can't be search by ISBN, even though a search by title can find them, and they display the right ISBN | +| [ISBSearcher](https://www.isbnsearcher.com/) | blurb, main category in english | | +| [Label Emmaus](https://www.label-emmaus.co/) | blurb, genres | | +| [OpenLibrary](https://openlibrary.org/) | blurb are not translated | Its is based on physical books, it is not really a book database | +| [Chasse Aux Livre](https://www.chasse-aux-livres.fr/) | price only | it is not possible to parse with Selenium | +| [AbeBooks](https://www.abebooks.fr/) | Seems to have good french blurb | | +| [Fnac](https://www.fnac.com/) | blurb, second-hand price | | +| [Librarie Kleber](https://www.librairie-kleber.com/) | blurb, price | | +| [JustBooks](https://www.justbooks.fr/) | blurb (seldom), prices | | #### GoogleBooks GoogleBooks has some inconsistencies: diff --git a/lib/bridge_definitions.dart b/lib/bridge_definitions.dart index 8e0b4d3..4e08b92 100644 --- a/lib/bridge_definitions.dart +++ b/lib/bridge_definitions.dart @@ -130,6 +130,7 @@ enum ProviderEnum { BooksPrice, AbeBooks, LesLibraires, + JustBooks, } class ProviderMetadataPair { diff --git a/native/src/api.rs b/native/src/api.rs index a4423a6..de5ecde 100644 --- a/native/src/api.rs +++ b/native/src/api.rs @@ -3,11 +3,11 @@ use std::fs::File; use std::io::{Read, Write}; use crate::cached_client::CachedClient; -use crate::common; use crate::common::Ad; use crate::common::{LbcCredential, Provider}; use crate::publisher::Publisher; -use crate::{abebooks, babelio, booksprice, google_books, leboncoin, leslibraires}; +use crate::{abebooks, babelio, booksprice, google_books, leboncoin, leslibraires, justbooks}; +use crate::common; use itertools::Itertools; use serde::{Deserialize, Serialize}; use strum::IntoEnumIterator; @@ -20,6 +20,7 @@ pub enum ProviderEnum { BooksPrice, AbeBooks, LesLibraires, + JustBooks, } #[derive(PartialEq, Debug, Deserialize, Serialize)] @@ -180,6 +181,7 @@ pub fn get_metadata_from_provider( ProviderEnum::LesLibraires => { leslibraires::LesLibraires {}.get_book_metadata_from_isbn(&isbn) } + ProviderEnum::JustBooks => justbooks::JustBooks {}.get_book_metadata_from_isbn(&isbn), } } diff --git a/native/src/bridge_generated.rs b/native/src/bridge_generated.rs index 66c61eb..299a44a 100644 --- a/native/src/bridge_generated.rs +++ b/native/src/bridge_generated.rs @@ -149,6 +149,7 @@ impl Wire2Api for i32 { 2 => ProviderEnum::BooksPrice, 3 => ProviderEnum::AbeBooks, 4 => ProviderEnum::LesLibraires, + 5 => ProviderEnum::JustBooks, _ => unreachable!("Invalid variant for ProviderEnum: {}", self), } } @@ -218,6 +219,7 @@ impl support::IntoDart for ProviderEnum { Self::BooksPrice => 2, Self::AbeBooks => 3, Self::LesLibraires => 4, + Self::JustBooks => 5, } .into_dart() } diff --git a/native/src/justbooks.rs b/native/src/justbooks.rs new file mode 100644 index 0000000..06d0fb4 --- /dev/null +++ b/native/src/justbooks.rs @@ -0,0 +1,16 @@ +use crate::{cached_client::CachedClient, common}; +mod parser; +mod request; + +pub struct JustBooks; + +impl common::Provider for JustBooks { + fn get_book_metadata_from_isbn(&self, isbn: &str) -> Option { + let client = reqwest::blocking::Client::builder().build().unwrap(); + let cached_client = CachedClient { + http_client: client, + }; + let book_page = request::get_book_page(&cached_client, isbn); + parser::extract_metadata(&book_page) + } +} diff --git a/native/src/justbooks/parser.rs b/native/src/justbooks/parser.rs new file mode 100644 index 0000000..63dcf03 --- /dev/null +++ b/native/src/justbooks/parser.rs @@ -0,0 +1,108 @@ +use crate::common::{html_select, BookMetaDataFromProvider}; +use itertools::Itertools; + +fn extract_author(author_scope: scraper::ElementRef) -> crate::common::Author { + let author_span = author_scope + .first_child() + .expect("author scope > span shoud have a first child"); + + crate::common::Author { + first_name: author_span + .value() + .as_text() + .expect("Should be a text") + .trim() + .to_string(), + last_name: "".to_string(), + } +} + +pub fn extract_metadata(html: &str) -> Option { + let doc = scraper::Html::parse_document(html); + + let book_select = html_select("div[itemscope][itemtype=\"http://schema.org/Book\"]"); + let res = doc.select(&book_select); + let book_scope = match res.exactly_one() { + Ok(book_scope) => book_scope, + Err(_) => { + eprintln!("Response should contain a element whose with id is itemscope and itemtype=\"https://schema.org/Book\""); + return None; + } + }; + let title_select = html_select("[itemprop=\"name\"]"); + let title = book_scope + .select(&title_select) + .exactly_one() + .expect("There should be exactly one element with itemprop=\"name\"") + .first_child() + .unwrap() + .value() + .as_text() + .unwrap() + .trim() + .to_string(); + + let authors_select = html_select("[itemprop=\"author\"]"); + let authors = book_scope + .select(&authors_select) + .map(extract_author) + .collect_vec(); + + let blurb = book_scope + .select(&html_select("[itemprop=\"description\"]")) + .at_most_one() + .unwrap() + .map(|d| { + d.first_child() + .unwrap() + .value() + .as_text() + .unwrap() + .trim() + .to_string() + }); + + Some(BookMetaDataFromProvider { + title: Some(title), + authors, + blurb, + ..Default::default() + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn extract_metadata_with_blurb() { + let html = std::fs::read_to_string("src/justbooks/test/9782953189018.html").unwrap(); + let md = extract_metadata(&html); + assert_eq!(md, Some(BookMetaDataFromProvider { + title: Some("La prière en sept chapitres par PADMASAMBHAVA".to_string()), + authors: vec![crate::common::Author { + first_name: "Tchimé Rigdzin Rinpotché; James Low".to_string(), + last_name: "".to_string() + }], + blurb: Some("Traduction : Chhimed Rigdzin Rinpoche et James Low Tirées du Terma du Nord (Tchang Ter), ces prières furent écrites par Padmasambhava à la requête de ses cinq principaux disciples (Yéshé Tsogyel, Trisong Deutsen, etc.). On y retrouve le célèbre Sampa Lhundroup (prière qui exauce tous les souhaits) et le Bartché Namsel (prière qui élimine tous les obstacles). Avec texte en tibétain, phonétique, traduction mot à mot et traduction du vers. Introduction de James Low sur la foi et la dévotion dans le bouddhisme tibétain. Relié, 322 pages".to_owned()), + keywords: vec![], + market_price: vec![], + })); + } + #[test] + + fn extract_metadata_without_blurb() { + let html = std::fs::read_to_string("src/justbooks/test/9782298086294.html").unwrap(); + let md = extract_metadata(&html); + assert_eq!( + md, + Some(BookMetaDataFromProvider { + title: Some("1918 la terrible victoire".to_string()), + authors: vec![], + blurb: None, + keywords: vec![], + market_price: vec![], + }) + ); + } +} diff --git a/native/src/justbooks/request.rs b/native/src/justbooks/request.rs new file mode 100644 index 0000000..1ea2642 --- /dev/null +++ b/native/src/justbooks/request.rs @@ -0,0 +1,18 @@ +use crate::cached_client::Client; + +pub fn get_book_page(client: &dyn Client, isbn: &str) -> String { + client.make_request( + format!("justbooks/get_book_url_{}.html", isbn).as_str(), + &|http_client| { + http_client + .get(format!( + "https://www.justbooks.fr/search/?isbn={}&st=xl&ac=qr", + &isbn + )) + .send() + .unwrap() + .text() + .unwrap() + }, + ) +} diff --git a/native/src/justbooks/test/9782298086294.html b/native/src/justbooks/test/9782298086294.html new file mode 100644 index 0000000..1463796 --- /dev/null +++ b/native/src/justbooks/test/9782298086294.html @@ -0,0 +1,721 @@ + + + + + + + + + +1918 la terrible victoire (9782298086294) + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + + +
    +
    + +
    +
    + + + + + +
    +
    + +
    +
    +
    + L'ISBN est

    9782298086294 / 229808629X

    +
    + + + +
    + + + +
    +
    3.68 Note moyenne25 avis fournis par GoodReads

    + +
    + Les prix comprennent des frais de port standards vers France +
    + + +
    + Les frais de port peuvent être approximatifs. Veuillez les vérifier avant d'accepter votre commande. +
    +
    +
    +
    + + + +
    +Cliquez sur le prix d'un livre pour accéder à la description de celui-ci.
    +
    +
        +

    Livres d'occasion: 1 - 27 de 27

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    + +
    + + + + + + + diff --git a/native/src/justbooks/test/9782953189018.html b/native/src/justbooks/test/9782953189018.html new file mode 100644 index 0000000..5a4ba24 --- /dev/null +++ b/native/src/justbooks/test/9782953189018.html @@ -0,0 +1,549 @@ + + + + + + + + + +La prière en sept chapitres par PADMASAMBHAVA (9782953189018) de Tchimé Rigdzin Rinpot... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + + + + + +
    +
    + +
    +
    +
    + L'ISBN est

    9782953189018 / 2953189017

    +
    + + + +

    + Editeur:Khordong France, 2009 +

    +

    + Edition:Couverture rigide +

    +
    + Les prix comprennent des frais de port standards vers France +
    + + +
    + Les frais de port peuvent être approximatifs. Veuillez les vérifier avant d'accepter votre commande. +
    +
    +
    +

    Résumé de livre:

    +
    Traduction : Chhimed Rigdzin Rinpoche et James Low Tirées du Terma du Nord (Tchang Ter), ces prières furent écrites par Padmasambhava à la requête de ses cinq principaux disciples (Yéshé Tsogyel, Trisong Deutsen, etc.). On y retrouve le célèbre Sampa Lhundroup (prière qui exauce tous les souhaits) et le Bartché Namsel (prière qui élimine tous les obstacles). Avec texte en tibétain, phonétique, traduction mot à mot et traduction du vers. Introduction de James Low sur la foi et la dévotion dans le bouddhisme tibétain. Relié, 322 pages
    +
    + + + +

    Cette édition n'est actuellement pas disponible. Chercher parmi toutes les éditions de ce livre

    + + + + + +
    + +
    + + + + + + + diff --git a/native/src/lib.rs b/native/src/lib.rs index 41fdf89..d07fd28 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -1,17 +1,15 @@ - -// #[macro_use] -// extern crate thirtyfour; +mod abebooks; mod api; mod babelio; +mod booksprice; mod bridge_generated; mod cached_client; mod common; mod config; mod google_books; mod image_tools; +mod justbooks; mod jwt_decoder; mod leboncoin; -mod publisher; -mod booksprice; -mod abebooks; mod leslibraires; +mod publisher; From 377be63ccf6fd2894458a52274eda6832dabd6f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 20:10:46 +0000 Subject: [PATCH 150/150] Bump flutter_image_compress from 1.1.3 to 2.0.3 Bumps [flutter_image_compress](https://github.com/fluttercandies/flutter_image_compress) from 1.1.3 to 2.0.3. - [Release notes](https://github.com/fluttercandies/flutter_image_compress/releases) - [Commits](https://github.com/fluttercandies/flutter_image_compress/commits) --- updated-dependencies: - dependency-name: flutter_image_compress dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index b17320f..8fcf27f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,7 +32,7 @@ dependencies: permission_handler: ^10.2.0 google_mlkit_commons: ^0.3.0 google_mlkit_barcode_scanning: ^0.6.0 - flutter_image_compress: ^1.1.3 + flutter_image_compress: ^2.0.3 image: ^4.0.17 vector_math: ^2.1.4