Replace small example models, and add example geomodel (#2644)

* Update env.example

* Update model download script

* Delete geomodel.placeholder

* Update README.md

* Update e2e_ios.yml

* Update vision-plugin

* Accept android flavor for downloading models

* Change Android e2e env

* Replace Android e2e model download step

* Update comment

* Move Java setup step

* Revert "Move Java setup step"

This reverts commit d8ca01a176.
This commit is contained in:
Johannes Klein
2025-02-03 12:16:22 +01:00
committed by GitHub
parent 7e19fd58dc
commit c378c8894d
10 changed files with 144 additions and 157 deletions

View File

@@ -74,7 +74,7 @@ jobs:
E2E_TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} E2E_TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }}
JWT_ANONYMOUS_API_SECRET: ${{ secrets.JWT_ANONYMOUS_API_SECRET }} JWT_ANONYMOUS_API_SECRET: ${{ secrets.JWT_ANONYMOUS_API_SECRET }}
GMAPS_API_KEY: ${{ secrets.GMAPS_API_KEY }} GMAPS_API_KEY: ${{ secrets.GMAPS_API_KEY }}
run: printf 'API_URL=https://stagingapi.inaturalist.org/v2\nOAUTH_API_URL=https://staging.inaturalist.org\nJWT_ANONYMOUS_API_SECRET=%s\nOAUTH_CLIENT_ID=%s\nOAUTH_CLIENT_SECRET=%s\nE2E_TEST_USERNAME=%s\nE2E_TEST_PASSWORD=%s\nGMAPS_API_KEY=%s\nANDROID_MODEL_FILE_NAME=small_inception_tf1.tflite\nANDROID_TAXONOMY_FILE_NAME=small_export_tax.csv\nIOS_MODEL_FILE_NAME=small_inception_tf1.mlmodel\nIOS_TAXONOMY_FILE_NAME=small_export_tax.json\n' "$JWT_ANONYMOUS_API_SECRET" "$OAUTH_CLIENT_ID" "$OAUTH_CLIENT_SECRET" "$E2E_TEST_USERNAME" "$E2E_TEST_PASSWORD" "$GMAPS_API_KEY" > .env run: printf 'API_URL=https://stagingapi.inaturalist.org/v2\nOAUTH_API_URL=https://staging.inaturalist.org\nJWT_ANONYMOUS_API_SECRET=%s\nOAUTH_CLIENT_ID=%s\nOAUTH_CLIENT_SECRET=%s\nE2E_TEST_USERNAME=%s\nE2E_TEST_PASSWORD=%s\nGMAPS_API_KEY=%s\nANDROID_MODEL_FILE_NAME=INatVision_Small_2_fact256_8bit.tflite\nANDROID_TAXONOMY_FILE_NAME=taxonomy.csv\nANDROID_GEOMODEL_FILE_NAME=INatGeomodel_Small_2_8bit.tflite\nIOS_MODEL_FILE_NAME=INatVision_Small_2_fact256_8bit.mlmodel\nIOS_TAXONOMY_FILE_NAME=taxonomy.json\nIOS_GEOMODEL_FILE_NAME=INatGeomodel_Small_2_8bit.mlmodel\nCV_MODEL_VERSION=small_2\n' "$JWT_ANONYMOUS_API_SECRET" "$OAUTH_CLIENT_ID" "$OAUTH_CLIENT_SECRET" "$E2E_TEST_USERNAME" "$E2E_TEST_PASSWORD" "$GMAPS_API_KEY" > .env
- name: Create keystore.properties file - name: Create keystore.properties file
env: env:
ANDROID_KEY_STORE_PASSWORD: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} ANDROID_KEY_STORE_PASSWORD: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
@@ -92,8 +92,8 @@ jobs:
run: mv release.keystore android/app/release.keystore run: mv release.keystore android/app/release.keystore
# Download the example model otherwise an error alert will be shown on app start, requires .env # Download the example model otherwise an error alert will be shown on app start, requires .env
- name: Download a fake cv model and taxonomy file into the assets folder - name: Download the small example cv and geomodel
run: npm run add-github-actions-test-model run: npm run add-example-model -- -f=main
# Macos-latest runner has 3 Java versions pre-installed, if not specified as here, the build step errors with requiring at least Java 11 or higher # Macos-latest runner has 3 Java versions pre-installed, if not specified as here, the build step errors with requiring at least Java 11 or higher
# So, this step is needed for the apk build step, but somehow this is breaking emulator setup, so it is placed here # So, this step is needed for the apk build step, but somehow this is breaking emulator setup, so it is placed here

