Facial Recognition Reverse Image Face Search API

Let your users search the Internet by face! Integrate FaceCheck facial search seamlessly with your app, website or software platform.
FaceCheck's REST API is hassle-free and easy to use. 1 search costs 3 credits. Purchase credits with crypto for $0.10 USD per credit.

Hassle Free API

  1. or... Login to Account
  2. Add search credits with Bitcoin, Litecoin or Monero
  3. Programmatically search the Internet by face

Code Examples for Search by Face

Code illustrates how to run a face search

  1. Upload an image as multipart/form-data to /api/upload_pic and get id_search
  2. Run a search and get its progress by posting id_search to /api/search
  3. Iterate over the returned json search results

Testing Mode

Example code has TESTING_MODE=True for testing purposes. In testing mode only 100,000 faces are scanned. Testing mode search is fast and doesn't consume much compute power and the search results are not useful but credits will not be deducted. When you're ready for real search, set TESTING_MODE=False.

For all the descriptions of the API endpoints and data structures see FaceCheck Swagger Specification

import time
import requests
import urllib.request
TESTING_MODE = True
APITOKEN = 'YOUR_API_TOKEN' # Your API Token

# Download the photo of the person you want to find
urllib.request.urlretrieve('https://www.indiewire.com/wp-content/uploads/2018/01/daniel-craig.jpg?w=300', "daniel.jpg")
image_file = 'daniel.jpg' 

def search_by_face(image_file):
    if TESTING_MODE:
        print('****** TESTING MODE search, results are inacurate, and queue wait is long, but credits are NOT deducted ******')

    site='https://facecheck.id'
    headers = {'accept': 'application/json', 'Authorization': APITOKEN}
    files = {'images': open(image_file, 'rb'), 'id_search': None}
    response = requests.post(site+'/api/upload_pic', headers=headers, files=files).json()

    if response['error']:
        return f"{response['error']} ({response['code']})", None

    id_search = response['id_search']
    print(response['message'] + ' id_search='+id_search)
    json_data = {'id_search': id_search, 'with_progress': True, 'status_only': False, 'demo': TESTING_MODE}

    while True:
        response = requests.post(site+'/api/search', headers=headers, json=json_data).json()
        if response['error']:
            return f"{response['error']} ({response['code']})", None
        if response['output']:
            return None, response['output']['items']
        print(f'{response["message"]} progress: {response["progress"]}%')
        time.sleep(1)


# Search the Internet by face
error, urls_images = search_by_face(image_file)

if urls_images:
    for im in urls_images:      # Iterate search results
        score = im['score']     # 0 to 100 score how well the face is matching found image
        url = im['url']         # url to webpage where the person was found
        image_base64 = im['base64']     # thumbnail image encoded as base64 string
        print(f"{score} {url} {image_base64[:32]}...")
else:
    print(error)
using System.Net;

namespace FaceCheckAPI
{
    public class SearchItem
    {
        public string? guid { get; set; }
        public int score { get; set; }
        public string? base64 { get; set; }
        public string? url { get;  set; }
        public int index { get; set; } = -1;
    }

    public class Search_Results
    {
        public List<SearchItem>? items { get; set; }
    }

    public class InputImage
    {
        public string? base64 { get; set; }
        public string? id_pic { get; set; }
    }

    public class BrowserJsonResponse
    {
        public string? id_search { get; set; }
        public string? message { get; set; }
        public int? progress { get; set; }
        public string? error { get; set; }
        public string? code { get; set; }
        public Search_Results? output { get; set; }
        public List<InputImage>? input { get; set; }
    }

    public class FaceCheckAPI
    {
        static bool TESTING_MODE = true;
        static string APITOKEN = "YOUR_API_TOKEN"; // Your API Token
        static string site = "https://facecheck.id";

