emma.Emma.java Source code

Java tutorial

Introduction

Here is the source code for emma.Emma.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package emma;

import com.omertron.themoviedbapi.enumeration.ExternalSource;
import com.omertron.themoviedbapi.methods.TmdbFind;
import com.omertron.themoviedbapi.model.FindResults;
import com.omertron.themoviedbapi.tools.HttpTools;
import com.sun.javafx.iio.ImageFrame;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import edu.cmu.sphinx.api.LiveSpeechRecognizer;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Executors;
import javax.imageio.ImageIO;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yamj.api.common.http.SimpleHttpClientBuilder;

/**
 *
 * @author tdn
 */

public class Emma {

    static boolean debug = true;

    public static void debug(String s) {
        if (debug) {
            System.out.println(s);
        }
    }

    public static void error(String s) {
        if (debug) {
            System.err.println(s);
        }
    }

    /**
     * 
     *      HTTP
     * 
     */
    static class Http {
        public Http() {

        }

        static public int get(String url, StringBuilder response, String auth) {
            System.out.println("get->" + url);
            if (auth == null) {

            }
            try {
                HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection();
                con.setRequestMethod("GET");
                con.setRequestProperty("User-Agent", "Mozilla/5.0");
                con.setRequestProperty("Authorization", "Basic " + auth);
                con.setInstanceFollowRedirects(false);
                BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                String inputLine;
                int code = con.getResponseCode();

                // directly found and redirected by "Location" header
                if (code == 302) {
                    // redirected
                    String location = con.getHeaderField("Location");
                    if (location.length() > 0) {
                        String s = "?fr=";
                        int idx = location.indexOf(s);
                        if (idx > -1) {
                            location = location.substring(0, idx);
                        }

                        get(location, response, null);
                        return code;
                    }
                }

                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();

                return code;
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            return 404;
        }
    }

    public static String getPath(String p) {
        String OS = System.getProperty("os.name").toLowerCase();
        if ((OS.contains("win"))) {
            //p = p.replace("\\", "\\\\");
        }
        return p;
    }

    /**
     * 
     * 
     *      IMDB
     * 
     */
    static class MetaInfo {
        public MetaInfo() {

        }

        public static String getURL(String hash) {
            return Memory.select("url", "entries", "hash = '" + hash + "'").get(0);
        }

        public static String getAlt(String hash) {
            return Memory.select("alt", "entries", "hash = '" + hash + "'").get(0);
        }

        private static void readImg(String c, String hash) { // c = alt
            int idx;
            String s = "IMDb</title>";
            String s_html = c;
            int code = 0;
            StringBuilder r = new StringBuilder(2048);

            // return if exists
            File outputfile = new File("www/images/work/" + hash + ".jpg");
            if (!outputfile.exists()) {
                // IMDB
                // <img alt="TRON: Legacy (2010) Poster" title="TRON: Legacy (2010) Poster" src="http://ia.media-imdb.com/images/M/MV5BMTk4NTk4MTk1OF5BMl5BanBnXkFtZTcwNTE2MDIwNA@@._V1_SX214_AL_.jpg" itemprop="image" height="317" width="214">
                idx = s_html.indexOf(s);
                if (idx > -1) {
                    s = "itemprop=\"image";
                    idx = s_html.indexOf(s);
                    if (idx > -1) {
                        s_html = s_html.substring(idx - 250, idx + 250);
                        s = "src=\"";
                        idx = s_html.indexOf(s);
                        if (idx > -1) {
                            s_html = s_html.substring(idx + s.length());
                            s = "\"";
                            idx = s_html.indexOf(s);
                            if (idx > -1) {
                                s_html = s_html.substring(0, idx);
                                code = Http.get(s_html, r, null);
                                if (code == 404) {
                                    System.err.println(hash + ".jpg");
                                }
                                try {
                                    BufferedImage img = ImageIO.read(new URL(s_html));
                                    ImageIO.write(img, "jpg", outputfile);
                                } catch (Exception e) {
                                    System.err.println(e.toString());
                                }
                            }
                        }
                    }
                } else {
                    // TheMovieDB
                    s = "id=\"upload_poster\"";
                    idx = s_html.indexOf(s);
                    if (idx > -1) {
                        s_html = s_html.substring(idx + s.length());
                        s = "src=\"";
                        idx = s_html.indexOf(s);
                        if (idx > -1) {
                            s_html = s_html.substring(idx + s.length());
                            s = "\"";
                            idx = s_html.indexOf(s);
                            if (idx > -1) {
                                s_html = s_html.substring(0, idx);
                                code = Http.get(s_html, r, null);
                                if (code == 404) {
                                    System.err.println(hash + ".jpg");
                                }
                                try {
                                    BufferedImage img = ImageIO.read(new URL(c));
                                    ImageIO.write(img, "jpg", outputfile);
                                } catch (Exception e) {
                                    System.err.println(e.toString());
                                }
                            }
                        }
                    }
                }
            }

            // big one
            s_html = c;
            outputfile = new File("www/images/work/_" + hash + ".jpg");
            if (!outputfile.exists()) {
                // IMDB
                s = "<a href=\"/media/";
                idx = s_html.indexOf(s);
                if (idx > -1) {
                    s_html = s_html.substring(idx);
                    s = "\"";
                    idx = s_html.indexOf(s);
                    if (idx > -1) {
                        s_html = s_html.substring(idx + s.length());
                        s = "\"";
                        idx = s_html.indexOf(s);
                        if (idx > -1) {
                            s_html = s_html.substring(0, idx);
                            Http.get("http://www.imdb.com" + s_html, r, null);
                            s_html = r.toString();
                            s = "<div class=\"photo\">";
                            idx = s_html.indexOf(s);
                            if (idx > -1) {
                                s_html = s_html.substring(idx + s.length());
                                s = "src=\"";
                                idx = s_html.indexOf(s);
                                if (idx > -1) {
                                    s_html = s_html.substring(idx + s.length());
                                    s = "\"";
                                    idx = s_html.indexOf(s);
                                    if (idx > -1) {
                                        s_html = s_html.substring(0, idx);
                                        try {
                                            BufferedImage img = ImageIO.read(new URL(s_html));
                                            ImageIO.write(img, "jpg", outputfile);
                                        } catch (Exception e) {
                                            System.err.println(e.toString());
                                        }
                                    }
                                }
                            }
                        }
                    }
                } else {
                    // TheMovieDB

                }
            }
        }