View File

@@ -87,7 +87,7 @@ jobs:
E2E_TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }} E2E_TEST_PASSWORD: ${{ secrets.E2E_TEST_PASSWORD }}
JWT_ANONYMOUS_API_SECRET: ${{ secrets.JWT_ANONYMOUS_API_SECRET }} JWT_ANONYMOUS_API_SECRET: ${{ secrets.JWT_ANONYMOUS_API_SECRET }}
run: | run: |
printf 'API_URL=https://stagingapi.inaturalist.org/v2\nOAUTH_API_URL=https://staging.inaturalist.org\nJWT_ANONYMOUS_API_SECRET=%s\nOAUTH_CLIENT_ID=%s\nOAUTH_CLIENT_SECRET=%s\nE2E_TEST_USERNAME=%s\nE2E_TEST_PASSWORD=%s\nGMAPS_API_KEY=%s\nANDROID_MODEL_FILE_NAME=small_inception_tf1.tflite\nANDROID_TAXONOMY_FILE_NAME=small_export_tax.csv\nIOS_MODEL_FILE_NAME=small_inception_tf1.mlmodel\nIOS_TAXONOMY_FILE_NAME=small_export_tax.json\nCV_MODEL_VERSION=2.13\n' \ printf 'API_URL=https://stagingapi.inaturalist.org/v2\nOAUTH_API_URL=https://staging.inaturalist.org\nJWT_ANONYMOUS_API_SECRET=%s\nOAUTH_CLIENT_ID=%s\nOAUTH_CLIENT_SECRET=%s\nE2E_TEST_USERNAME=%s\nE2E_TEST_PASSWORD=%s\nGMAPS_API_KEY=%s\nANDROID_MODEL_FILE_NAME=INatVision_Small_2_fact256_8bit.tflite\nANDROID_TAXONOMY_FILE_NAME=taxonomy.csv\nANDROID_GEOMODEL_FILE_NAME=INatGeomodel_Small_2_8bit.tflite\nIOS_MODEL_FILE_NAME=INatVision_Small_2_fact256_8bit.mlmodel\nIOS_TAXONOMY_FILE_NAME=taxonomy.json\nIOS_GEOMODEL_FILE_NAME=INatGeomodel_Small_2_8bit.mlmodel\nCV_MODEL_VERSION=small_2\n' \
"$JWT_ANONYMOUS_API_SECRET" \ "$JWT_ANONYMOUS_API_SECRET" \
"$OAUTH_CLIENT_ID" \ "$OAUTH_CLIENT_ID" \
"$OAUTH_CLIENT_SECRET" \ "$OAUTH_CLIENT_SECRET" \

View File