        static readonly HttpClient client = new HttpClient();
        static FaceCheckAPI()
        {
            client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", APITOKEN);
        }
        public static async Task<List<SearchItem>?> Search_by_FaceAsync(string image_file)
        {
            if (TESTING_MODE) Console.WriteLine("****** TESTING MODE search, results are inaccurate, and queue wait is long, but credits are NOT deducted ******");
            using var content = new MultipartFormDataContent("Upload----" + DateTime.Now.Ticks.ToString("x"));
            using var ImageStream = new FileStream(image_file, FileMode.Open, FileAccess.Read, FileShare.None);
            content.Add(new StreamContent(ImageStream), "images", Path.GetFileName(image_file));
            using var message = await client.PostAsync(site + "/api/upload_pic", content);
            var input = await message.Content.ReadAsStringAsync();
            BrowserJsonResponse? a = System.Text.Json.JsonSerializer.Deserialize<BrowserJsonResponse?>(input);
            if (a.error != null) throw new Exception(a.error);
            Console.WriteLine(a.message + " id_search=" + a.id_search);

            while (true)
            {
                BrowserJsonResponse? b = await PostSearch(Convert.ToString(a.id_search));
                if (b.error != null) throw new Exception(b.error);
                if (b.output != null) return b.output.items;
                Console.WriteLine($"{b.message} Progress: {b.progress}%");
                await Task.Delay(1000);
            }

        }
            
        

        private static async Task<BrowserJsonResponse?> PostSearch(string? id_search)
        {
            using var _Body = new StringContent($"{{\"id_search\":\"{id_search}\",\"with_progress\":true,\"status_only\":false,\"demo\":{(TESTING_MODE ? "true" : "false")}}}");
            _Body.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            using var msg = await client.PostAsync(site + "/api/search", _Body);
            string json = await msg.Content.ReadAsStringAsync();
            return Utf8Json.JsonSerializer.Deserialize<BrowserJsonResponse>(json);
        }

    }

    internal class Program
    {
        static async Task Main(string[] args)
        {
            try
            {
                //download a test image 
                using (WebClient webClient = new WebClient())
                    webClient.DownloadFile("https://www.indiewire.com/wp-content/uploads/2018/01/daniel-craig.jpg?w=300", "daniel.jpg");
                

                //Search the Internet by face
                var images = await FaceCheckAPI.Search_by_FaceAsync("daniel.jpg");
                if (images != null)
                    foreach (SearchItem im in images) //Iterate search results
                    {
                        var score = im.score;     // 0 to 100 score how well the face is matching found image
                        var url = im.url;         // url to webpage where the person was found
                        var image_base64 = im.base64;     // thumbnail image encoded as base64 string
                        //convert to binary .WEBP image
                        byte[] webp_image = Convert.FromBase64String(image_base64.Substring(image_base64.IndexOf(' ') + 1));
                        Console.WriteLine($"{score} {url} {image_base64?.Substring(0, 32)}...");
                    }
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.Message);
            }
            Console.ReadKey();
        }
    }
}
const axios = require('axios');
const FormData = require('form-data');
const fs = require('fs');
const https = require('https');


const TESTING_MODE = true;
const APITOKEN = 'YOUR_API_TOKEN';


const downloadImage = (url, filename) => {
  return new Promise((resolve, reject) => {
    const file = fs.createWriteStream(filename);
    https.get(url, response => {
      response.pipe(file);
      file.on('finish', () => {
        file.close(resolve);
      });
      file.on('error', reject);
    });
  });
};