        private static void downloadMetaData(String name, String hash) {
            try {
                if (name.length() == 0)
                    return;

                // IMDB
                //String url = "http://www.imdb.com/xml/find?json=1&q=" + URLEncoder.encode(name, "ISO-8859-1");
                String url = "http://www.imdb.com/xml/find?json=1&q="
                        + name.replace(" ", "+").replace("&", "%26").replace("", "%DF");

                boolean found = false, exit = false;

                // work files
                File html = new File(getPath("work/" + hash + ".html"));

                // if name is url, imdb url already known
                if (name.startsWith("http:")) {
                    /*
                                        // only write correct imdb title page
                                        FileUtils.writeStringToFile(html, r.toString());
                        
                                        // read img from title page
                                        readImg(r.toString(), hash);                  
                    */
                    StringBuilder r = new StringBuilder(8192);
                    int code = Http.get(name, r, null);
                    if (html.exists()) {
                        html.delete();
                    }
                    File image = new File("www/images/work/" + hash + ".jpg");
                    if (image.exists()) {
                        image.delete();
                    }
                    image = new File("www/images/work/_" + hash + ".jpg");
                    if (image.exists()) {
                        image.delete();
                    }
                    FileUtils.writeStringToFile(html, r.toString());
                    readImg(r.toString(), hash);

                    return;
                }

                // exit after title found
                if (html.exists()) {
                    return;
                }

                String s = "";
                int tr_idx = -1, idx = -1, tmp_idx = -1;

                // http request
                StringBuilder r = new StringBuilder(8192);
                int code = Http.get(url, r, null);

                // build local imdb
                String s_html = r.toString();
                if (code == 200) { // ok will response json                    

                    // --> try to get title

                    // try search and parse first output line
                    r = new StringBuilder(8192);
                    //url = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + URLEncoder.encode(name, "ISO-8859-1") + "&s=all";
                    url = "http://www.imdb.com/find?ref_=nv_sr_fn&q=" + name.replace(" ", "+").replace("&", "%26")
                            + "&s=all";
                    code = Http.get(url, r, null);

                    // ok will response html
                    if (code == 200) {
                        s_html = r.toString();
                        String response = s_html;

                        // reading in loop -> every <tr
                        while (found == false && exit == false) {
                            s = "<tr class=\"findResult";
                            idx = s_html.indexOf(s);
                            tr_idx = idx;
                            if (idx > -1) {
                                s_html = s_html.substring(idx + s.length());
                                s = "</tr>";
                                idx = s_html.indexOf(s);
                                if (idx > -1) {
                                    s_html = s_html.substring(0, idx);
                                    s = "<a href=\"";
                                    idx = s_html.indexOf(s);
                                    if (idx > -1) {
                                        // /tt...
                                        s_html = s_html.substring(idx + s.length(), s_html.length());
                                        tmp_idx = s_html.indexOf("><img") - 2;
                                        if (tmp_idx > -1) {
                                            String title_url = s_html.substring(0, tmp_idx);
                                            s = "<a href=";
                                            idx = s_html.indexOf(s);
                                            if (idx > -1) {
                                                s_html = s_html.substring(idx + s.length(), s_html.length());
                                                s = ">";
                                                idx = s_html.indexOf(s);
                                                if (idx > -1) {
                                                    tmp_idx = s_html.indexOf("</td>");
                                                    if (tmp_idx > -1) {
                                                        String title = s_html.substring(idx + s.length(), tmp_idx)
                                                                .trim().replace("</a>", "");
                                                        int lfd = org.apache.commons.lang3.StringUtils
                                                                .getLevenshteinDistance(name, title);
                                                        double ratio = ((double) lfd)
                                                                / (Math.max(name.length(), title.length()));
                                                        // levenshtein = 0.5 = 50%
                                                        // levenshtein = 0.0 = 100%
                                                        if (ratio <= 0.4) {
                                                            r = new StringBuilder(8192);
                                                            //url = "http://www.imdb.com" + URLEncoder.encode(title_url, "ISO-8859-1") + "/?ref_=fn_al_tt_1";
                                                            url = "http://www.imdb.com"
                                                                    + title_url.replace("&", "%26")
                                                                    + "/?ref_=fn_al_tt_1";
                                                            code = Http.get(url, r, null);
                                                            debug("has: " + hash + ">>" + url + ">>" + name);
                                                            if (code == 200) { // ok will response html
                                                                // write .html
                                                                if (!html.exists()) {
                                                                    // only write correct imdb title page
                                                                    FileUtils.writeStringToFile(html, r.toString());

                                                                    // read img from title page
                                                                    readImg(r.toString(), hash);

                                                                    return;
                                                                }
                                                            } else {
                                                                // WHATS UP HERE???
                                                                System.err.println("404-2>>>" + hash + ">>" + url
                                                                        + ">>" + name);
                                                            }
                                                        } else {
                                                            // try other name
                                                            if (title.startsWith(
                                                                    name.substring(0, name.lastIndexOf("(")))
                                                                    && title.contains(
                                                                            name.substring(name.lastIndexOf("("),
                                                                                    name.lastIndexOf(")") + 1))) {
                                                                r = new StringBuilder(8192);
                                                                //url = "http://www.imdb.com" + URLEncoder.encode(title_url, "ISO-8859-1") + "/?ref_=fn_al_tt_1";
                                                                url = "http://www.imdb.com"
                                                                        + title_url.replace("&", "%26")
                                                                        + "/?ref_=fn_al_tt_1";
                                                                code = Http.get(url, r, null);
                                                                debug("has: " + hash + ">>" + url + ">>" + name);
                                                                if (code == 200) { // ok will response html
                                                                    // write .html
                                                                    if (!html.exists()) {
                                                                        // only write correct imdb title page
                                                                        FileUtils.writeStringToFile(html,
                                                                                r.toString());

                                                                        // read img from title page
                                                                        readImg(r.toString(), hash);

                                                                        return;
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    } else {
                                                    }
                                                } else {
                                                }
                                            } else {
                                            }
                                        } else {
                                        }
                                    } else {
                                    }
                                } else {
                                }
                                response = response.substring(tr_idx + s.length());
                                s_html = response;
                            } else {
                                exit = true;
                            }
                        }

                        if (!found) {
                            // TheMovieDB                
                            exit = false; // for the loop
                            r = new StringBuilder(8192);
                            // https://www.themoviedb.org/search?query=dark+knight
                            s = " (";
                            idx = name.lastIndexOf(s);
                            String tmp_name = name, s_name = name;
                            if (idx > -1) {
                                tmp_name = name.substring(0, idx);
                            }
                            int i = 0;
                            while (!found) {
                                if (i == 0) {
                                    url = "https://www.themoviedb.org/search?query="
                                            + tmp_name.replace("&", "%26").replace(" ", "%20") + "&language=de";
                                } else {
                                    url = "https://www.themoviedb.org/search?query="
                                            + name.replace("&", "%26").replace(" ", "%20") + "&language=de";
                                }
                                code = Http.get(url, r, null);
                                if (code == 200) {
                                    s_html = r.toString();
                                    s = "<ul class=\"search_results movie\">";
                                    idx = s_html.indexOf(s);
                                    if (idx > -1) {
                                        s_html = s_html.substring(idx + s.length());
                                        while (!found && !exit) {
                                            s = "<li>";
                                            idx = s_html.indexOf(s);
                                            tr_idx = idx;
                                            if (idx > -1) {
                                                s_html = s_html.substring(idx + s.length());
                                                s = "<div class=\"info\">";
                                                idx = s_html.indexOf(s);
                                                if (idx > -1) {
                                                    s_html = s_html.substring(idx + s.length());
                                                    s = "<a";
                                                    idx = s_html.indexOf(s);
                                                    if (idx > -1) {
                                                        s_html = s_html.substring(idx + s.length());
                                                        // href="/movie/218-the-terminator"
                                                        String title_url = s_html.substring(idx + s.length());
                                                        s = "href=\"";
                                                        idx = title_url.indexOf(s);
                                                        if (idx > -1) {
                                                            title_url = title_url.substring(idx + s.length());
                                                            s = "\"";
                                                            idx = title_url.indexOf(s);
                                                            if (idx > -1) {
                                                                title_url = title_url.substring(0, idx);
                                                            }
                                                        }
                                                        s = ">";
                                                        idx = s_html.indexOf(s);
                                                        if (idx > -1) {
                                                            s_html = s_html.substring(idx + s.length());
                                                            s = "</span>";
                                                            idx = s_html.indexOf(s);
                                                            if (idx > -1) {
                                                                String title = s_html.substring(0, idx);
                                                                title = title.replace("</a>", "").replace("<span>",
                                                                        "");
                                                                int lfd = org.apache.commons.lang3.StringUtils
                                                                        .getLevenshteinDistance(name, title);
                                                                double ratio = ((double) lfd)
                                                                        / (Math.max(name.length(), title.length()));
                                                                // levenshtein = 0.5 = 50%
                                                                // levenshtein = 0.0 = 100%
                                                                if (ratio <= 0.4) {
                                                                    // https://www.themoviedb.org/movie/155-the-dark-knight
                                                                    url = "https://www.themoviedb.org" + title_url
                                                                            + "?language=de";
                                                                    r = new StringBuilder(8192);
                                                                    code = Http.get(url, r, null);
                                                                    if (code == 200) {
                                                                        if (!html.exists()) {
                                                                            System.err.println(">>>" + hash);
                                                                            FileUtils.writeStringToFile(html,
                                                                                    r.toString());

                                                                            // read img from title page
                                                                            readImg(r.toString(), hash);

                                                                            return;
                                                                        }
                                                                    }
                                                                } else {
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            } else {
                                                exit = true;
                                            }
                                        }
                                        exit = false;
                                    } else {
                                        s = "<h3>";
                                        idx = s_html.indexOf(s);
                                        if (idx > -1) {
                                            s_html = s_html.substring(idx + s.length());
                                            s = "href=\"";
                                            idx = s_html.indexOf(s);
                                            if (idx > -1) {
                                                String title_url = s_html = s_html.substring(idx + s.length());
                                                s = "\"";
                                                idx = title_url.indexOf(s);
                                                if (idx > -1) {
                                                    title_url = title_url.substring(0, idx);
                                                    s = ">";
                                                    idx = s_html.indexOf(s);
                                                    if (idx > -1) {
                                                        s_html = s_html.substring(idx + s.length());
                                                        s = "<";
                                                        idx = s_html.indexOf(s);
                                                        if (idx > -1) {
                                                            s_html = s_html.substring(0, idx);
                                                            int lfd = org.apache.commons.lang3.StringUtils
                                                                    .getLevenshteinDistance(name, s_html);
                                                            double ratio = ((double) lfd)
                                                                    / (Math.max(name.length(), s_html.length()));
                                                            // levenshtein = 0.5 = 50%
                                                            // levenshtein = 0.0 = 100%
                                                            if (ratio <= 0.4) {
                                                                // https://www.themoviedb.org/movie/155-the-dark-knight
                                                                url = "https://www.themoviedb.org" + title_url
                                                                        + "?language=de";
                                                                r = new StringBuilder(8192);
                                                                code = Http.get(url, r, null);
                                                                if (code == 200) {
                                                                    if (!html.exists()) {
                                                                        FileUtils.writeStringToFile(html,
                                                                                r.toString());

                                                                        // read img from title page
                                                                        readImg(r.toString(), hash);

                                                                        return;
                                                                    }
                                                                }
                                                            } else {
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                                if (i < 3) { // i < size of fiter (tokenizers)
                                    if (i == 0) {
                                        name = s_name.replaceAll("(?i)der ", "").replaceAll("(?i)die ", "")
                                                .replaceAll("(?i)das ", "").replaceAll("(?i)the ", "");
                                        s = " (";
                                        idx = name.lastIndexOf(s);
                                        if (idx > -1) {
                                            name = name.substring(0, idx);
                                        }
                                    }
                                    if (i == 1) {
                                        name = s_name;
                                    }
                                    if (i == 2) {
                                        name = s_name;
                                        s = " (";
                                        idx = name.lastIndexOf(s);
                                        if (idx > -1) {
                                            name = name.substring(0, idx);
                                        }
                                        name = name.replace(" I", "");
                                        name = name.replace(" II", "");
                                        name = name.replace(" III", "");
                                        name = name.replace(" IV", "");
                                        name = name.replace(" V", "");
                                        name = name.replace(" VI", "");
                                        name = name.replace(" VII", "");
                                        name = name.replace(" VIII", "");
                                        name = name.replace(" IX", "");
                                        name = name.replace(" X", "");
                                    }
                                    i++;
                                } else {
                                    found = true; // fake-found -- nothing here :(
                                    i = 0;
                                }
                            }
                        }
                    } else {
                        System.err.println(">" + code + ">>>" + hash + ">>" + url + ">>" + name);
                    }
                }

                // redirected will response title-page
                if (code == 302) {
                    // write .html
                    if (!html.exists()) {
                        // read img from title page
                        readImg(r.toString(), hash);
                        FileUtils.writeStringToFile(html, r.toString());

                        return;
                    }
                }

                // create fake html
                if (!html.exists()) {
                    //
                    System.err.println(html.getCanonicalPath());
                    FileUtils.writeStringToFile(html, hash);
                }

            } catch (Exception e) {
                System.err.println(e.toString());
            }
        }

        // reads content from file[hash]
        // and
        // updates alt, title-url, cast
        private static void readMetaData(String hash) {
            String content = "", alt, title_url, img = "", cast = "", actors = "", desc = "";
            try {
                File html = new File(getPath("work/" + hash + ".html"));
                if (html.exists()) {
                    BufferedReader reader = new BufferedReader(new FileReader(html));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        content += line;
                    }
                    //FileUtils.readFileToString(html, content);

                    // imdb
                    int idx;
                    String s;

                    s = "IMDb</title>";
                    idx = content.indexOf(s);
                    if (idx > -1) {
                        s = "<span class=\"title-extra\" itemprop=\"name\">";
                        idx = content.indexOf(s);
                        if (idx > -1) {
                            idx = idx + s.length();
                            alt = content.substring(idx, idx + 1000);
                            alt = alt.substring(0, alt.indexOf("<i>")).trim();
                            alt = alt.substring(1, alt.length() - 1);
                        } else {
                            s = "<span class=\"itemprop\" itemprop=\"name\">";
                            idx = content.indexOf(s);
                            if (idx > -1) {
                                idx = idx + s.length();
                                alt = content.substring(idx, idx + 1000);
                                alt = alt.substring(0, alt.indexOf("</span>"));
                            } else {
                                alt = "";
                            }
                        }
                        // insert alt name
                        alt = alt.replace("'", ""); // no ' needed...
                        Memory.update("entries", "alt", alt, "hash = '" + hash + "'");

                        // read title-url
                        // <link rel="canonical" href="http://www.imdb.com/title/tt0331811/" />
                        s = "<link rel=\"canonical\" href=\"";
                        idx = content.indexOf(s);
                        if (idx > -1) {
                            title_url = content.substring(idx + s.length());
                            s = "/\" />";
                            idx = title_url.indexOf(s);
                            if (idx > -1) {
                                title_url = title_url.substring(0, idx);
                                // insert alt name
                                title_url = title_url.replace("'", ""); // no ' needed...
                                Memory.update("entries", "url", title_url, "hash = '" + hash + "'");
                            }
                        }

                        // read cast
                        s = "<table class=\"cast_list\">";
                        idx = content.indexOf(s);
                        if (idx > -1) {
                            cast = content.substring(idx + s.length());
                            s = "</table>";
                            idx = cast.indexOf(s);
                            if (idx > -1) {
                                cast = cast.substring(0, idx);
                                s = "title=\"";
                                idx = cast.indexOf(s);
                                while (idx > -1) {
                                    cast = cast.substring(idx + s.length());
                                    actors += cast.substring(0, cast.indexOf("\""));
                                    actors += ", ";
                                    s = "title=\"";
                                    idx = cast.indexOf(s);
                                }
                                if (actors.length() > 2) {
                                    actors = actors.substring(0, actors.length() - 2);
                                    Memory.update("entries", "actors", actors.replace("'", ""),
                                            "hash = '" + hash + "'");
                                }
                            }
                        }

                        // read desc
                        s = "<p itemprop=\"description\">";
                        idx = content.indexOf(s);
                        if (idx > -1) {
                            desc = content.substring(idx + s.length());
                            s = "</p>";
                            idx = desc.indexOf(s);
                            if (idx > -1) {
                                desc = desc.substring(0, idx);
                                if (desc.length() > 2) {
                                    Memory.update("entries", "desc", desc.replace("'", ""),
                                            "hash = '" + hash + "'");
                                    return;
                                }
                            }
                        }
                        /*
                        s = "<div class=\"inline canwrap\" itemprop=\"description\">";
                        idx = content.indexOf(s);
                        if (idx > -1) {
                        desc = content.substring(idx+s.length());
                        s = "<p>";
                        idx = desc.indexOf(s);
                        if (idx > -1) {
                            desc = desc.substring(idx+s.length());
                            s = "<em";
                            idx = desc.indexOf(s);
                            if (idx > -1) {
                                desc = desc.substring(0, idx);
                                if (desc.length() > 2) {
                                    Memory.update("entries", "desc", desc.trim().replace("'", ""), "hash = '" + hash + "'");  
                                    return;
                                }                                    
                            }
                        }
                        }                         
                        */
                    }
                    //   <link rel="canonical" href="https://www.themoviedb.org/movie/14161-2012">
                    s = "  <link rel=\"canonical\" href=\"";
                    idx = content.indexOf(s);
                    if (idx > -1) {
                        title_url = content.substring(idx + s.length());
                        s = "\"";
                        idx = title_url.indexOf(s);
                        if (idx > -1) {
                            title_url = title_url.substring(0, idx);
                            Memory.update("entries", "alt", "null", "hash = '" + hash + "'");
                            Memory.update("entries", "url", title_url, "hash = '" + hash + "'");
                            return;
                        }
                    }
                }

                // default if not returned before
                Memory.update("entries", "alt", "null", "hash = '" + hash + "'");
                Memory.update("entries", "url", "null", "hash = '" + hash + "'");
            } catch (Exception e) {
                System.err.println(e.toString());
            } finally {
                // check if image is available
                File f = new File("www/images/work/" + hash + ".jpg");
                if (!f.exists()) {
                    Memory.update("entries", "alt", "null", "hash = '" + hash + "'");
                    Memory.update("entries", "url", "null", "hash = '" + hash + "'");
                }
            }
        }

        private static void createScreenshots(String hash) {
            File f = new File("ffmpeg.sh");
            if (!f.exists()) {
                return;
            }
            try {
                Runtime rt = Runtime.getRuntime();
                LinkedList<String> tmp_fn = Memory.select("filepath", "entries", "hash = '" + hash + "'");
                if (tmp_fn.size() > 0) {
                    String fn = tmp_fn.get(0);
                    //rt.exec("sh ./ffmpeg.sh " + hash + " \""" + fn + "\""");   
                    Process p = new ProcessBuilder("sh", "./ffmpeg.sh", hash, fn).start();
                    System.out.println("Process (" + Thread.currentThread().getId() + ") started.");
                }

            } catch (Exception e) {
                System.err.println(e.toString());
            }
        }

        public static void getMeta(String name, String hash) {

            // work .json and .html files if not exists
            downloadMetaData(name, hash); // needs a loooong time, for a beautiful collection ;)

            // read meta data from .html file and insert into memory
            readMetaData(hash);

            // create screenshots
            //createScreenshots(hash);            

            // insert into image cache
            try {
                //ImageCache.insert("work/" + hash + ".jpg", Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(new File("www/images/work/" + hash + ".jpg"))));
                ImageCache.insert("work/" + hash + ".jpg",
                        FileUtils.readFileToByteArray(new File("www/images/work/" + hash + ".jpg")));
            } catch (IOException ex) {
                // print error not needed here
            }
        }
    }

    /**
     * 
     *      MEMORY
     * 
     */
    static class Memory {
        public Memory() {

        }

        static public LinkedList<String> select(String key, String table) {
            Measure.start();
            LinkedList<String> r = new LinkedList<>();
            try {
                ResultSet rs = s_m.executeQuery("SELECT " + key + " FROM " + table + " ORDER BY " + key + ";");
                while (rs.next()) {
                    r.add(rs.getString(1));
                }
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("SELECT " + key + " FROM " + table + " ORDER BY " + key + "; (" + Measure.resultMs() + ")");

            return r;
        }

        static public LinkedList<String> select(String key, String table, String condition) {
            Measure.start();
            if (condition.length() > 0) {
                condition = " WHERE " + condition;
            }
            LinkedList<String> r = new LinkedList<>();
            try {
                ResultSet rs = s_m
                        .executeQuery("SELECT " + key + " FROM " + table + condition + " ORDER BY " + key + ";");
                while (rs.next()) {
                    r.add(rs.getString(1));
                }
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("SELECT " + key + " FROM " + table + condition + " ORDER BY " + key + "; (" + Measure.resultMs()
                    + ")");
            return r;
        }

        static public LinkedList<String> selectAsJSON(String keys, String table, String order, String filter) {
            Measure.start();

            if (filter.length() > 0) {
                filter = " WHERE " + filter;
            }
            if (order.length() > 0) {
                order = " ORDER BY " + order;
            }
            LinkedList<String> r = new LinkedList<>();
            try {
                ResultSet rs = s_m.executeQuery("SELECT " + keys + " FROM " + table + filter + order + ";");
                ResultSetMetaData meta = rs.getMetaData();
                int cols = meta.getColumnCount();
                String row = "";
                while (rs.next()) {
                    for (int i = 0; i < cols; i++) {
                        row += meta.getColumnName(i + 1).toLowerCase() + ": '" + rs.getString(i + 1) + "', ";
                    }
                    row = row.substring(0, row.length() - 2);
                    r.add("{" + row + "}");
                    row = "";
                }
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("SELECT " + keys + " FROM " + table + filter + order + "; (" + Measure.resultMs() + ")");
            return r;
        }

        static public void insert(String table, String keys, String values) {
            Measure.start();

            try {
                s_m.executeUpdate("INSERT INTO " + table + " (" + keys + ") VALUES (" + values + ");");
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("INSERT INTO " + table + " (" + keys + ") VALUES (" + values + "); (" + Measure.resultMs() + ")");
        }

        static public void update(String table, String key, String value, String condition) {
            Measure.start();

            try {
                if (condition.length() > 0) {
                    condition = " WHERE " + condition;
                }
                s_m.executeUpdate("UPDATE " + table + " SET " + key + " = '" + value + "'" + condition + ";");
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("UPDATE " + table + " SET " + key + " = '" + value + "'" + condition + "; (" + Measure.resultMs()
                    + ")");
        }
    }

    /**
     * 
     *      SETTINGS
     * 
     */
    static class Settings {
        public Settings() {

        }

        static public int count(String key) {
            Measure.start();

            try {
                ResultSet rs = s_p
                        .executeQuery("SELECT COUNT(settingsindex) FROM settings WHERE key = '" + key + "';");
                while (rs.next()) {
                    debug("SELECT COUNT(settingsindex) FROM settings WHERE key = '" + key + "'; ("
                            + Measure.resultMs() + ")");
                    return rs.getInt(1);
                }
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("SELECT COUNT(settingsindex) FROM settings WHERE key = '" + key + "'; (" + Measure.resultMs()
                    + ")");
            return -1;
        }

        static public LinkedList<String> get(String key) {
            Measure.start();

            try {
                ResultSet rs = s_p.executeQuery("SELECT value FROM settings WHERE key = '" + key + "';");
                LinkedList<String> r = new LinkedList<>();
                while (rs.next()) {
                    r.add(rs.getString(1));
                }

                debug("SELECT value FROM settings WHERE key = '" + key + "'; (" + Measure.resultMs() + ")");
                return r;
            } catch (Exception e) {
                System.err.println(e.toString());
                debug("SELECT value FROM settings WHERE key = '" + key + "'; (" + Measure.resultMs() + ")");
                return null;
            }
        }

        static public void remove(String table, String key, String condition) {
            Measure.start();

            if (condition.length() > 0) {
                condition = " WHERE key = '" + key + "' AND value =  '" + condition + "'";
            }
            try {
                s_p.executeUpdate("DELETE FROM " + table + condition);
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("DELETE FROM " + table + condition + "(" + Measure.resultMs() + ")");
        }

        static public void setOnce(String key, String value) {
            Measure.start();
            try {
                if (count(key) == 0) {
                    s_p.executeQuery("INSERT INTO settings (settingsindex, key, value) VALUES ("
                            + System.currentTimeMillis() + ", '" + key + "', '" + value + "');");
                } else {
                    s_p.executeUpdate("UPDATE settings SET value = '" + value + "' WHERE key = '" + key + "';");
                }
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("INSERT INTO settings (settingsindex, key, value) VALUES (" + System.currentTimeMillis() + ", '"
                    + key + "', '" + value + "'); (" + Measure.resultMs() + ")");
        }

        static public void set(String key, String value) {
            Measure.start();
            try {
                s_p.executeQuery("INSERT INTO settings (settingsindex, key, value) VALUES ("
                        + System.currentTimeMillis() + ", '" + key + "', '" + value + "');");
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            debug("INSERT INTO settings (settingsindex, key, value) VALUES (" + System.currentTimeMillis() + ", '"
                    + key + "', '" + value + "'); (" + Measure.resultMs() + ")");
        }

        static public void setURI(String uri) {
            set(Settings.getFromURI(uri, "key"), Settings.getFromURI(uri, "value"));
        }

        static public void setURIOnce(String uri) {
            setOnce(Settings.getFromURI(uri, "key"), Settings.getFromURI(uri, "value"));
        }

        static public LinkedList<String> getURI(String uri) {
            String key = uri.substring(uri.indexOf("key=") + 4);
            if (key.contains("&")) {
                key = key.substring(0, key.indexOf("&"));
            }

            return get(key);
        }

        static public String getFromURI(String uri, String key) {
            String value = "";
            if (uri.contains(key + "=")) {
                value = uri.substring(uri.indexOf(key + "=") + key.length() + 1);
                if (value.contains("&")) {
                    value = value.substring(0, value.indexOf("&"));
                }
                return value;
            } else {
                return value;
            }
        }
    }

    /**
     * 
     *      MEASURE
     * 
     */
    static class Measure {
        public Measure() {

        }

        static private Map<Long, Long> m = Collections.synchronizedMap(new HashMap<>());

        static public void start() {
            System.out.println("Start: " + Thread.currentThread().getId());
            startMs();
            //startNano();
        }

        static private void startMs() {
            m.put(Thread.currentThread().getId(), System.currentTimeMillis());
        }

        static private void startNano() {
            m.put(Thread.currentThread().getId(), System.nanoTime());
        }

        static public String resultMs() {
            String s = (System.currentTimeMillis() - m.get(Thread.currentThread().getId())) + " ms";
            //m.remove(Thread.currentThread().getId());
            return s;
        }

        static public String resultNano() {
            String s = (System.nanoTime() - m.get(Thread.currentThread().getId())) + " ns";
            //m.remove(Thread.currentThread().getId());
            return s;
        }
    }

    static class ImageCache {
        static private final Map<String, byte[]> images = new HashMap<>();

        static public void read() throws IOException {
            // read images
            File dir = new File("./www/images/");
            File[] files = dir.listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    File[] files2 = f.listFiles();
                    for (File f2 : files2) {
                        if (!f2.isDirectory()) {
                            String fn = f2.toString().replace("\\", "/");
                            fn = fn.substring(fn.indexOf("images/") + 7);
                            insert(fn, FileUtils.readFileToByteArray(f2));
                        }
                    }
                } else {
                    String fn = f.toString().replace("\\", "/");
                    fn = fn.substring(fn.indexOf("images/") + 7);
                    //insert(f, Base64.getEncoder().encodeToString(FileUtils.readFileToByteArray(file.toFile())));
                    insert(fn, FileUtils.readFileToByteArray(f));
                }
            }
        }

        static public boolean contains(String k) {
            return images.containsKey(k);
        }

        static public void insert(String k, byte[] v) {
            images.put(k, v);
        }

        static public byte[] get(int i) {
            return images.get(i);
        }

        static public byte[] get(String k) {
            return images.get(k);
        }
    }

    /**
     * 
     *      HANDLERS
     * 
     */

    /**
     *      FileHandler
     */
    static boolean is_mobile = false;

    static class FileHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) {
            Measure.start();

            // check header for mobile detection
            String ua = t.getRequestHeaders().get("User-Agent").get(0);
            if (ua.matches(
                    "(?i).*((android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino).*")
                    || ua.substring(0, 4).matches(
                            "(?i)1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\\-|your|zeto|zte\\-")) {
                is_mobile = true;
            }

            String uri = t.getRequestURI().toString();
            debug(Thread.currentThread().getId() + ">" + uri);

            if (uri.equals("/"))
                uri += "?file=index.html";

            // read file
            try {
                String file = Settings.getFromURI(uri, "file");

                // prevent . and ..
                if (file.startsWith(".")) {
                    sendResponse(t, 403, "Forbidden");
                    return;
                }
                if (file.startsWith("..")) {
                    sendResponse(t, 403, "Forbidden");
                    return;
                }

                // special file operations (dummy files that do not exist)
                if (file.contains("mobile_detection.java")) {
                    sendResponse(t, 200, "<script type='text/javascript'>setMobile(" + is_mobile + ");</script>");
                    return;
                }

                if (!file.startsWith("/")) {
                    file = "/" + file;
                }
                file = System.getProperty("user.dir") + "/www" + file;
                File f = new File(getPath(file));

                if (file.endsWith(".jpg") || file.endsWith(".gif") || file.endsWith(".png")) {
                    String suf = file.substring(file.lastIndexOf(".") + 1);
                    String k = file.substring(file.indexOf("images/") + 7);
                    //sendResponse(t, 200, "data:image/"+suf+";base64," + ImageCache.get(k));
                    sendResponse(t, 200, ImageCache.get(k), suf);
                    return;
                }
                sendResponse(t, 200, FileUtils.readFileToString(new File(getPath(file))));
            } catch (Exception e) {
                System.err.println(e.toString());
                sendResponse(t, 404, "Not found.");
            } finally {
                debug(Thread.currentThread().getId() + ">FileHandler>>>" + Measure.resultMs());
            }

            is_mobile = false; // if callers are mobile and desktop at the same time
        }
    }

    /**
     *      ConfigHandler
     */
    static boolean intense_imdb_work_done = false;

    static class ConfigHandler implements HttpHandler {
        private String getConfigPath() {
            StringBuilder content = new StringBuilder(1024);

            content.append("<script type=\"text/javascript\">");

            LinkedList<String> paths = Settings.get("path");
            for (String path : paths) {
                content.append("wh.push('");
                content.append(path);
                content.append("');");
            }

            content.append("getConfigPath();</script>");

            return content.toString();
        }

        private void deleteUnknownHashFiles() {
            try {
                LinkedList<String> entries = Memory.select("hash", "entries");

                Path dir = FileSystems.getDefault().getPath("work/");
                DirectoryStream<Path> stream = Files.newDirectoryStream(dir);

                // insert entries
                for (Path file : stream) {
                    String hash = file.toString().replace("\\", "/")
                            .substring(file.toString().lastIndexOf("/") + 1);
                    hash = hash.substring(0, hash.lastIndexOf("."));
                    if (!entries.contains(hash)) {
                        FileUtils.deleteQuietly(file.toFile());
                    } else {
                        int a = 0;
                    }
                }

                dir = FileSystems.getDefault().getPath("www/images/work/");
                stream = Files.newDirectoryStream(dir);

                // insert entries
                for (Path file : stream) {
                    String hash = file.toString().replace("\\", "/")
                            .substring(file.toString().lastIndexOf("/") + 1);
                    hash = hash.substring(0, hash.lastIndexOf("."));
                    if (!entries.contains(hash)) {
                        if (!FileUtils.deleteQuietly(file.toFile())) {
                            System.err.println("Unable to delete file '" + file.toString() + "'.");
                        }
                    }
                }
            } catch (Exception e) {
                System.err.println(e.toString());
            }
        }

        private String readName(String fn, String name) {
            String old = name;
            try (Scanner in = new Scanner(System.in)) {
                //name = in.nextLine();
                name = name.replace(" ", "_");
                name = name.replaceAll("- 3D - ", "");
                name = name.replaceAll("DVD9", "");
                name = name.replaceAll("1080p", "");
                name = name.replaceAll("720p", "");
                name = name.replaceAll("BlueRay", "");
                name = name.replaceAll("Remux", "");
                name = name.replaceAll("REMASTERED", "");
                name = name.replaceAll("DVD5", "");
                name = name.replaceAll("x264", "");
                name = name.replaceAll("DTSD", "");
                name = name.replaceAll(".DL.", "");
                name = name.replaceAll("DTS", "");
                name = name.replaceAll("AC3D", "");
                name = name.replaceAll("AVC", "");
                name = name.replaceAll("UNTOUCHED", "");
                name = name.replaceAll("PAL", "");
                name = name.replaceAll("DVDR", "");
                name = name.replaceAll("DVD", "");
                name = name.replaceAll("COMPLETE.BLURAY", "");
                name = name.replaceAll("HD2DVD", "");
                name = name.replaceAll("XviD", "");
                name = name.replaceAll("DTSHD", "");
                name = name.replaceAll(".COMPLETE.", "");
                name = name.replace("..", "");
                name = name.trim();
                return name;
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            // rename file
            File f = new File(fn);
            boolean ok = f.renameTo(new File(fn.replace(old, name)));

            return name;
        }

        private String getCleanName(String fn, String name) {
            // remove Thumbs.db
            if (name.equals("Thumbs.db")) {
                return "";
            }

            // invalid suffixes
            String[] i_suf = { ".sub" };
            for (String i_suf1 : i_suf) {
                if (name.endsWith(i_suf1)) {
                    return "";
                }
            }

            // min length 5
            if (name.length() < 5) {
                return readName(fn, name);
            }

            // no whitespaces
            if (name.contains(" ")) {
                name = readName(fn, name);
            }

            // strip suffix
            String[] suf = { ".mkv", ".avi", ".mpg", ".mp4", ".iso", ".ts" };
            for (String suf1 : suf) {
                if (name.contains(suf1)) {
                    name = name.substring(0, name.indexOf(suf1));
                }
            }
            // _(nnnn)
            if (!name.endsWith(")") || !name.substring(name.length() - 6, name.length() - 5).equals("(")
                    || !name.substring(name.length() - 7, name.length() - 6).equals("_")) {
                return readName(fn, name);
            }

            return name; // ok
        }

        private void readEntries() {
            if (intense_imdb_work_done) {
                return;
            }
            // only do imdb work only once
            intense_imdb_work_done = true;
            LinkedList<String> l = Settings.getURI("key=path");

            try {
                // remove entries
                s_m.executeUpdate("DELETE FROM entries;");

                // symbolic link path checker (exists() needs to long if link target not available)
                HashMap<String, Boolean> dirs_exists = new HashMap<>();

                for (String cur : l) {
                    // insert entries
                    File dir = new File(cur);
                    File[] files = dir.listFiles();
                    for (File file : files) {
                        // check is link
                        Path p = file.toPath();
                        boolean file_exists = true;
                        if (Files.isSymbolicLink(p)) {
                            file = Files.readSymbolicLink(p).toFile();
                            int idx;
                            String s = "/";
                            String path = file.toString().replace("\\", "/");
                            idx = path.lastIndexOf(s);
                            if (idx > -1) {
                                path = path.substring(0, idx);
                                if (!dirs_exists.containsKey(path)) {
                                    file_exists = Files.exists(p); // needs a while
                                    dirs_exists.put(path, file_exists);
                                } else {
                                    file_exists = dirs_exists.get(path);
                                }
                            } else { // invalid path?!
                                file_exists = false;
                            }
                        } else {
                            file_exists = file.exists();
                        }
                        String hash, name = "";
                        String filepath = file.toString();
                        filepath = filepath.replace("'", "");
                        filepath = filepath.replace("\\", "/");
                        int idx;
                        // name
                        if ((idx = filepath.lastIndexOf("/")) > -1) {
                            name = filepath.substring(idx + 1);
                        }
                        // only sweeeet and clean file names
                        name = getCleanName(filepath, name);
                        if (name.length() > 0) {
                            name = name.replace("_", " ").replace("[", "(").replace("]", ")");

                            // hash
                            MessageDigest md = MessageDigest.getInstance("MD5");
                            md.update(name.getBytes(), 0, name.getBytes().length);
                            hash = new BigInteger(1, md.digest()).toString(16);

                            Memory.insert("entries", "entriesindex, hash, filepath, name, f_exists",
                                    "" + System.currentTimeMillis() + ", '" + hash + "', '" + filepath + "', '"
                                            + name + "', " + file_exists);

                            // alt & title-url
                            MetaInfo.getMeta(name, hash);
                        }
                    }

                    // read images into cache
                    ImageCache.read();

                    // everything read, now cleanup work dirs
                    //deleteUnknownHashFiles();                      
                }
            } catch (SQLException | IOException | NoSuchAlgorithmException e) {
                System.err.println(e.toString());
            }

            // could be set when /config/ triggered on false
            intense_imdb_work_done = true;
        }

        private void delete(String uri) {
            String condition = Settings.getFromURI(uri, "condition");
            Settings.remove("settings", "path", condition);
        }

        private void seek() {
            new Thread() {
                public void run() {
                    try {
                        while (true) {
                            int state = 10;
                            StringBuilder r = new StringBuilder();
                            int code = 0;
                            while (state < 80) {
                                code = Http.get("http://127.0.0.1:8080/requests/status.xml?command=seek&val="
                                        + state + "%25", r, null);
                                if (code == 200) {
                                    String time = r.toString().substring(r.toString().indexOf("<time>"));
                                    String length = r.toString().substring(r.toString().indexOf("<length>"));
                                }

                                state += 10;
                                Thread.sleep(2000L);
                            }
                            return;
                        }
                    } catch (InterruptedException e) {
                    }
                }
            }.start();
        }

        private void killMp() {
            // try to kill old process
            try {
                if (p != null) {
                    p.destroy();
                }
            } catch (Exception e) {
                // ignore
            }
        }

        Process p = null;

        @Override
        public void handle(HttpExchange t) throws IOException {
            //Measure.start();

            String response = "";
            String uri = t.getRequestURI().toString();

            if (uri.contains("/config/?createDist")) {
                try {
                    // create zip process
                    Runtime rt = Runtime.getRuntime();
                    rt.exec("sh ./create_dist.sh");
                } catch (Exception e) {
                    System.err.println(e.toString());
                } finally {
                    sendResponse(t, 200, "");
                    debug("ConfigHandler>>>" + Measure.resultMs());
                    return;
                }
            }

            if (uri.contains("/config/?getConfigPath")) {
                sendResponse(t, 200, getConfigPath());
                debug("ConfigHandler>>>" + Measure.resultMs());
                return;
            }

            if (uri.contains("/config/?readEntries")) {
                killMp();
                readEntries();
                // count entries
                int with = Memory.select("url", "entries").size();
                int without = Memory.select("url", "entries", "url = 'null'").size();
                sendResponse(t, 200, "<script type=\"text/javascript\">calcQuality('" + with + "', '" + without
                        + "');</script>");
                debug("ConfigHandler>>>" + Measure.resultMs());
                return;
            }

            if (uri.contains("/config/?stop")) {
                killMp();
                sendResponse(t, 200, "");
                debug("ConfigHandler>>>" + Measure.resultMs());
                return;
            }

            if (uri.contains("/config/?seek")) {
                seek();
                sendResponse(t, 200, "hui");
                debug("ConfigHandler>>>" + Measure.resultMs());
                return;
            }
            if (uri.contains("/config/?play=")) {
                killMp();
                String filepath = Settings.getFromURI(uri, "play").replace("play_", "");
                LinkedList<String> l = Settings.get("mediaplayer");
                if (l.size() == 0) {
                    sendResponse(t, 200, "");
                    debug("ConfigHandler>>>" + Measure.resultMs());
                    return;
                }
                String mp = l.get(0).replace("%20", " ");

                try {
                    String line;
                    p = Runtime.getRuntime().exec("/usr/bin/" + mp + " " + filepath);
                    try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
                        while ((line = input.readLine()) != null) {
                            System.out.println(line);
                        }
                    }
                    int c = p.waitFor();
                    int a = 0;
                } catch (IOException | InterruptedException err) {
                    System.err.println(err.toString());
                }

                sendResponse(t, 200, "");
                debug("ConfigHandler>>>" + Measure.resultMs());
                return;
            }

            if (uri.contains("key=") && !uri.contains("value=")) {
                LinkedList<String> l = Settings.getURI(uri);
                response = "";
                for (String l1 : l) {
                    response += l1;
                    response += ", ";
                }
                if (response.length() > 2) {
                    response = response.substring(0, response.length() - 2);
                }
                sendResponse(t, 200, response);
                return;
            }

            // everything ABOVE -> meta work will be skipped

            // everything BELOW -> meta work will be done

            intense_imdb_work_done = false;
            response = "<script type=\"text/javascript\">window.location.href='/?file=test.html';</script>";
            if (uri.contains("/config/?delete")) {
                delete(uri);
                sendResponse(t, 200, response);
                debug("ConfigHandler>>>" + Measure.resultMs());
                return;
            }

            if (uri.contains("value=")) {
                if (uri.contains("once")) {
                    Settings.setURIOnce(uri);
                } else {
                    Settings.setURI(uri);
                }
            }

            sendResponse(t, 200, response);

            //debug("ConfigHandler>>>"+Measure.resultMs());
        }
    }

    /**
     *      StorageHandler
     */
    static boolean storageHandlerBusy = false;

    static class StorageHandler implements HttpHandler {
        private String printEntries(String uri, LinkedList<String> recs) {
            Measure.start();

            // build filter condition
            String filter = "";
            if (uri.contains("&filter=") || recs != null) {
                if (Settings.getFromURI(uri, "filter").equals("only_not_found")) {
                    filter = "url = 'null'";
                    // check all work images exists
                    LinkedList<String> hashes = Memory.select("hash", "entries");
                    for (String hash : hashes) {
                        try {
                            File f = new File("www/images/work/" + hash + ".jpg");
                            if (!f.exists()) {
                                filter += " OR hash = '" + hash + "'";
                            }
                        } catch (Exception e) {
                            System.err.println(e.toString());
                        }
                    }
                } else {
                    if (recs != null) {
                        String f;
                        for (int i = 0; i < recs.size(); i++) {
                            f = recs.get(i);
                            filter += "LOWER(name) LIKE '%" + f + "%' OR LOWER(alt) LIKE '%" + f
                                    + "%' OR LOWER(url) LIKE '%" + f + "%' OR LOWER(actors) LIKE '%" + f
                                    + "%' OR LOWER(hash) LIKE '%" + f + "%'";
                            filter += " OR ";
                        }
                        if (filter.length() > 0) {
                            filter = filter.substring(0, filter.length() - 4);
                        }
                    } else {
                        String f = Settings.getFromURI(uri, "filter").toLowerCase().replace("%20", " ");
                        if (!f.equals("search for movie title, alt title, imdb title or actor...")) {
                            filter = "LOWER(name) LIKE '%" + f + "%' OR LOWER(alt) LIKE '%" + f
                                    + "%' OR LOWER(url) LIKE '%" + f + "%' OR LOWER(actors) LIKE '%" + f
                                    + "%' OR LOWER(hash) LIKE '%" + f + "%'";
                        }
                    }
                }
            }

            String mode = "json";
            StringBuilder response = new StringBuilder(100000);
            response.append("<script type=\"text/javascript\">");

            // get mode
            if (uri.contains("&mode=")) {
                mode = Settings.getFromURI(uri, "mode");
                if (mode.length() == 0)
                    mode = "json";
            }

            // mode json
            if (mode.equals("json")) {
                LinkedList<String> l = Memory.selectAsJSON("hash, filepath, name, alt, url, f_exists, desc, actors",
                        "entries", "name", filter);
                for (String l1 : l) {
                    response.append("entries.push(Object.toJSON(").append(l1).append("));");
                }
                response.append("printEntriesCallback('json');");
            }

            // mode direct
            if (mode.equals("direct")) {
                LinkedList<String> l = Memory.select("name", "entries");
                response.append("$('wh_entries').innerHTML = '");
                String bg_color;
                for (int i = 0; i < l.size(); i++) {
                    if (i % 2 == 0) {
                        bg_color = "yellow";
                    } else {
                        bg_color = "red";
                    }
                    response.append("<div style='background-color: ").append(bg_color).append(";'>")
                            .append(l.get(i)).append("</div>");
                }
                response.append("';printEntriesCallback('direct');");
            }

            response.append("</script>");
            try {
                debug("printEntries>" + Measure.resultMs());
            } catch (Exception e) {
                System.err.println(e.toString());
            }
            return response.toString();
        }

        private LinkedList<String> related(String hash) {
            LinkedList<String> l = Memory.select("name", "entries", "hash = '" + hash + "'");
            if (l.size() > 0) { // entry found
                String s;
                int idx;
                // read work/hash.html
                try {
                    File f = new File("work/" + hash + ".html");
                    String content = FileUtils.readFileToString(f);
                    s = "class=\"rec_slide\">";
                    idx = content.indexOf(s);
                    if (idx > -1) { // valid html and recs found
                        content = content.substring(idx + s.length());
                        LinkedList<String> recs = new LinkedList<>();
                        s = "data-tconst=\"";
                        idx = content.indexOf(s);
                        while (idx > -1) {
                            content = content.substring(idx + s.length());
                            s = "\"";
                            idx = content.indexOf(s);
                            if (idx > -1) {
                                s = content.substring(0, idx);
                                if (!recs.contains(s)) {
                                    recs.add(s);
                                }
                            }
                            s = "data-tconst=\"";
                            idx = content.indexOf(s);
                        }
                        if (recs.size() > 0) { // recs found
                            return recs;
                        }
                    }
                } catch (Exception e) {
                    System.err.println(e.toString());
                }
                /*
                // get recs from bing (also searched)
                s = l.get(0);
                idx = s.indexOf("(");
                if (idx > -1) {
                s = s.substring(0, idx).trim();
                }
                s = s.replace(" ", "%20");
                StringBuilder r = new StringBuilder();
                Http.get("https://api.datamarket.azure.com/Bing/Search/v1/RelatedSearch?Query=%27" + s + "%27&Market=%27de-DE%27&$format=json", r, "OlkwRXEvNXJpNzRiME1KSDRHVlMxUjM4aWVaVlBNSkZVSTlMdjBFM2pPeHM=");
                */
            }

            return null;
        }

        @Override
        public void handle(HttpExchange t) throws IOException {
            Measure.start();

            String uri = t.getRequestURI().toString();

            // url was set
            if (uri.contains("/update/?url")) {
                String hash = Settings.getFromURI(uri, "hash");
                String url = Settings.getFromURI(uri, "url");

                // re-read meta data
                MetaInfo.getMeta(url, hash);

                sendResponse(t, 200,
                        "<script type=\"text/javascript\">window.location.href='/?file=test.html&scrollTo=" + hash
                                + "';</script>");
            }

            if (uri.startsWith("/select/?related")) {
                storageHandlerBusy = true;
                sendResponse(t, 200, printEntries(uri, related(Settings.getFromURI(uri, "hash"))));
                storageHandlerBusy = false;
                return;
            }

            if (uri.contains("/select/?printEntries")) {
                if (storageHandlerBusy) { // prevent flurry calls
                    sendResponse(t, 200, "");
                    return;
                }
                storageHandlerBusy = true;
                sendResponse(t, 200, printEntries(uri, null));
                storageHandlerBusy = false;
                return;
            }

            debug("StorageHandler>>>" + Measure.resultMs());
        }
    }

    static class MiscHandler implements HttpHandler {
        private String getComplementary(String c) {
            if (c.startsWith("%23")) {
                c = c.substring(3, c.length());
            }
            if (c.startsWith("#")) {
                c = c.substring(1, c.length());
            }
            int r = Integer.valueOf(c.substring(0, 2), 16);
            int g = Integer.valueOf(c.substring(2, 4), 16);
            int b = Integer.valueOf(c.substring(4, 6), 16);

            r = ((r - 255) * -1);
            g = ((g - 255) * -1);
            b = ((b - 255) * -1);

            return String.format("#%02x%02x%02x", r, g, b);
        }

        private String getAvgColor(String hash) {
            try {
                BufferedImage img = ImageIO.read(new File("www/images/work/_" + hash + ".jpg"));

                long r = 0;
                long g = 0;
                long b = 0;
                long px_count = 0;
                long c = 0;

                for (int y = 0; y < img.getHeight(); y++) {
                    for (int x = 0; x < img.getWidth(); x++) {
                        c = img.getRGB(x, y);
                        r += (c >> 16) & 0xff;
                        g += (c >> 8) & 0xff;
                        b += c & 0xff;
                        px_count++;
                    }
                }
                return String.format("#%02x%02x%02x", r / px_count, g / px_count, b / px_count);
            } catch (Exception e) {
                System.err.println(e.toString());
            }

            return "#000"; // default
        }

        @Override
        public void handle(HttpExchange t) throws IOException {
            String uri = t.getRequestURI().toString();

            if (uri.startsWith("/misc/?avgColor")) {
                sendResponse(t, 200, getAvgColor(Settings.getFromURI(uri, "hash")));
                return;
            }
            if (uri.startsWith("/misc/?getComplementary")) {
                // #FF0000 -> #00FFFF
                // #00FF00 -> #FF00FF
                // #0000FF -> #FFFF00
                sendResponse(t, 200, getComplementary(Settings.getFromURI(uri, "c")));
                return;
            }

            sendResponse(t, 200, "<script type=\"text/javascript\"></script>");
        }
    }

    static class ChatHandler implements HttpHandler {
        private String heardBeat() {
            StringBuilder r = new StringBuilder();
            int c = Http.get("http://adda-srv:9982/?heardbeat", r, null);
            if (c == 200) {
                return r.toString();
            } else {
                return "";
            }
        }

        private String send(String u) {
            String v = Settings.getFromURI(u, "send");
            StringBuilder r = new StringBuilder();
            int c = Http.get("http://adda-srv:9982/?send=" + v, r, null);
            if (c == 200) {
                return r.toString();
            } else {
                return "";
            }
        }

        private String communicate(String s) {
            StringBuilder r = new StringBuilder();
            int c = Http.get("http://adda-srv:9982/?communicate&euuid=" + s, r, null);
            if (c == 200) {
                return r.toString();
            } else {
                return "";
            }
        }

        private final LinkedList<String> messages = new LinkedList<>(); // (ip, messages)

        private String next() {
            StringBuilder r = new StringBuilder();
            int c = Http.get("http://adda-srv:9982/?next", r, null);
            if (c == 200) {
                String s = r.toString();
                if (s.length() > 0) {
                    messages.add(s);
                }
                return s;
            } else { // unable to find adda-srv
                return "<Client xxx disconnected>";
            }
        }

        @Override
        public void handle(HttpExchange t) throws IOException {
            String uri = t.getRequestURI().toString();
            String s = Settings.get("communicate").get(0);
            if (uri.equals("/chat/?heardbeat") && s.equals("true")) {
                sendResponse(t, 200, heardBeat());
                return;
            }
            if (uri.startsWith("/chat/?communicate")) {
                sendResponse(t, 200, communicate(Settings.getFromURI(uri, "euuid")));
                return;
            }
            if (uri.startsWith("/chat/?send=") && s.equals("true")) {
                sendResponse(t, 200, send(uri));
                return;
            }
            if (uri.equals("/chat/?next")) {
                sendResponse(t, 200, next());
                return;
            }

            sendResponse(t, 200, "<script type=\"text/javascript\"></script>");
        }
    }

    static public void sendResponse(HttpExchange t, int state, String r) {
        if (state != 200)
            System.err.println(r);

        try {
            byte[] bytes = r.getBytes();
            Headers h = t.getResponseHeaders();
            if (t.getRequestURI().toString().endsWith(".css")) {
                h.set("Content-Type", "text/css");
            } else {
                h.set("Content-Type", "text/html");
            }
            t.sendResponseHeaders(state, bytes.length);
            try (OutputStream os = t.getResponseBody()) {
                os.write(bytes);
            }
        } catch (Exception e) {
            System.err.println("BIG ERROR!!! " + e.toString());
        }
    }

    static public void sendResponse(HttpExchange t, int state, byte[] bytes, String type) {
        if (state != 200)
            System.err.println(bytes);

        if (type != null) {
            if (type.equals("png") || type.equals("gif") || type.equals("jpg")) {
                type = "image/" + type;
            }
        }

        try {
            Headers h = t.getResponseHeaders();
            h.set("Content-Type", type);
            t.sendResponseHeaders(state, bytes.length);
            try (OutputStream os = t.getResponseBody()) {
                os.write(bytes);
            }
        } catch (Exception e) {
            System.err.println("BIG ERROR!!! " + e.toString());
        }
    }

    // statics
    static Connection mem; // memory db
    static Connection pers; // persistent db
    static Statement s_m; // connection memory
    static Statement s_p; // connection persistent
    static BasicDataSource d_m; // data source memory
    static BasicDataSource d_p; // data source persistent

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            Measure.start();

            // create dbs            
            // memory
            d_m = new BasicDataSource();
            d_m.setDriverClassName("org.hsqldb.jdbc.JDBCDriver");
            d_m.setUrl("jdbc:hsqldb:mem:emma");
            d_m.setUsername("sa");
            d_m.setPassword("");
            mem = d_m.getConnection();
            s_m = mem.createStatement();
            s_m.executeUpdate(
                    "CREATE TABLE entries (entriesindex BIGINT, hash VARCHAR(42), filepath VARCHAR(255), name VARCHAR(255), alt VARCHAR(255), url VARCHAR(255), img VARCHAR(255), actors VARCHAR(2048), f_exists BOOLEAN, desc VARCHAR(2048));");
            // persistent
            d_p = new BasicDataSource();
            d_p.setDriverClassName("org.hsqldb.jdbc.JDBCDriver");
            d_p.setUrl("jdbc:hsqldb:file:settings");
            d_p.setUsername("sa");
            d_p.setPassword("");
            pers = d_p.getConnection();
            s_p = pers.createStatement();
            s_p.executeUpdate(
                    "CREATE TABLE IF NOT EXISTS settings (settingsindex BIGINT, key VARCHAR(255), value VARCHAR(255));");

            // default settings
            Settings.setOnce("communicate", "false");

            try {
                String line;
                Process p = Runtime.getRuntime().exec("sleep 0");
                try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
                    while ((line = input.readLine()) != null) {
                        System.out.println(line);
                    }
                }
                int c = p.waitFor();
                int a = 0;
            } catch (IOException | InterruptedException err) {
                //err.printStackTrace();
            }

            /*
            try {
            Logger LOG = LoggerFactory.getLogger(Emma.class);
            org.apache.http.client.HttpClient httpClient = new SimpleHttpClientBuilder().build();
            HttpTools httpTools = new HttpTools(httpClient);
            TmdbFind find = new TmdbFind("ca7d0fb887a7be06bfee3a557f6e2238", httpTools);
            FindResults result = find.find("tt0468569", ExternalSource.IMDB_ID, "de");
            System.out.println(result.toString());
            } catch (Exception e) {
            System.err.println(e.toString());
            }     
            */

            // build image cache
            ImageCache.read();

            // create http server
            InetSocketAddress addr = new InetSocketAddress(9980);
            HttpServer server = HttpServer.create(addr, 0);
            server.createContext("/", new FileHandler());
            server.createContext("/select", new StorageHandler());
            server.createContext("/insert", new StorageHandler());
            server.createContext("/update", new StorageHandler());
            server.createContext("/delete", new StorageHandler());
            server.createContext("/config", new ConfigHandler());
            server.createContext("/chat", new ChatHandler());
            server.createContext("/misc", new MiscHandler());
            server.setExecutor(Executors.newFixedThreadPool(12));
            server.start();

            debug(">" + Measure.resultMs());
        } catch (SQLException | IOException e) {
            System.err.println(e.toString());
        } finally {

        }
    }
}