@@ -20,14 +20,9 @@ See [CONTRIBUTING](CONTRIBUTING.md) for guidelines on contributing to this proje
1. To run on Android, do this `cp android/example-keystore.properties android/keystore.properties`. Fill in the relevant values. If you are a member of iNat staff, get them from another member of iNat Staff. 1. To run on Android, do this `cp android/example-keystore.properties android/keystore.properties`. Fill in the relevant values. If you are a member of iNat staff, get them from another member of iNat Staff.
1. Add AI Camera model and taxonomy files. The computer vision model and Geomodel files are not part of the code repo, and have to be installed. The app itself will load the model file with the filename specified in a .env file. On Android, the current file names are specified in these env variables `ANDROID_MODEL_FILE_NAME`, `ANDROID_TAXONOMY_FILE_NAME`, and `ANDROID_GEOMODEL_FILE_NAME`. On iOS, the current file names are specified in these env variables `IOS_MODEL_FILE_NAME`, `IOS_TAXONOMY_FILE_NAME`, and `IOS_GEOMODEL_FILE_NAME`. After a fresh clone of the repo and copying the env.example file (see above), you have to add the files by following these steps: 1. Add AI Camera model and taxonomy files. The computer vision model and Geomodel files are not part of the code repo, and have to be installed. The app itself will load the model file with the filename specified in a .env file. On Android, the current file names are specified in these env variables `ANDROID_MODEL_FILE_NAME`, `ANDROID_TAXONOMY_FILE_NAME`, and `ANDROID_GEOMODEL_FILE_NAME`. On iOS, the current file names are specified in these env variables `IOS_MODEL_FILE_NAME`, `IOS_TAXONOMY_FILE_NAME`, and `IOS_GEOMODEL_FILE_NAME`. After a fresh clone of the repo and copying the env.example file (see above), you have to add the files by following these steps:
1. Add the example model files by executing `npm run add-example-model`. If that does not work continue with the next step. 1. Add the example model files by executing `npm run add-example-model`. If that does not work continue with the next step.
1. If the download script fails: The sample model files are available in this [`small_model.zip`](https://github.com/inaturalist/SeekReactNative/releases/tag/v2.9.1-138) file. 1. If the download script fails: The sample model files are available in the latest release in this [`repository`](https://github.com/inaturalist/model-files).
1. On Android, these files are named `small_inception_tf1.tflite` and `small_export_tax.csv`. Create a camera folder within Android assets (i.e. `android/app/src/debug/assets/camera`) and place the files there. 1. On Android, these files are named `INatVision_Small_2_fact256_8bit.tflite`, `INatGeomodel_Small_2_8bit.tflite` and `taxonomy.csv`. Create a camera folder within Android assets (i.e. `android/app/src/debug/assets/camera`) and place the files there.
1. On iOS, these files are named `small_inception_tf1.mlmodel` and `small_export_tax.json` and should be added to the `ios` folder. 1. On iOS, these files are named `smallINatVision_Small_2_fact256_8bit.mlmodel`, `INatGeomodel_Small_2_8bit.mlmodel` and `taxonomy.json` and should be added to the `ios` folder.
1. On iOS, in the `ios` folder, copy the Geomodel placeholder file `geomodel.placeholder` to `geomodel.mlmodel`. This file is just a placeholder to get the app to build. We'll release a functional Geomodel for development purposes soon.
1. Optional: Add Geomodel file. If you have access to the model file, you can add it by following these steps:
1. Uncomment and set the `ANDROID_GEOMODEL_FILE_NAME` and `IOS_GEOMODEL_FILE_NAME` variables in the `.env` and `.env.staging` files.
1. On Android, the file Geomodel should be placed in the `android/app/src/debug/assets/camera` folder.
1. On iOS, the Geomodel file should be placed in the `ios` folder.
### Set up pre-commit hooks ### Set up pre-commit hooks

View File

@@ -22,14 +22,13 @@ GMAPS_API_KEY=some-key
# phase in xcode to hard link these files using file names xcode knows about, # phase in xcode to hard link these files using file names xcode knows about,
# so we don't have to add these files to xcode in addition to declaring them # so we don't have to add these files to xcode in addition to declaring them
# here. # here.
ANDROID_MODEL_FILE_NAME=small_inception_tf1.tflite ANDROID_MODEL_FILE_NAME=INatVision_Small_2_fact256_8bit.tflite
ANDROID_TAXONOMY_FILE_NAME=small_export_tax.csv ANDROID_TAXONOMY_FILE_NAME=taxonomy.csv
# Android Geomodel is not implemented yet, uncommenting this can cause unforeseen problems ANDROID_GEOMODEL_FILE_NAME=INatGeomodel_Small_2_8bit.tflite
#ANDROID_GEOMODEL_FILE_NAME=small_geomodel_tf1.tflite IOS_MODEL_FILE_NAME=INatVision_Small_2_fact256_8bit.mlmodel
IOS_MODEL_FILE_NAME=small_inception_tf1.mlmodel IOS_TAXONOMY_FILE_NAME=taxonomy.json
IOS_TAXONOMY_FILE_NAME=small_export_tax.json IOS_GEOMODEL_FILE_NAME=INatGeomodel_Small_2_8bit.mlmodel
#IOS_GEOMODEL_FILE_NAME=small_geomodel_tf1.mlmodel CV_MODEL_VERSION=small_2
CV_MODEL_VERSION=1.0
# Fastlane # Fastlane
IOS_PROVISIONING_PROFILE_NAME="provisioning profile name" IOS_PROVISIONING_PROFILE_NAME="provisioning profile name"

View File

@@ -1206,7 +1206,7 @@ PODS:
- VisionCamera/React (4.0.5): - VisionCamera/React (4.0.5):
- React-Core - React-Core
- VisionCamera/FrameProcessors - VisionCamera/FrameProcessors
- VisionCameraPluginInatVision (4.2.1): - VisionCameraPluginInatVision (4.2.2):
- React-Core - React-Core
- Yoga (1.14.0) - Yoga (1.14.0)
@@ -1616,7 +1616,7 @@ SPEC CHECKSUMS:
RNVectorIcons: 102cd20472bf0d7cd15443d43cd87f9c97228ac3 RNVectorIcons: 102cd20472bf0d7cd15443d43cd87f9c97228ac3
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
VisionCamera: f02de0b1b6b1516b327bd8215237a97e7386db8a VisionCamera: f02de0b1b6b1516b327bd8215237a97e7386db8a
VisionCameraPluginInatVision: f2e065009ed10375293fe343834c6d946e884a8a VisionCameraPluginInatVision: 69370c8be26b9b7372996d423f4fdb4c94e42ab1
Yoga: c716aea2ee01df6258550c7505fa61b248145ced Yoga: c716aea2ee01df6258550c7505fa61b248145ced
PODFILE CHECKSUM: eff4b75123af5d6680139a78c055b44ad37c269b PODFILE CHECKSUM: eff4b75123af5d6680139a78c055b44ad37c269b

View File

Binary file not shown.

8
package-lock.json generated
View File

@@ -104,7 +104,7 @@
"sanitize-html": "^2.13.0", "sanitize-html": "^2.13.0",
"ts-jest": "^29.1.2", "ts-jest": "^29.1.2",
"uuid": "^11.0.5", "uuid": "^11.0.5",
"vision-camera-plugin-inatvision": "github:inaturalist/vision-camera-plugin-inatvision#dcad11b1532cb8b777eaaf78d6df0a704e8c71d3", "vision-camera-plugin-inatvision": "github:inaturalist/vision-camera-plugin-inatvision#fb3c35d3b19c9bdeb4c0de359f3c1b8133b2a8eb",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
"devDependencies": { "devDependencies": {
@@ -20703,9 +20703,9 @@
} }
}, },
"node_modules/vision-camera-plugin-inatvision": { "node_modules/vision-camera-plugin-inatvision": {
"version": "4.2.1", "version": "4.2.2",
"resolved": "git+ssh://git@github.com/inaturalist/vision-camera-plugin-inatvision.git#dcad11b1532cb8b777eaaf78d6df0a704e8c71d3", "resolved": "git+ssh://git@github.com/inaturalist/vision-camera-plugin-inatvision.git#fb3c35d3b19c9bdeb4c0de359f3c1b8133b2a8eb",
"integrity": "sha512-PRgGpmWL6/XIYAh6xS50YrHxoUQxSlWWzPdDYHzcYBct9gBESCF0NUeFctWeqCJGjDcmL7RKlCYdizivZyiPmQ==", "integrity": "sha512-pybIhPdLCgDOVurJmoDpnGxlaip6JLs8gAiHHPgy/NcO3aljJaWJhLqOex8vd4Ofk1G4mKc7Q528/Yg+1qqQZA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"h3-js": "^4.1.0" "h3-js": "^4.1.0"

View File

@@ -39,7 +39,6 @@
"e2e": "npm run e2e:build && npm run e2e:test", "e2e": "npm run e2e:build && npm run e2e:test",
"icons": "./scripts/update-icon-font.sh", "icons": "./scripts/update-icon-font.sh",
"add-example-model": "node scripts/add-example-model.js", "add-example-model": "node scripts/add-example-model.js",
"add-github-actions-test-model": "node scripts/add-github-actions-test-model.js",
"reassure": "reassure" "reassure": "reassure"
}, },
"dependencies": { "dependencies": {
@@ -138,7 +137,7 @@
"sanitize-html": "^2.13.0", "sanitize-html": "^2.13.0",
"ts-jest": "^29.1.2", "ts-jest": "^29.1.2",
"uuid": "^11.0.5", "uuid": "^11.0.5",
"vision-camera-plugin-inatvision": "github:inaturalist/vision-camera-plugin-inatvision#dcad11b1532cb8b777eaaf78d6df0a704e8c71d3", "vision-camera-plugin-inatvision": "github:inaturalist/vision-camera-plugin-inatvision#fb3c35d3b19c9bdeb4c0de359f3c1b8133b2a8eb",
"zustand": "^4.5.2" "zustand": "^4.5.2"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -2,64 +2,140 @@
const { DownloaderHelper } = require( "node-downloader-helper" ); const { DownloaderHelper } = require( "node-downloader-helper" );
const fs = require( "fs" ).promises; const fs = require( "fs" ).promises;
const path = require( "path" ); const path = require( "path" );
// eslint-disable-next-line import/no-extraneous-dependencies const yargs = require( "yargs" );
const Decompress = require( "decompress" );
const filename = "small_model.zip"; const binariesBaseDir
const modelURL = "https://github.com/inaturalist/model-files/releases/download/v25.01.15";
= "https://github.com/inaturalist/SeekReactNative/releases/download/v2.9.1-138/small_model.zip";
const modelPath = path.join( __dirname, "..", "temp", "model" ); const androidExt = "tflite";
const examplePath = path.join( modelPath, "tf1 2" ); const iosExt = "mlmodel";
const androidModelFile = "small_inception_tf1.tflite"; const cvModelFilename = "INatVision_Small_2_fact256_8bit";
const androidTaxonomyFile = "small_export_tax.csv"; const geomodelFilename = "INatGeomodel_Small_2_8bit";
const iosModelFile = "small_inception_tf1.mlmodel";
const iosTaxonomyFile = "small_export_tax.json";
const androidModelPath = path.join( examplePath, androidModelFile );
const androidTaxonomyPath = path.join( examplePath, androidTaxonomyFile );
const iosModelPath = path.join( examplePath, iosModelFile );
const iosTaxonomyPath = path.join( examplePath, iosTaxonomyFile );
const androidDestinationPath const androidCV = `${binariesBaseDir}/${cvModelFilename}.${androidExt}`;
= path.join( __dirname, "..", "android", "app", "src", "debug", "assets", "camera" ); const iosCV = `${binariesBaseDir}/${cvModelFilename}.${iosExt}`;
const iosDestinationPath = path.join( __dirname, "..", "ios" ); const androidGeo = `${binariesBaseDir}/${geomodelFilename}.${androidExt}`;
const iosGeo = `${binariesBaseDir}/${geomodelFilename}.${iosExt}`;
const taxonomyCSV = `${binariesBaseDir}/taxonomy.csv`;
const taxonomyJSON = `${binariesBaseDir}/taxonomy.json`;
( async () => { const downloadAndroid = async argv => {
console.log( `Downloading example model from '${modelURL}'...` ); console.log( "argv", argv );
await fs.mkdir( modelPath, { recursive: true } ); const androidFlavor = argv.androidFlavor || "debug";
const dl = new DownloaderHelper( modelURL, modelPath ); console.log( "androidFlavor", androidFlavor );
const androidDestination = path.join(
__dirname,
"..",
"android",
"app",
"src",
androidFlavor,
"assets",
"camera"
);
const androidModel = path.join(
androidDestination,
`${cvModelFilename}.${androidExt}`
);
console.log( "Checking android model files..." );
let exist = true;
try {
await fs.access( androidModel );
} catch ( _ ) {
exist = false;
}
if ( exist ) {
console.log( "Android model exist!" );
return;
}
console.log(
`Android model files missing, downloading from '${binariesBaseDir}'...`
);
await fs.mkdir( androidDestination, { recursive: true } );
const dl = new DownloaderHelper( androidCV, androidDestination );
dl.on( "end", () => console.log( "Download Completed" ) ); dl.on( "end", () => console.log( "Download Completed" ) );
dl.on( "error", err => console.log( "Download Failed", err ) ); dl.on( "error", err => console.log( "Download Failed", err ) );
await dl.start().catch( err => console.error( err ) ); await dl.start().catch( err => console.error( err ) );
console.log( "Downloaded!" ); console.log( "Downloaded!" );
const dl2 = new DownloaderHelper( androidGeo, androidDestination );
dl2.on( "end", () => console.log( "Download Completed" ) );
dl2.on( "error", err => console.log( "Download Failed", err ) );
await dl2.start().catch( err => console.error( err ) );
console.log( "Downloaded!" );
const dl3 = new DownloaderHelper( taxonomyCSV, androidDestination );
dl3.on( "end", () => console.log( "Download Completed" ) );
dl3.on( "error", err => console.log( "Download Failed", err ) );
await dl3.start().catch( err => console.error( err ) );
console.log( "Downloaded!" );
console.log( "Unzipping!" ); console.log( "Android done!" );
const zipPath = path.join( modelPath, filename ); };
await Decompress( zipPath, modelPath )
.then( () => {
console.log( "Done Unzipping!" );
} )
.catch( error => console.log( error ) );
console.log( "Copying model files to assets folder..." ); const downloadIOS = async () => {
await fs.mkdir( androidDestinationPath, { recursive: true } ); const iosDestination = path.join( __dirname, "..", "ios" );
await fs.copyFile( androidModelPath, path.join( androidDestinationPath, androidModelFile ) );
await fs.copyFile( const iosModel = path.join( iosDestination, `${cvModelFilename}.${iosExt}` );
androidTaxonomyPath,
path.join( androidDestinationPath, androidTaxonomyFile ) console.log( "Checking ios model files..." );
let exist = true;
try {
await fs.access( iosModel );
} catch ( _ ) {
exist = false;
}
if ( exist ) {
console.log( "ios model exist!" );
return;
}
console.log(
`iOS Model files missing, downloading from '${binariesBaseDir}'...`
); );
await fs.mkdir( iosDestinationPath, { recursive: true } ); await fs.mkdir( iosDestination, { recursive: true } );
await fs.copyFile( iosModelPath, path.join( iosDestinationPath, iosModelFile ) );
await fs.copyFile( iosTaxonomyPath, path.join( iosDestinationPath, iosTaxonomyFile ) );
console.log( "Copying Geomodel placeholder to be model file..." );
await fs.copyFile(
path.join( iosDestinationPath, "geomodel.placeholder" ),
path.join( iosDestinationPath, "geomodel.mlmodel" )
);
console.log( "Delete temp model folder and its contents..." ); const dl = new DownloaderHelper( iosCV, iosDestination );
await fs.rm( modelPath, { recursive: true } ); dl.on( "end", () => console.log( "Download Completed" ) );
dl.on( "error", err => console.log( "Download Failed", err ) );
await dl.start().catch( err => console.error( err ) );
console.log( "Downloaded!" );
const dl2 = new DownloaderHelper( iosGeo, iosDestination );
dl2.on( "end", () => console.log( "Download Completed" ) );
dl2.on( "error", err => console.log( "Download Failed", err ) );
await dl2.start().catch( err => console.error( err ) );
console.log( "Downloaded!" );
const dl3 = new DownloaderHelper( taxonomyJSON, iosDestination );
dl3.on( "end", () => console.log( "Download Completed" ) );
dl3.on( "error", err => console.log( "Download Failed", err ) );
await dl3.start().catch( err => console.error( err ) );
console.log( "Downloaded!" );
console.log( "Done!" ); console.log( "iOS done!" );
} )(); };
// eslint-disable-next-line no-unused-expressions
yargs
.usage( "Usage: $0 [args]" )
.option( "androidFlavor", {
alias: "f",
type: "string",
description: "Android flavor to download model files into"
} )
.command(
"$0",
"Download example model files if not present",
// eslint-disable-next-line @typescript-eslint/no-empty-function
() => {},
async argv => {
await downloadAndroid( argv );
await downloadIOS();
}
)
.help().argv;

View File

@@ -1,82 +0,0 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { DownloaderHelper } = require( "node-downloader-helper" );
const fs = require( "fs" ).promises;
const path = require( "path" );
// eslint-disable-next-line import/no-extraneous-dependencies
const Decompress = require( "decompress" );
require( "dotenv" ).config();
const filename = "small_model.zip";
const modelURL
= "https://github.com/inaturalist/SeekReactNative/releases/download/v2.9.1-138/small_model.zip";
const modelPath = path.join( __dirname, "..", "temp", "model" );
const androidModelPath = path.join( modelPath, "tf1 2", "small_inception_tf1.tflite" );
const androidTaxonomyPath = path.join( modelPath, "tf1 2", "small_export_tax.csv" );
const iosModelPath = path.join( modelPath, "tf1 2", "small_inception_tf1.mlmodel" );
const iosTaxonomyPath = path.join( modelPath, "tf1 2", "small_export_tax.json" );
const androidDestinationPath = path.join(
__dirname,
"..",
"android",
"app",
"src",
"main",
"assets",
"camera"
);
const iosDestinationPath = path.join( __dirname, "..", "ios" );
( async () => {
console.log( `Downloading example model from '${modelURL}'...` );
await fs.mkdir( modelPath, { recursive: true } );
const dl = new DownloaderHelper( modelURL, modelPath );
dl.on( "end", () => console.log( "Download Completed" ) );
dl.on( "error", err => console.log( "Download Failed", err ) );
await dl.start().catch( err => console.error( err ) );
console.log( "Downloaded!" );
console.log( "Unzipping!" );
const zipPath = path.join( modelPath, filename );
await Decompress( zipPath, modelPath )
.then( () => {
console.log( "Done Unzipping!" );
} )
.catch( error => console.log( error ) );
console.log( "Reading output filenames from .env file..." );
const androidModelFile = process.env.ANDROID_MODEL_FILE_NAME;
const androidTaxonomyFile = process.env.ANDROID_TAXONOMY_FILE_NAME;
const iosModelFile = process.env.IOS_MODEL_FILE_NAME;
const iosTaxonomyFile = process.env.IOS_TAXONOMY_FILE_NAME;
// TODO: donwload an example Geomodel from the internet
// NEEDS: https://github.com/inaturalist/iNaturalistMLWork/issues/146
// const iosGeoModelFile = process.env.IOS_GEOMODEL_FILE_NAME;
console.log( "Copying model files to assets folder..." );
await fs.mkdir( androidDestinationPath, { recursive: true } );
await fs.copyFile(
androidModelPath,
path.join( androidDestinationPath, androidModelFile )
);
await fs.copyFile(
androidTaxonomyPath,
path.join( androidDestinationPath, androidTaxonomyFile )
);
await fs.mkdir( iosDestinationPath, { recursive: true } );
await fs.copyFile(
iosModelPath,
path.join( iosDestinationPath, iosModelFile )
);
await fs.copyFile(
iosTaxonomyPath,
path.join( iosDestinationPath, iosTaxonomyFile )
);
console.log( "Delete temp model folder and its contents..." );
await fs.rm( modelPath, { recursive: true } );
console.log( "Done!" );
} )();