const search_by_face = async (image_file) => {
    if (TESTING_MODE) {
        console.log('****** TESTING MODE search, results are inacurate, and queue wait is long, but credits are NOT deducted ******');
    }


    const site = 'https://facecheck.id';
    const headers = {
        accept: 'application/json',
        Authorization: APITOKEN,
    };


    let form = new FormData();
    form.append('images', fs.createReadStream(image_file));
    form.append('id_search', '');


    let response = await axios.post(site+'/api/upload_pic', form, { headers: {
        ...form.getHeaders(),
        'accept': 'application/json',
        'Authorization': APITOKEN
    } });
    response = response.data;


    if (response.error) {
        return [`${response.error} (${response.code})`, null];
    }


    const id_search = response.id_search;
    console.log(`${response.message} id_search=${id_search}`);
    const json_data = {
        id_search: id_search,
        with_progress: true,
        status_only: false,
        demo: TESTING_MODE,
    };


    while (true) {
        response = await axios.post(site+'/api/search', json_data, { headers: headers });
        response = response.data;
        if (response.error) {
            return [`${response.error} (${response.code})`, null];
        }
        if (response.output) {
            return [null, response.output.items];
        }
        console.log(`${response.message} progress: ${response.progress}%`);
        await new Promise(r => setTimeout(r, 1000));
    }
};


const run = async () => {
    await downloadImage('https://www.indiewire.com/wp-content/uploads/2018/01/daniel-craig.jpg?w=300', 'daniel.jpg');

    // Search the Internet by face
    const [error, urls_images] = await search_by_face('daniel.jpg');


  if (urls_images) {
    urls_images.forEach(im => {
        const score = im.score;     // 0 to 100 score how well the face is matching found image
        const url = im.url;         // url to webpage where the person was found
        const image_base64 = im.base64;     // thumbnail image encoded as base64 string
        console.log(`${score} ${url} ${image_base64.slice(0, 32)}...`);
    });
  } else {
    console.log(error);
  }
}


run().catch(console.error);



import org.json.JSONArray;
import org.json.JSONObject;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class API_TEST {
    private static final boolean TESTING_MODE = true;
    private static final String APITOKEN = "YOUR_API_TOKEN";
    private static final String IMAGE_URL = "https://www.indiewire.com/wp-content/uploads/2018/01/daniel-craig.jpg?w=300";

    public static void main(String[] args) {
        try {
            String imageFilePath = downloadImage();
            JSONArray urlsImages;

            if (TESTING_MODE) {
                System.out.println("****** TESTING MODE search, results are bad, and queue wait is long, but credits are NOT deducted ******");
            }

            String site = "https://facecheck.id";
            String authToken = APITOKEN;

            // Upload the image
            JSONObject uploadResponse = uploadImage(site, authToken, imageFilePath);
            // System.out.println(uploadResponse);
            if (uploadResponse.has("error") && !uploadResponse.isNull("error")) {
                System.out.println("Error Code: " + uploadResponse.getString("code") + " , " + uploadResponse.getString("error"));
                return;
            }

            String idSearch = uploadResponse.getString("id_search");
            System.out.println(uploadResponse.getString("message") + " id_search=" + idSearch);

            JSONObject jsonData = new JSONObject()
                    .put("id_search", idSearch)
                    .put("with_progress", true)
                    .put("status_only", false)
                    .put("demo", TESTING_MODE);

            while (true) {
                JSONObject searchResponse = searchImage(site, authToken, jsonData);
                if (searchResponse.has("error") && !searchResponse.isNull("error")) {
                    System.out.println("Error Code: " + searchResponse.getString("code") + " , " + searchResponse.getString("error"));
                    return;
                }

                if (searchResponse.has("output") && !searchResponse.isNull("output")) {
                    urlsImages = searchResponse.getJSONObject("output").getJSONArray("items");
                    break;
                }

                System.out.println(searchResponse.getString("message") + " progress: " + searchResponse.getInt("progress") + "%");
                Thread.sleep(1000);
            }

            if (urlsImages != null) {
                for (int i = 0; i < urlsImages.length(); i++) {
                    JSONObject im = urlsImages.getJSONObject(i);
                    int score = im.getInt("score");
                    String url = im.getString("url");
                    String imageBase64 = im.getString("base64");

                    System.out.println(score + " " + url + " " + imageBase64.substring(0, 32) + "...");
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static String getImageExtensionFromURL(String imageUrl) throws MalformedURLException {
        try {
            URL url = new URL(imageUrl);
            String path = url.getPath();
            String fileName = path.substring(path.lastIndexOf('/') + 1);
            return fileName.substring(fileName.lastIndexOf('.') + 1);
        } catch (Exception exception) {
            System.out.println(exception.getMessage());
            throw exception;
        }
    }

    private static String downloadImage() throws IOException {
        String imageFileName = "SearchImage." + getImageExtensionFromURL(IMAGE_URL) ;
        URL imageUrl = new URL(IMAGE_URL);
        try (InputStream in = imageUrl.openStream()) {
            Files.copy(in, Paths.get(imageFileName), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException exception) {
            System.out.println(exception.getMessage());
            throw exception;
        }
        return imageFileName;
    }

    private static JSONObject uploadImage(String site, String authToken, String imageFilePath) throws IOException {
        String boundary = "*****" + Long.toHexString(System.currentTimeMillis()) + "*****";
        String lineEnd = "\r\n";
        String twoHyphens = "--";

        URL url = new URL(site + "/api/upload_pic");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("accept", "application/json");
        connection.setRequestProperty("Authorization", authToken);
        connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

        try (DataOutputStream dos = new DataOutputStream(connection.getOutputStream())) {
            dos.writeBytes(twoHyphens + boundary + lineEnd);
            dos.writeBytes("Content-Disposition: form-data; name=\"images\";filename=\"" + imageFilePath + "\"" + lineEnd);
            dos.writeBytes(lineEnd);

            try (FileInputStream fis = new FileInputStream(imageFilePath)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    dos.write(buffer, 0, bytesRead);
                }
            }

            dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
            dos.flush();
        }

        try (InputStream is = connection.getInputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                response.append(line);
            }
            return new JSONObject(response.toString());
        }
    }

    private static JSONObject searchImage(String site, String authToken, JSONObject jsonData) throws IOException {
        URL url = new URL(site + "/api/search");
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("accept", "application/json");
        connection.setRequestProperty("Authorization", authToken);
        connection.setRequestProperty("Content-Type", "application/json");

        try (DataOutputStream dos = new DataOutputStream(connection.getOutputStream())) {
            dos.writeBytes(jsonData.toString());
            dos.flush();
        }  catch (Exception exception) {
            System.out.println(exception.getMessage());
            throw exception;
        }

        try (InputStream is = connection.getInputStream();
             BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                response.append(line);
            }
            return new JSONObject(response.toString());
        } catch (NoRouteToHostException exception) {
            System.out.println(exception.getMessage());
            throw exception;
        }
    }


}
//#define CURL_STATICLIB
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <thread>
#include "curl/curl.h"
#include "json/json.h"

using namespace std;

const bool TESTING_MODE = true;
const std::string APITOKEN = "YOUR_API_TOKEN";
const std::string IMAGE_URL = "https://www.indiewire.com/wp-content/uploads/2018/01/daniel-craig.jpg?w=300";

std::string getImageExtensionFromURL(const std::string& url) {
    // Find the last occurrence of '.' in the URL
    size_t dotPos = url.find_last_of('.');

    if (dotPos == std::string::npos) {
        // No extension found
        return "";
    }

    // Find the first occurrence of '?' after the last dot position
    size_t queryParamPos = url.find_first_of('?', dotPos);

    // Extract the extension from the URL
    std::string extension;
    if (queryParamPos != std::string::npos) {
        extension = url.substr(dotPos + 1, queryParamPos - dotPos - 1);
    } else {
        extension = url.substr(dotPos + 1);
    }

    return "jpg";
}

std::string downloadImage() {
    std::string imageFileName = "SearchImage." + getImageExtensionFromURL(IMAGE_URL);
    cout << "MESSAGE: Downloading Image..." << endl;
    CURL* curl = curl_easy_init();
    if (!curl) {
        std::cout << "Error: Failed to initialize CURL." << std::endl;
        exit(EXIT_FAILURE);
    }

    
    FILE* imageFile;
#ifdef _WIN32
    fopen_s(&imageFile, imageFileName.c_str(), "wb");
#else
    imageFile = fopen(imageFileName.c_str(), "wb");
#endif
    if (!imageFile) {
        std::cout << "Error: Failed to create image file." << std::endl;
        curl_easy_cleanup(curl);
        exit(EXIT_FAILURE);
    }

    curl_easy_setopt(curl, CURLOPT_URL, IMAGE_URL.c_str());
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, imageFile);
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);

    CURLcode res = curl_easy_perform(curl);
    curl_easy_cleanup(curl);
    fclose(imageFile);

    if (res != CURLE_OK) {
        std::cout << "Error: Failed to download image." << std::endl;
        exit(EXIT_FAILURE);
    }

    return imageFileName;
}

size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
    size_t totalSize = size * nmemb;
    output->append(static_cast<char*>(contents), totalSize);
    return totalSize;
}

Json::Value uploadImage(const std::string& site, const std::string& authToken, const std::string& imageFilePath) {
    std::string boundary = "*****" + std::to_string(std::time(nullptr)) + "*****";

    std::string url = site + "/api/upload_pic";
    CURL* curl = curl_easy_init();
    if (!curl) {
        std::cout << "Error: Failed to initialize CURL." << std::endl;
        exit(EXIT_FAILURE);
    }

    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, ("Authorization: " + authToken).c_str());
    headers = curl_slist_append(headers, "accept: application/json");

    std::string responseString; // To store the response data
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);

    // Create the form data
    curl_mime* form = curl_mime_init(curl);
    curl_mimepart* part = curl_mime_addpart(form);
    curl_mime_name(part, "images");
    curl_mime_filedata(part, imageFilePath.c_str());

    // Attach the form data to the request
    curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);

    // Set the write callback function
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    // Set the pointer to responseString as the user data to pass to the callback function
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseString);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
    CURLcode res = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_mime_free(form);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK) {
        std::cout << "Error: Failed to upload image." << std::endl;
        exit(EXIT_FAILURE);
    }

    Json::Value response;
    Json::CharReaderBuilder reader;
    std::string errors;
    std::istringstream iss(responseString);
    if (!Json::parseFromStream(reader, iss, &response, &errors)) {
        std::cout << "Error: Failed to parse JSON response." << std::endl;
        exit(EXIT_FAILURE);
    }
    return response;
}

Json::Value searchImage(const std::string& site, const std::string& authToken, const Json::Value& jsonData) {
    std::string url = site + "/api/search";
    CURL* curl = curl_easy_init();
    if (!curl) {
        std::cout << "Error: Failed to initialize CURL." << std::endl;
        exit(EXIT_FAILURE);
    }

    struct curl_slist* headers = nullptr;
    headers = curl_slist_append(headers, ("Authorization: " + authToken).c_str());
    headers = curl_slist_append(headers, "accept: application/json");
    headers = curl_slist_append(headers, "Content-Type: application/json");
    string responseString;
    std::string jsonDataStr = jsonData.toStyledString();
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonDataStr.c_str());
    curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
    //curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    // Set the pointer to responseString as the user data to pass to the callback function
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseString);

    // Set the option to suppress the output
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
    CURLcode res = curl_easy_perform(curl);
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK) {
        std::cout << "Error: Failed to perform search." << std::endl;
        exit(EXIT_FAILURE);
    }

    Json::Value response;
    Json::CharReaderBuilder reader;
    std::string errors;
    std::istringstream iss(responseString);
    if (!Json::parseFromStream(reader, iss, &response, &errors)) {
        std::cout << "Error: Failed to parse JSON response." << std::endl;
        exit(EXIT_FAILURE);
    }
    return response;
}

int main() {
    try {
        std::string imageFilePath = downloadImage();
        Json::Value urlsImages;

        if (TESTING_MODE) {
            std::cout << "****** TESTING MODE search, results are bad, but credits are NOT deducted ******" << std::endl;
        }

        std::string site = "https://facecheck.id";
        std::string authToken = APITOKEN;

        // Upload the image
        Json::Value uploadResponse = uploadImage(site, authToken, imageFilePath);
        if(uploadResponse.isMember("error")) {
            if (uploadResponse.isMember("error") && !uploadResponse["error"].isNull()) {
                std::cout << "Error Code: " << uploadResponse["code"].asString() << " , " << uploadResponse["error"].asString() << std::endl;
                return 1;
            }
        }

        std::string idSearch = uploadResponse["id_search"].asString();
        std::cout << uploadResponse["message"].asString() << " id_search=" << idSearch << std::endl;

        Json::Value jsonData;
        jsonData["id_search"] = idSearch;
        jsonData["with_progress"] = true;
        jsonData["status_only"] = false;
        jsonData["demo"] = TESTING_MODE;

        while (true) {
            Json::Value searchResponse = searchImage(site, authToken, jsonData);
            if (searchResponse.isMember("error") && !searchResponse["error"].isNull()) {
                std::cout << "Error Code: " << searchResponse["code"].asString() << " , " << searchResponse["error"].asString() << std::endl;
                return 1;
            }

            if (searchResponse.isMember("output") && !searchResponse["output"].isNull()) {
                urlsImages = searchResponse["output"]["items"];
                break;
            }

            std::cout<< searchResponse["message"].asString() << " progress: " << searchResponse["progress"] << "%" << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }

        cout << "MESSAGE: Image Search Completed!" << endl;
        if (!urlsImages.isNull()) {
            for (unsigned int i = 0; i < urlsImages.size(); ++i) {
                Json::Value im = urlsImages[i];
                int score = im["score"].asInt();
                std::string url = im["url"].asString();
                std::string imageBase64 = im["base64"].asString();

                std::cout << score << " " << url << " " << imageBase64.substr(0, 32) << "..." << std::endl;
            }
        }
    }
    catch (const std::exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
import org.json.JSONArray
import org.json.JSONObject
import java.io.*
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths
import java.nio.file.StandardCopyOption

object FaceCheck {
    private const val TESTING_MODE = true
    private const val APITOKEN = "YOUR_API_TOKEN" // Your API Token
    private const val IMAGE_URL = "https://www.indiewire.com/wp-content/uploads/2018/01/daniel-craig.jpg?w=300"

    @JvmStatic
    fun main(args: Array<String>) {
        try {
            val imageFilePath = downloadImage()
            val urlsImages: JSONArray?

            if (TESTING_MODE) {
                println("****** TESTING MODE search, results are bad, but credits are NOT deducted ******")
            }

            val site = "https://facecheck.id"
            val authToken = APITOKEN

            // Upload the image
            val uploadResponse = uploadImage(site, authToken, imageFilePath)
            if (uploadResponse.has("error") && !uploadResponse.isNull("error")) {
                println(
                    "Error Code: " + uploadResponse.getString("code") + " , " + uploadResponse.getString("error")
                )
                return
            }

            val idSearch = uploadResponse.getString("id_search")
            println(uploadResponse.getString("message") + " id_search=$idSearch")

            val jsonData = JSONObject()
                .put("id_search", idSearch)
                .put("with_progress", true)
                .put("status_only", false)
                .put("demo", TESTING_MODE)

            while (true) {
                val searchResponse = searchImage(site, authToken, jsonData)
                if (searchResponse.has("error") && !searchResponse.isNull("error")) {
                    println(
                        "Error Code: " + searchResponse.getString("code") + " , " + searchResponse.getString("error")
                    )
                    return
                }

                if (searchResponse.has("output") && !searchResponse.isNull("output")) {
                    urlsImages = searchResponse.getJSONObject("output").getJSONArray("items")
                    break
                }

                println(
                    searchResponse.getString("message") + " progress: " + searchResponse.getInt("progress") + "%"
                )
                Thread.sleep(1000)
            }

            if (urlsImages != null) {
                for (i in 0 until urlsImages.length()) {
                    val im = urlsImages.getJSONObject(i)
                    val score = im.getInt("score")
                    val url = im.getString("url")
                    val imageBase64 = im.getString("base64")

                    println("$score $url ${imageBase64.substring(0, 32)}...")
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }

    @Throws(IOException::class)
    private fun downloadImage(): String {
        val imageFileName = "SearchImage.${getImageExtensionFromURL(IMAGE_URL)}"
        val imageUrl = URL(IMAGE_URL)
        imageUrl.openStream().use { `in` ->
            Files.copy(`in`, Paths.get(imageFileName), StandardCopyOption.REPLACE_EXISTING)
        }
        return imageFileName
    }

    @Throws(IOException::class)
    private fun uploadImage(site: String, authToken: String, imageFilePath: String): JSONObject {
        val boundary = "*****" + java.lang.Long.toHexString(System.currentTimeMillis()) + "*****"
        val lineEnd = "\r\n"
        val twoHyphens = "--"

        val url = URL("$site/api/upload_pic")
        val connection = url.openConnection() as HttpURLConnection
        connection.doOutput = true
        connection.requestMethod = "POST"
        connection.setRequestProperty("accept", "application/json")
        connection.setRequestProperty("Authorization", authToken)
        connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=$boundary")

        DataOutputStream(connection.outputStream).use { dos ->
            dos.writeBytes(twoHyphens + boundary + lineEnd)
            dos.writeBytes("Content-Disposition: form-data; name=\"images\";filename=\"$imageFilePath\"$lineEnd")
            dos.writeBytes(lineEnd)

            FileInputStream(imageFilePath).use { fis ->
                val buffer = ByteArray(1024)
                var bytesRead: Int
                while (fis.read(buffer).also { bytesRead = it } != -1) {
                    dos.write(buffer, 0, bytesRead)
                }
            }

            dos.writeBytes(lineEnd)
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd)
            dos.flush()
        }

        val response = StringBuilder()
        connection.inputStream.bufferedReader().use { br ->
            var line: String?
            while (br.readLine().also { line = it } != null) {
                response.append(line)
            }
        }
        return JSONObject(response.toString())
    }

    @Throws(IOException::class)
    private fun searchImage(site: String, authToken: String, jsonData: JSONObject): JSONObject {
        val url = URL("$site/api/search")
        val connection = url.openConnection() as HttpURLConnection
        connection.doOutput = true
        connection.requestMethod = "POST"
        connection.setRequestProperty("accept", "application/json")
        connection.setRequestProperty("Authorization", authToken)
        connection.setRequestProperty("Content-Type", "application/json")

        DataOutputStream(connection.outputStream).use { dos ->
            dos.writeBytes(jsonData.toString())
            dos.flush()
        }

        val response = StringBuilder()
        connection.inputStream.bufferedReader().use { br ->
            var line: String?
            while (br.readLine().also { line = it } != null) {
                response.append(line)
            }
        }

        return JSONObject(response.toString())
    }

    @Throws(MalformedURLException::class)
    fun getImageExtensionFromURL(imageUrl: String): String {
        try {
            val url = URL(imageUrl)
            val path = url.path
            val fileName = path.substring(path.lastIndexOf('/') + 1)
            return fileName.substring(fileName.lastIndexOf('.') + 1)
        } catch (exception: Exception) {
            // If any error occurs, return null or an appropriate default value
            println(exception.message)
            throw exception
        }
    }
}