com.moviejukebox.plugin.KinopoiskPlugin.java Source code

Java tutorial

Introduction

Here is the source code for com.moviejukebox.plugin.KinopoiskPlugin.java

Source

/*
 *      Copyright (c) 2004-2016 YAMJ Members
 *      https://github.com/orgs/YAMJ/people
 *
 *      This file is part of the Yet Another Movie Jukebox (YAMJ) project.
 *
 *      YAMJ is free software: you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation, either version 3 of the License, or
 *      any later version.
 *
 *      YAMJ is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *
 *      You should have received a copy of the GNU General Public License
 *      along with YAMJ.  If not, see <http://www.gnu.org/licenses/>.
 *
 *      Web: https://github.com/YAMJ/yamj-v2
 *
 */
package com.moviejukebox.plugin;

import static com.moviejukebox.tools.PropertiesUtil.FALSE;
import static com.moviejukebox.tools.PropertiesUtil.TRUE;

import java.io.IOException;
import java.net.URLEncoder;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.moviejukebox.model.Award;
import com.moviejukebox.model.AwardEvent;
import com.moviejukebox.model.Filmography;
import com.moviejukebox.model.Movie;
import com.moviejukebox.model.Person;
import com.moviejukebox.model.enumerations.OverrideFlag;
import com.moviejukebox.tools.FileTools;
import com.moviejukebox.tools.HTMLTools;
import com.moviejukebox.tools.OverrideTools;
import com.moviejukebox.tools.PropertiesUtil;
import com.moviejukebox.tools.StringTools;
import com.moviejukebox.tools.SystemTools;
import java.nio.charset.Charset;

/**
 * Plugin to retrieve movie data from Russian movie database www.kinopoisk.ru Written by Yury Sidorov.
 *
 * First the movie data is searched in IMDB and TheTvDB.
 *
 * After that the movie is searched in kinopoisk and movie data is updated.
 *
 * It is possible to specify URL of the movie page on kinopoisk in the .nfo file. In this case movie data will be retrieved from
 * this page only.
 */
public class KinopoiskPlugin extends ImdbPlugin {

    private static final Logger LOG = LoggerFactory.getLogger(KinopoiskPlugin.class);
    public static final String KINOPOISK_PLUGIN_ID = "kinopoisk";
    private static final String SITE_DESIGN = "Site design changed - failed get KinoPoisk '{}'!";
    private static final Charset CHARSET = Charset.forName("windows-1251");
    private static final String ENGLISH = "english";
    private static final String FILM_URL = "http://www.kinopoisk.ru/film/";
    private static final String NAME_URL = "http://www.kinopoisk.ru/name/";
    private final String preferredRating = PropertiesUtil.getProperty("kinopoisk.rating", "imdb");
    private final TheTvDBPlugin tvdb;
    // Shows what name is on the first position with respect to divider
    private final String titleLeader = PropertiesUtil.getProperty("kinopoisk.title.leader", ENGLISH);
    private final String titleDivider = PropertiesUtil.getProperty("kinopoisk.title.divider", " - ");
    private final boolean joinTitles = PropertiesUtil.getBooleanProperty("kinopoisk.title.join", Boolean.TRUE);
    // Set NFO information priority
    private final boolean nfoPriority = PropertiesUtil.getBooleanProperty("kinopoisk.NFOpriority", Boolean.FALSE);
    private boolean nfoRating = false;
    private boolean nfoFanart = false;
    private boolean nfoPoster = false;
    private boolean nfoAwards = false;
    private final String tmpAwards = PropertiesUtil.getProperty("mjb.scrapeAwards", FALSE);
    private final boolean scrapeWonAwards = "won".equalsIgnoreCase(tmpAwards);
    private final boolean scrapeAwards = tmpAwards.equalsIgnoreCase(TRUE) || scrapeWonAwards;
    private final boolean scrapeBusiness = PropertiesUtil.getBooleanProperty("mjb.scrapeBusiness", Boolean.FALSE);
    private final boolean scrapeTrivia = PropertiesUtil.getBooleanProperty("mjb.scrapeTrivia", Boolean.FALSE);
    // Set priority fanart & poster by kinopoisk.ru
    private final boolean fanArt = PropertiesUtil.getBooleanProperty("kinopoisk.fanart", Boolean.FALSE);
    private final boolean poster = PropertiesUtil.getBooleanProperty("kinopoisk.poster", Boolean.FALSE);
    private final boolean kadr = PropertiesUtil.getBooleanProperty("kinopoisk.kadr", Boolean.FALSE);
    private final boolean companyAll = "all"
            .equalsIgnoreCase(PropertiesUtil.getProperty("kinopoisk.company", "first"));
    private final boolean countryAll = "all"
            .equalsIgnoreCase(PropertiesUtil.getProperty("kinopoisk.country", "first"));
    private final boolean clearAward = PropertiesUtil.getBooleanProperty("kinopoisk.clear.award", Boolean.FALSE);
    private final boolean clearTrivia = PropertiesUtil.getBooleanProperty("kinopoisk.clear.trivia", Boolean.FALSE);
    private final boolean translitCountry = PropertiesUtil.getBooleanProperty("kinopoisk.translit.country",
            Boolean.FALSE);
    private final boolean russianCertification = PropertiesUtil.getBooleanProperty("kinopoisk.russianCertification",
            Boolean.FALSE);
    private final String etalonId = PropertiesUtil.getProperty("kinopoisk.etalon", "448");
    // Personal information
    private final boolean translateName = PropertiesUtil.getBooleanProperty("kinopoisk.translateName",
            Boolean.FALSE);
    private final int biographyLength = PropertiesUtil.getIntProperty("plugin.biography.maxlength", 500);
    private final boolean skipTV = PropertiesUtil.getBooleanProperty("plugin.people.skip.TV", Boolean.FALSE);
    private final boolean skipV = PropertiesUtil.getBooleanProperty("plugin.people.skip.V", Boolean.FALSE);
    private final List<String> jobsInclude = Arrays.asList(PropertiesUtil
            .getProperty("plugin.filmography.jobsInclude", "Director,Writer,Actor,Actress").split(","));
    private int triviaMax = PropertiesUtil.getIntProperty("plugin.trivia.maxCount", 15);

    public KinopoiskPlugin() {
        super();
        preferredCountry = PropertiesUtil.getProperty("imdb.preferredCountry", "Russia");
        actorMax = PropertiesUtil.getReplacedIntProperty("movie.actor.maxCount", "plugin.people.maxCount.actor",
                10);
        directorMax = PropertiesUtil.getReplacedIntProperty("movie.director.maxCount",
                "plugin.people.maxCount.director", 2);
        writerMax = PropertiesUtil.getReplacedIntProperty("movie.writer.maxCount", "plugin.people.maxCount.writer",
                3);
        tvdb = new TheTvDBPlugin();
    }

    @Override
    public String getPluginID() {
        return KINOPOISK_PLUGIN_ID;
    }

    @Override
    public boolean scan(Movie mediaFile) {
        boolean retval = false;
        String kinopoiskId = mediaFile.getId(KINOPOISK_PLUGIN_ID);

        if (nfoPriority) {
            // checked NFO data
            nfoRating = mediaFile.getRating() > -1;
            nfoFanart = StringTools.isValidString(mediaFile.getFanartURL());
            nfoPoster = StringTools.isValidString(mediaFile.getPosterURL());
            nfoAwards = !mediaFile.getAwards().isEmpty();
        }

        if (StringTools.isNotValidString(kinopoiskId)) {
            // store original russian title and year
            String name = mediaFile.getOriginalTitle();
            String year = mediaFile.getYear();

            final String previousTitle = mediaFile.getTitle();
            int dash = previousTitle.indexOf(titleDivider);
            if (dash != -1) {
                if (titleLeader.equals(ENGLISH)) {
                    mediaFile.setTitle(previousTitle.substring(0, dash),
                            mediaFile.getOverrideSource(OverrideFlag.TITLE));
                } else {
                    mediaFile.setTitle(previousTitle.substring(dash),
                            mediaFile.getOverrideSource(OverrideFlag.TITLE));
                }
            }
            // Get base info from imdb or tvdb
            if (!mediaFile.isTVShow()) {
                super.scan(mediaFile);
            } else {
                tvdb.scan(mediaFile);
            }

            // Let's replace dash (-) by space ( ) in Title.
            //name.replace(titleDivider, " ");
            dash = name.indexOf(titleDivider);
            if (dash != -1) {
                if (titleLeader.equals(ENGLISH)) {
                    name = name.substring(0, dash);
                } else {
                    name = name.substring(dash);
                }
            }

            kinopoiskId = getKinopoiskId(mediaFile, name, year, mediaFile.getSeason());
            mediaFile.setId(KINOPOISK_PLUGIN_ID, kinopoiskId);
            //        } else {
            // If ID is specified in NFO, set original title to unknown
            //            mediaFile.setTitle(Movie.UNKNOWN);
        }

        if (StringTools.isValidString(kinopoiskId)) {
            // Replace some movie data by data from Kinopoisk
            retval = updateKinopoiskMediaInfo(mediaFile, kinopoiskId);
        }

        return retval;
    }

    @Override
    public boolean scanNFO(String nfo, Movie movie) {
        boolean result = false;
        LOG.debug("Scanning NFO for Kinopoisk Id");
        int beginIndex = nfo.indexOf("kinopoisk.ru/level/1/film/");
        StringTokenizer st = null;
        if (beginIndex != -1) {
            st = new StringTokenizer(nfo.substring(beginIndex + 26), "/");
        } else {
            beginIndex = nfo.indexOf("kinopoisk.ru/film/");
            if (beginIndex != -1) {
                st = new StringTokenizer(nfo.substring(beginIndex + 18), "/");
            }
        }
        if (st != null) {
            movie.setId(KinopoiskPlugin.KINOPOISK_PLUGIN_ID, st.nextToken());
            LOG.debug("Kinopoisk Id found in nfo = {}", movie.getId(KinopoiskPlugin.KINOPOISK_PLUGIN_ID));
            result = true;
        } else {
            LOG.debug("No Kinopoisk Id found in nfo !");
        }
        super.scanNFO(nfo, movie);
        return result;
    }

    public String getKinopoiskId(Movie movie) {
        String kinopoiskId = movie.getId(KINOPOISK_PLUGIN_ID);

        if (StringTools.isNotValidString(kinopoiskId)) {
            int season = movie.getSeason();
            String name = movie.getOriginalTitle();
            String year = movie.getYear();

            int dash = name.indexOf(titleDivider);
            if (dash > -1) {
                name = titleLeader.equals(ENGLISH) ? name.substring(0, dash) : name.substring(dash);
            }

            getKinopoiskId(movie, name, year, season);
        }
        return kinopoiskId;
    }

    public String getKinopoiskId(Movie movie, String name, String year, int season) {
        String kinopoiskId = movie.getId(KINOPOISK_PLUGIN_ID);

        if (StringTools.isNotValidString(kinopoiskId)) {
            kinopoiskId = getKinopoiskId(name, year, season);

            if (StringTools.isValidString(year) && StringTools.isNotValidString(kinopoiskId)) {
                // Search for year +/-1, since the year is not always correct
                int tmpYear = NumberUtils.toInt(year, -1);
                if (tmpYear > -1) {
                    kinopoiskId = getKinopoiskId(name,
                            Integer.toString(tmpYear - 1) + "-" + Integer.toString(tmpYear + 1), season);

                    if (StringTools.isNotValidString(kinopoiskId)) {
                        // Trying without specifying the year
                        kinopoiskId = getKinopoiskId(name, Movie.UNKNOWN, season);
                    }
                }
            }
        }
        return kinopoiskId;
    }

    /**
     * Retrieve Kinopoisk matching the specified movie name and year. This routine is base on a Google request.
     *
     * @param movieName
     * @param year
     * @param season
     * @return
     */
    public String getKinopoiskId(String movieName, String year, int season) {
        try {
            String kinopoiskId;
            String sb = movieName;
            // Unaccenting letters
            sb = Normalizer.normalize(sb, Normalizer.Form.NFD);
            sb = sb.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

            sb = "&m_act[find]=" + URLEncoder.encode(sb, "UTF-8").replace(" ", "+");

            if (season != -1) {
                sb = sb + "&m_act[content_find]=serial";
            } else if (StringTools.isValidString(year)) {
                if (year.indexOf('-') > -1) {
                    String[] years = year.split("-");
                    sb = sb + "&m_act[from_year]=" + years[0];
                    sb = sb + "&m_act[to_year]=" + years[1];
                } else {
                    sb = sb + "&m_act[year]=" + year;
                }
            }

            sb = "http://kinopoisk.ru/index.php?level=7&from=forma&result=adv&m_act[from]=forma&m_act[what]=content"
                    + sb;
            String xml = httpClient.request(sb, CHARSET);

            // Checking for zero results
            if (!xml.contains("class=\"search_results\"")) {
                // Checking direct movie page
                if (xml.contains("class=\"moviename-big\"")) {
                    return HTMLTools.extractTag(xml, "id_film = ", ";");
                }
                return Movie.UNKNOWN;
            }

            // Checking if we got the movie page directly
            int beginIndex = xml.indexOf("id_film = ");
            if (beginIndex == -1) {
                // It's search results page, searching a link to the movie page
                beginIndex = xml.indexOf("class=\"search_results\"");
                if (beginIndex == -1) {
                    return Movie.UNKNOWN;
                }

                beginIndex = xml.indexOf("/level/1/film/", beginIndex);
                if (beginIndex == -1) {
                    return Movie.UNKNOWN;
                }

                StringTokenizer st = new StringTokenizer(xml.substring(beginIndex + 14), "/\"");
                kinopoiskId = st.nextToken();
            } else {
                // It's the movie page
                StringTokenizer st = new StringTokenizer(xml.substring(beginIndex + 10), ";");
                kinopoiskId = st.nextToken();
            }

            if (StringTools.isValidString(kinopoiskId) && StringUtils.isNumeric(kinopoiskId)) {
                return kinopoiskId;
            }
        } catch (IOException error) {
            LOG.error("Failed retreiving Kinopoisk Id for movie : {}", movieName);
            LOG.error("Error : {}", error.getMessage());
        }
        return Movie.UNKNOWN;
    }

    private static boolean getCertification(Movie movie, String xml, String open, String close, String begin,
            String end, String key) {
        boolean certificationFounded = false;

        for (String mpaaTag : HTMLTools.extractTags(xml, open, close, begin, end)) {
            int pos = mpaaTag.indexOf(key);
            if (pos != -1) {
                int start = pos + key.length();
                pos = mpaaTag.indexOf("\"", start);
                if (pos != -1) {
                    mpaaTag = mpaaTag.substring(start, pos);
                    movie.setCertification(mpaaTag, KINOPOISK_PLUGIN_ID);
                    certificationFounded = true;
                }
            }
            break;
        }

        return certificationFounded;
    }

    /**
     * Scan Kinopoisk html page for the specified movie
     */
    private boolean updateKinopoiskMediaInfo(Movie movie, String kinopoiskId) {
        try {
            String originalTitle = movie.getTitle();
            String newTitle = originalTitle;
            String xml = httpClient.request(FILM_URL + kinopoiskId, CHARSET);
            boolean etalonFlag = kinopoiskId.equals(etalonId);
            // Work-around for issue #649
            xml = xml.replace("&#133;", "&hellip;");
            xml = xml.replace("&#151;", "&mdash;");

            // Title
            boolean overrideTitle = OverrideTools.checkOverwriteTitle(movie, KINOPOISK_PLUGIN_ID);
            if (overrideTitle || etalonFlag) {
                newTitle = HTMLTools.extractTag(xml, "class=\"moviename-big\" itemprop=\"name\">", 0, "</");
                if (StringTools.isValidString(newTitle)) {
                    if (overrideTitle) {
                        int i = newTitle.indexOf("(?");
                        if (i >= 0) {
                            newTitle = newTitle.substring(0, i);
                            movie.setMovieType(Movie.TYPE_TVSHOW);
                        }
                        newTitle = newTitle.replace('\u00A0', ' ').trim();
                        if (movie.getSeason() != -1) {
                            newTitle = newTitle + ", ? " + movie.getSeason();
                        }

                        // Original title
                        originalTitle = newTitle;
                        for (String s : HTMLTools.extractTags(xml, "class=\"moviename-big\" itemprop=\"name\">",
                                "</span>", "itemprop=\"alternativeHeadline\">", "</span>")) {
                            if (!s.isEmpty()) {
                                originalTitle = s;
                                if (joinTitles) {
                                    newTitle = newTitle + Movie.SPACE_SLASH_SPACE + originalTitle;
                                }
                            }
                            break;
                        }
                    } else {
                        newTitle = originalTitle;
                    }
                } else {
                    if (etalonFlag) {
                        LOG.error(SITE_DESIGN, "movie title");
                    }
                    newTitle = originalTitle;
                }
            }

            // Plot
            if (OverrideTools.checkOverwritePlot(movie, KINOPOISK_PLUGIN_ID)) {
                StringBuilder plot = new StringBuilder();
                for (String subPlot : HTMLTools.extractTags(xml, "<span class=\"_reachbanner_\"", "</span>", "",
                        "<")) {
                    if (!subPlot.isEmpty()) {
                        if (plot.length() > 0) {
                            plot.append(" ");
                        }
                        plot.append(subPlot);
                    }
                }

                movie.setPlot(plot.toString(), KINOPOISK_PLUGIN_ID);

                if (etalonFlag && (plot.length() == 0)) {
                    LOG.error(SITE_DESIGN, "plot");
                }
            }

            boolean valueFounded = false;
            boolean genresFounded = false;
            boolean certificationFounded = false;
            boolean countryFounded = false;
            boolean yearFounded = false;
            boolean runtimeFounded = false;
            boolean taglineFounded = false;
            boolean releaseFounded = false;
            for (String item : HTMLTools.extractTags(xml, "<table class=\"info\">", "</table>", "<tr", "</tr>")) {
                item = "<tr>" + item + "</tr>";
                // Genres
                if (OverrideTools.checkOverwriteGenres(movie, KINOPOISK_PLUGIN_ID)) {
                    LinkedList<String> newGenres = new LinkedList<>();
                    boolean innerGenresFound = false;
                    for (String genre : HTMLTools.extractTags(item, "><", "</tr>",
                            "<a href=\"/lists/m_act%5Bgenre%5D/", "</a>")) {
                        innerGenresFound = true;
                        genre = genre.substring(0, 1).toUpperCase() + genre.substring(1, genre.length());
                        if ("".equalsIgnoreCase(genre)) {
                            newGenres.addFirst(genre);
                        } else {
                            newGenres.add(genre);
                        }
                    }
                    if (innerGenresFound) {
                        // Limit genres count
                        int maxGenres = 9;
                        try {
                            maxGenres = PropertiesUtil.getIntProperty("genres.max", 9);
                        } catch (Exception ignore) {
                            //
                        }
                        while (newGenres.size() > maxGenres) {
                            newGenres.removeLast();
                        }

                        movie.setGenres(newGenres, KINOPOISK_PLUGIN_ID);
                        genresFounded = true;
                    }
                }

                // Certification from MPAA
                if (OverrideTools.checkOverwriteCertification(movie, KINOPOISK_PLUGIN_ID)) {
                    if (!certificationFounded) {
                        certificationFounded = getCertification(movie, item, "> MPAA<", "</tr>",
                                "<a href=\"/film", "</a>", "alt=\" ");
                    }
                    if (!certificationFounded || russianCertification) {
                        certificationFounded |= getCertification(movie, item, ">?<", "</tr>",
                                "<td style=\"color", "</span>", "ageLimit ");
                    }
                }

                // Country
                if (OverrideTools.checkOverwriteCountry(movie, KINOPOISK_PLUGIN_ID)) {
                    Collection<String> scraped = HTMLTools.extractTags(item, ">?<", "</tr>",
                            "a href=\"/lists/m_act%5Bcountry%5D/", "</a>");
                    if (scraped != null && !scraped.isEmpty()) {
                        List<String> countries = new ArrayList<>();
                        for (String country : scraped) {
                            if (translitCountry) {
                                country = FileTools.makeSafeFilename(country);
                            }
                            countries.add(country);

                            if (!countryAll) {
                                // just first country, so break here
                                break;
                            }
                        }
                        movie.setCountries(countries, KINOPOISK_PLUGIN_ID);
                        countryFounded = true;
                    }
                }

                // Year
                if (OverrideTools.checkOverwriteYear(movie, KINOPOISK_PLUGIN_ID)) {
                    for (String year : HTMLTools.extractTags(item, "><", "</tr>",
                            "<a href=\"/lists/m_act%5Byear%5D/", "</a>")) {
                        movie.setYear(year, KINOPOISK_PLUGIN_ID);
                        yearFounded = true;
                        break;
                    }
                }

                // Run time
                if (OverrideTools.checkOverwriteRuntime(movie, KINOPOISK_PLUGIN_ID) || etalonFlag) {
                    for (String runtime : HTMLTools.extractTags(item, ">?<", "</tr>", "<td", "</td>")) {
                        if (runtime.contains("<span")) {
                            runtime = runtime.substring(0, runtime.indexOf("<span"));
                        }
                        movie.setRuntime(runtime, KINOPOISK_PLUGIN_ID);
                        runtimeFounded = true;
                        break;
                    }
                }

                // Tagline
                if (OverrideTools.checkOverwriteTagline(movie, KINOPOISK_PLUGIN_ID)) {
                    for (String tagline : HTMLTools.extractTags(item, ">?<", "</tr>", "<td ", "</td>")) {
                        if (tagline.length() > 0) {
                            movie.setTagline(tagline.replace("\u00AB", "\"").replace("\u00BB", "\""),
                                    KINOPOISK_PLUGIN_ID);
                            taglineFounded = true;
                            break;
                        }
                    }
                }

                // Release date
                if (OverrideTools.checkOverwriteReleaseDate(movie, KINOPOISK_PLUGIN_ID)) {
                    String releaseDate = "";
                    for (String release : HTMLTools.extractTags(item, "> ()<", "</tr>",
                            "<a href=\"/film/", "</a>")) {
                        releaseDate = release;
                        releaseFounded = true;
                        break;
                    }
                    if (StringUtils.isBlank(releaseDate)) {
                        for (String release : HTMLTools.extractTags(item, "> ()<", "</tr>",
                                "<a href=\"/film/", "</a>")) {
                            releaseDate = release;
                            releaseFounded = true;
                            break;
                        }
                    }
                    movie.setReleaseDate(releaseDate, KINOPOISK_PLUGIN_ID);
                }
            }

            if (etalonFlag) {
                if (!genresFounded) {
                    LOG.error(SITE_DESIGN, "genres");
                }
                if (!certificationFounded) {
                    LOG.error(SITE_DESIGN, "certification");
                }
                if (!countryFounded) {
                    LOG.error(SITE_DESIGN, "country");
                }
                if (!yearFounded) {
                    LOG.error(SITE_DESIGN, "year");
                }
                if (!runtimeFounded) {
                    LOG.error(SITE_DESIGN, "runtime");
                }
                if (!taglineFounded) {
                    LOG.error(SITE_DESIGN, "tagline");
                }
                if (!releaseFounded) {
                    LOG.error(SITE_DESIGN, "release");
                }
            }

            // Rating
            if (!nfoRating) {
                valueFounded = false;
                for (String rating : HTMLTools.extractTags(xml, "<a href=\"/film/" + kinopoiskId + "/votes/\"",
                        "</a>", "<span", "</span>")) {
                    int kinopoiskRating = StringTools.parseRating(rating);
                    movie.addRating(KINOPOISK_PLUGIN_ID, kinopoiskRating);
                    valueFounded = true;
                    break;
                }
                if (!valueFounded && etalonFlag) {
                    LOG.error(SITE_DESIGN, "rating");
                }

                int imdbRating = movie.getRating(IMDB_PLUGIN_ID);
                if (imdbRating == -1 || etalonFlag) {
                    // Get IMDB rating from kinopoisk page
                    String rating = HTMLTools.extractTag(xml, ">IMDb:", 0, " (");
                    valueFounded = false;
                    if (StringTools.isValidString(rating)) {
                        imdbRating = StringTools.parseRating(rating);
                        movie.addRating(IMDB_PLUGIN_ID, imdbRating);
                        valueFounded = true;
                    }
                    if (!valueFounded && etalonFlag) {
                        LOG.error(SITE_DESIGN, "IMDB rating");
                    }
                }
            }

            // Top250
            if (OverrideTools.checkOverwriteTop250(movie, KINOPOISK_PLUGIN_ID)) {
                movie.setTop250(HTMLTools.extractTag(xml, "<a href=\"/level/20/#", 0, "\""), KINOPOISK_PLUGIN_ID);
            }

            // Poster
            String posterURL = movie.getPosterURL();
            if (StringTools.isNotValidString(posterURL) || (!nfoPoster && poster) || etalonFlag) {
                valueFounded = false;
                if (poster || etalonFlag) {
                    String previousURL = posterURL;
                    posterURL = Movie.UNKNOWN;

                    // Load page with all poster
                    String wholeArts = httpClient.request(FILM_URL + kinopoiskId + "/posters/", CHARSET);
                    if (StringTools.isValidString(wholeArts)) {
                        if (wholeArts.contains("<table class=\"fotos")) {
                            String picture = HTMLTools.extractTag(wholeArts,
                                    "src=\"http://st.kinopoisk.ru/images/poster/sm_", 0, "\"");
                            if (StringTools.isValidString(picture)) {
                                posterURL = "http://st.kinopoisk.ru/images/poster/" + picture;
                                valueFounded = true;
                            }
                        }
                    }

                    if (StringTools.isNotValidString(posterURL)) {
                        posterURL = previousURL;
                    }

                    if (StringTools.isValidString(posterURL)) {
                        movie.setPosterURL(posterURL);
                        movie.setPosterFilename(movie.getBaseName() + ".jpg");
                        LOG.debug("Set poster URL to {} for {}", posterURL, movie.getBaseName());
                    }
                }
                if (StringTools.isNotValidString(movie.getPosterURL())) {
                    if (overrideTitle) {
                        movie.setTitle(originalTitle, KINOPOISK_PLUGIN_ID);
                    }
                    // Removing Poster info from plugins. Use of PosterScanner routine instead.
                    // movie.setPosterURL(locatePosterURL(movie, ""));
                }
                if (!valueFounded && etalonFlag) {
                    LOG.error(SITE_DESIGN, "poster");
                }
            }

            // Fanart
            String fanURL = movie.getFanartURL();
            if (StringTools.isNotValidString(fanURL) || (!nfoFanart && (fanArt || kadr)) || etalonFlag
                    || fanURL.contains("originalnull")) {
                valueFounded = false;
                try {
                    String previousURL = fanURL;
                    fanURL = Movie.UNKNOWN;

                    // Load page with all wallpaper
                    String wholeArts = httpClient.request("http://www.kinopoisk.ru/film/" + kinopoiskId + "/wall/",
                            CHARSET);
                    if (StringTools.isValidString(wholeArts)) {
                        if (wholeArts.contains("<table class=\"fotos")) {
                            String picture = HTMLTools.extractTag(wholeArts,
                                    "src=\"http://st.kinopoisk.ru/images/wallpaper/sm_", 0, ".jpg");
                            if (StringTools.isValidString(picture)) {
                                if (picture.contains("_")) {
                                    picture = picture.substring(0, picture.indexOf('_'));
                                }
                                String size = HTMLTools.extractTag(wholeArts,
                                        "<u><a href=\"/picture/" + picture + "/w_size/", 0, "/");
                                wholeArts = httpClient.request(
                                        "http://www.kinopoisk.ru/picture/" + picture + "/w_size/" + size, CHARSET);
                                if (StringTools.isValidString(wholeArts)) {
                                    picture = HTMLTools.extractTag(wholeArts,
                                            "src=\"http://st-im.kinopoisk.r/im/wallpaper/", 0, "\"");
                                    if (StringTools.isValidString(picture)) {
                                        fanURL = "http://st.kinopoisk.ru/im/wallpaper/" + picture;
                                        valueFounded = true;
                                    }
                                }
                            }
                        }
                    }

                    if (kadr && (StringTools.isNotValidString(fanURL) || fanURL.contains("originalnull"))) {
                        // Load page with all videoimage
                        wholeArts = httpClient.request(FILM_URL + kinopoiskId + "/stills/", CHARSET);
                        if (StringTools.isValidString(wholeArts)) {
                            // Looking for photos table
                            int photosInd = wholeArts.indexOf("<table class=\"fotos");
                            if (photosInd != -1) {
                                String picture = HTMLTools.extractTag(wholeArts,
                                        "src=\"http://st.kinopoisk.ru/images/kadr/sm_", 0, "\"");
                                if (StringTools.isValidString(picture)) {
                                    fanURL = "http://www.kinopoisk.ru/images/kadr/" + picture;
                                    valueFounded = true;
                                }
                            }
                        }
                    }

                    if (StringTools.isNotValidString(fanURL)) {
                        fanURL = previousURL;
                    }

                    if (StringTools.isValidString(fanURL)) {
                        movie.setFanartURL(fanURL);
                        movie.setFanartFilename(movie.getBaseName() + fanartToken + "." + fanartExtension);
                        LOG.debug("Set fanart URL to {} for {}", fanURL, movie.getBaseName());
                    }
                } catch (IOException ex) {
                    // Ignore
                }
            }

            // Studio/Company
            if (OverrideTools.checkOverwriteCompany(movie, KINOPOISK_PLUGIN_ID)) {
                xml = httpClient.request(FILM_URL + kinopoiskId + "/studio/", CHARSET);
                valueFounded = false;
                if (StringTools.isValidString(xml)) {
                    Collection<String> studio = new ArrayList<>();
                    if (xml.contains(">?:<")) {
                        for (String tmp : HTMLTools.extractTags(xml, ">?:<", "</table>",
                                "<a href=\"/lists/m_act%5Bstudio%5D/", "</a>")) {
                            studio.add(HTMLTools.removeHtmlTags(tmp));
                            if (!companyAll) {
                                break;
                            }
                        }
                    }
                    if (xml.contains(">:<") && (companyAll || studio.isEmpty())) {
                        for (String tmp : HTMLTools.extractTags(xml, ">:<", "</table>",
                                "<a href=\"/lists/m_act%5Bcompany%5D/", "</a>")) {
                            studio.add(HTMLTools.removeHtmlTags(tmp));
                        }
                    }
                    if (!studio.isEmpty()) {
                        movie.setCompany(companyAll ? StringUtils.join(studio, Movie.SPACE_SLASH_SPACE)
                                : new ArrayList<>(studio).get(0), KINOPOISK_PLUGIN_ID);
                        valueFounded = true;
                    }
                }
                if (!valueFounded && etalonFlag) {
                    LOG.error(SITE_DESIGN, "company");
                }
            }

            // Awards
            if ((scrapeAwards && !nfoAwards) || etalonFlag) {
                if (clearAward) {
                    movie.clearAwards();
                }
                xml = httpClient.request("http://www.kinopoisk.ru/film/" + kinopoiskId + "/awards/", CHARSET);
                Collection<AwardEvent> awards = new ArrayList<>();
                if (StringTools.isValidString(xml)) {
                    int beginIndex = xml.indexOf("<b><a href=\"/awards/");
                    if (beginIndex != -1) {
                        for (String item : HTMLTools.extractTags(xml,
                                "<table cellspacing=0 cellpadding=0 border=0 width=100%>",
                                "<br /><br /><br /><br /><br /><br />",
                                "<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" ",
                                "</table>")) {
                            String name = Movie.UNKNOWN;
                            int year = -1;
                            int won = 0;
                            int nominated = 0;
                            Collection<String> wons = new ArrayList<>();
                            Collection<String> nominations = new ArrayList<>();
                            for (String tmp : HTMLTools.extractTags(item,
                                    "<td height=\"40\" class=\"news\" style=\"padding: 10px\">", "</td>",
                                    "<a href=\"/awards/", "</a>")) {
                                int coma = tmp.indexOf(",");
                                name = tmp.substring(0, coma);
                                year = NumberUtils.toInt(tmp.substring(coma + 2, coma + 6));
                                break;
                            }
                            for (String tmp : HTMLTools.extractTags(item, "><", ":", "(",
                                    ")")) {
                                won = NumberUtils.toInt(tmp);
                                break;
                            }
                            if (won > 0) {
                                for (String tmp : HTMLTools.extractTags(item, "><", "</ul>",
                                        "<li class=\"trivia\">", "</li>")) {
                                    wons.add(HTMLTools.removeHtmlTags(tmp).replaceAll(" {2,}", " "));
                                }
                            }
                            for (String tmp : HTMLTools.extractTags(item, ">?<", ":", "(", ")")) {
                                nominated = NumberUtils.toInt(tmp);
                                break;
                            }
                            if (nominated > 0) {
                                for (String tmp : HTMLTools.extractTags(item, ">?<", "</ul>",
                                        "<li class=\"trivia\"", "</li>")) {
                                    nominations.add(HTMLTools.removeHtmlTags(tmp).replaceAll(" {2,}", " "));
                                }
                            }
                            if (StringTools.isValidString(name) && year > 1900 && year < 2020
                                    && (!scrapeWonAwards || (won > 0))) {
                                Award award = new Award();
                                award.setName(name);
                                award.setYear(year);
                                if (won > 0) {
                                    award.setWons(wons);
                                }
                                if (nominated > 0) {
                                    award.setNominations(nominations);
                                }

                                AwardEvent event = new AwardEvent();
                                event.setName(name);
                                event.addAward(award);
                                awards.add(event);
                            }
                        }
                    }
                }
                if (!awards.isEmpty()) {
                    movie.setAwards(awards);
                } else if (etalonFlag) {
                    LOG.error(SITE_DESIGN, "award");
                }
            }

            // Cast enhancement
            boolean overrideCast = OverrideTools.checkOverwriteActors(movie, KINOPOISK_PLUGIN_ID);
            boolean overridePeopleCast = OverrideTools.checkOverwritePeopleActors(movie, KINOPOISK_PLUGIN_ID);
            boolean overrideDirectors = OverrideTools.checkOverwriteDirectors(movie, KINOPOISK_PLUGIN_ID);
            boolean overridePeopleDirectors = OverrideTools.checkOverwritePeopleDirectors(movie,
                    KINOPOISK_PLUGIN_ID);
            boolean overrideWriters = OverrideTools.checkOverwriteWriters(movie, KINOPOISK_PLUGIN_ID);
            boolean overridePeopleWriters = OverrideTools.checkOverwritePeopleWriters(movie, KINOPOISK_PLUGIN_ID);

            if (overrideCast || overridePeopleCast || overrideDirectors || overridePeopleDirectors
                    || overrideWriters || overridePeopleWriters || etalonFlag) {
                xml = httpClient.request(FILM_URL + kinopoiskId + "/cast", CHARSET);
                if (StringTools.isValidString(xml)) {
                    if (overrideDirectors || overridePeopleDirectors || etalonFlag) {
                        int count = scanMoviePerson(movie, xml, "director", directorMax, overrideDirectors,
                                overridePeopleDirectors);
                        if (etalonFlag && count == 0) {
                            LOG.error(SITE_DESIGN, "directors");
                        }
                    }
                    if (overrideWriters || overridePeopleWriters || etalonFlag) {
                        int count = scanMoviePerson(movie, xml, "writer", writerMax, overrideWriters,
                                overridePeopleWriters);
                        if (etalonFlag && count == 0) {
                            LOG.error(SITE_DESIGN, "writers");
                        }
                    }
                    if (overrideCast || overridePeopleCast || etalonFlag) {
                        int count = scanMoviePerson(movie, xml, "actor", actorMax, overrideCast,
                                overridePeopleCast);
                        if (etalonFlag && count == 0) {
                            LOG.error(SITE_DESIGN, "cast");
                        }
                    }

                    Collection<Filmography> outcast = new ArrayList<>();
                    for (Filmography p : movie.getPeople()) {
                        if (StringTools.isNotValidString(p.getId(KINOPOISK_PLUGIN_ID))) {
                            outcast.add(p);
                        }
                    }
                    for (Filmography p : outcast) {
                        movie.removePerson(p);
                    }
                }
            }

            // Business
            if (scrapeBusiness || etalonFlag) {
                xml = httpClient.request(FILM_URL + kinopoiskId + "/box/", CHARSET);
                if (StringTools.isValidString(xml)) {
                    valueFounded = false;
                    for (String tmp : HTMLTools.extractTags(xml, ">:<", "</table>",
                            "<font color=\"#ff6600\"", "</h3>")) {
                        if (StringTools.isValidString(tmp)) {
                            tmp = tmp.replaceAll("\u00A0", ",").replaceAll(",$", "");
                            if (StringTools.isValidString(tmp) && !"--".equals(tmp)) {
                                movie.setBudget(tmp);
                                valueFounded = true;
                                break;
                            }
                        }
                    }
                    if (!valueFounded && etalonFlag) {
                        LOG.error(SITE_DESIGN, "business: summary");
                    }
                    valueFounded = false;
                    for (String tmp : HTMLTools.extractTags(xml, "> -? (?)<",
                            "</table>", "<h3 style=\"font-size: 18px; margin: 0; padding: 0;color:#f60\"",
                            "</h3>")) {
                        if (StringTools.isValidString(tmp)) {
                            tmp = tmp.replaceAll("\u00A0", ",").replaceAll(",$", "");
                            if (StringTools.isValidString(tmp) && !"--".equals(tmp)) {
                                movie.setOpenWeek("USA", tmp);
                                valueFounded = true;
                                break;
                            }
                        }
                    }
                    if (!valueFounded && etalonFlag) {
                        LOG.error(SITE_DESIGN, "business: first weekend");
                    }
                    valueFounded = false;
                    for (String tmp : HTMLTools.extractTags(xml, ">?? ?<", "</table>",
                            "<tr><td colspan=2", "</h3>")) {
                        tmp += "</h3>";
                        String country = HTMLTools.extractTag(tmp, ">", ":");
                        if (StringTools.isValidString(country)) {
                            String money = HTMLTools
                                    .removeHtmlTags("<h3 " + HTMLTools.extractTag(tmp, "<h3 ", "</h3>"))
                                    .replaceAll("\u00A0", ",").replaceAll(",$", "");
                            if (!"--".equals(money)) {
                                movie.setGross(
                                        " ??"
                                                .equals(country)
                                                        ? "Russia"
                                                        : " ?".equals(country) ? "USA"
                                                                : " ?".equals(country)
                                                                        ? "Worldwide"
                                                                        : "  ?".equals(
                                                                                country) ? "Others" : country,
                                        money);
                                valueFounded = true;
                            }
                        }
                    }
                    if (!valueFounded && etalonFlag) {
                        LOG.error(SITE_DESIGN, "business: gross");
                    }
                } else if (etalonFlag) {
                    LOG.error(SITE_DESIGN, "business");
                }
            }

            // Did You Know
            if (scrapeTrivia || etalonFlag) {
                if (clearTrivia) {
                    movie.clearDidYouKnow();
                }
                if (etalonFlag && triviaMax == 0) {
                    triviaMax = 1;
                }
                if (triviaMax != 0) {
                    xml = httpClient.request(FILM_URL + kinopoiskId + "/view_info/ok/#trivia", CHARSET);
                    if (StringTools.isValidString(xml)) {
                        int i = 0;
                        for (String tmp : HTMLTools.extractTags(xml, ">  , ...<", "</ul>",
                                "<li class=\"trivia", "</li>")) {
                            if (i < triviaMax || triviaMax == -1) {
                                movie.addDidYouKnow(HTMLTools.removeHtmlTags(tmp));
                                valueFounded = true;
                                i++;
                            } else {
                                break;
                            }
                        }
                    }
                }
                if (!valueFounded && etalonFlag) {
                    LOG.error(SITE_DESIGN, "trivia");
                }
            }

            // Finally set title
            if (overrideTitle) {
                movie.setTitle(newTitle, KINOPOISK_PLUGIN_ID);
            }
        } catch (IOException | NumberFormatException error) {
            LOG.error("Failed retreiving movie data from Kinopoisk : {}", kinopoiskId);
            LOG.error(SystemTools.getStackTrace(error));
        }
        return true;
    }

    private int scanMoviePerson(Movie movie, String xml, String mode, int personMax, boolean overrideNormal,
            boolean overridePeople) {
        int count = 0;
        boolean clearList = Boolean.TRUE;

        if (personMax > 0 && -1 != xml.indexOf("<a name=\"" + mode + "\">")) {
            for (String item : HTMLTools.extractTags(xml, "<a name=\"" + mode + "\">", "<a name=\"",
                    "<div class=\"dub ", "<div class=\"clear\"></div>   </div>   <div class=\"clear\"></div>")) {
                String name = HTMLTools.extractTag(item, "<div class=\"name\"><a href=\"/name/", "</a>");
                int beginIndex = name.indexOf("/\">");
                String personID = name.substring(0, beginIndex);
                name = name.substring(beginIndex + 3);
                String origName = HTMLTools.extractTag(item, "<span class=\"gray\">", "</span>")
                        .replace('\u00A0', ' ').trim();
                beginIndex = origName.indexOf(" (");
                if (beginIndex > -1) {
                    origName = origName.substring(0, beginIndex);
                }
                if (StringTools.isNotValidString(origName)) {
                    origName = name;
                }
                String role = Movie.UNKNOWN;
                String dubler = Movie.UNKNOWN;
                if ("actor".equals(mode)) {
                    role = HTMLTools.extractTag(item, "<div class=\"role\">", "</div>").replaceAll("^\\.+\\s", "")
                            .replaceAll("\\s.$", "");
                    if (item.contains("<div class=\"dubInfo\">")) {
                        dubler = HTMLTools.extractTag(
                                HTMLTools.extractTag(item, "<div class=\"dubInfo\">", "</span>"),
                                "<div class=\"name\"><a href=\"/name/", "</a>");
                        dubler = dubler.substring(dubler.indexOf("/\">") + 3);
                    }
                }
                count++;

                // NOTE: kinopoisk just enhances people informations, but don't overwrite them
                if (overridePeople) {
                    boolean found = false;
                    for (Filmography p : movie.getPeople()) {
                        if (p.getName().equalsIgnoreCase(origName) && p.getJob().equalsIgnoreCase(mode)) {
                            p.setId(KINOPOISK_PLUGIN_ID, personID);
                            p.setTitle(name);
                            p.setCharacter(role);
                            p.setDoublage(dubler);
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        switch (mode) {
                        case "actor":
                            movie.addActor(KINOPOISK_PLUGIN_ID + ":" + personID, origName + ":" + name, role,
                                    NAME_URL + personID + "/", dubler, KINOPOISK_PLUGIN_ID);
                            break;
                        case "director":
                            movie.addDirector(KINOPOISK_PLUGIN_ID + ":" + personID, origName + ":" + name,
                                    NAME_URL + personID + "/", KINOPOISK_PLUGIN_ID);
                            break;
                        case "writer":
                            movie.addWriter(KINOPOISK_PLUGIN_ID + ":" + personID, origName + ":" + name,
                                    NAME_URL + personID + "/", KINOPOISK_PLUGIN_ID);
                            break;
                        default:
                            break;
                        }
                    }
                }

                if (overrideNormal) {
                    switch (mode) {
                    case "actor":
                        if (clearList) {
                            movie.clearCast();
                            clearList = Boolean.FALSE;
                        }
                        movie.addActor(translateName ? name : origName, KINOPOISK_PLUGIN_ID);
                        break;
                    case "director":
                        if (clearList) {
                            movie.clearDirectors();
                            clearList = Boolean.FALSE;
                        }
                        movie.addDirector(translateName ? name : origName, KINOPOISK_PLUGIN_ID);
                        break;
                    case "writer":
                        if (clearList) {
                            movie.clearWriters();
                            clearList = Boolean.FALSE;
                        }
                        movie.addWriter(translateName ? name : origName, KINOPOISK_PLUGIN_ID);
                        break;
                    default:
                        break;
                    }
                }

                if (count == personMax) {
                    break;
                }
            }
        }
        return count;
    }

    @Override
    public boolean scan(Person person) {
        String kinopoiskId = person.getId(KINOPOISK_PLUGIN_ID);
        if (StringTools.isNotValidString(kinopoiskId)) {
            kinopoiskId = getKinopoiskPersonId(person.getName(), person.getJob());
            person.setId(KINOPOISK_PLUGIN_ID, kinopoiskId);
        }

        boolean retval = super.scan(person);
        if (StringTools.isValidString(kinopoiskId)) {
            retval = updateKinopoiskPersonInfo(person);
        }
        return retval;
    }

private boolean updateKinopoiskPersonInfo(Person person) {
    String kinopoiskId = person.getId(KINOPOISK_PLUGIN_ID);
    boolean returnStatus = false;

    try {
        String xml = httpClient.request(NAME_URL + kinopoiskId + "/view_info/ok/", CHARSET);
        if (StringTools.isValidString(xml)) {
            if (StringTools.isNotValidString(person.getName())) {
                person.setName(HTMLTools.extractTag(xml, "<span style=\"font-size:13px;color:#666\">", "</span>"));
            }
            person.setTitle(HTMLTools.extractTag(xml, "<h1 style=\"padding:0px;margin:0px\" class=\"moviename-big\">", "</h1>"));

            String date = Movie.UNKNOWN;
            if (xml.contains("<td class=\"birth\"")) {
                String bd = HTMLTools.extractTag(xml, "<td class=\"birth\"", "</td>");
                if (StringTools.isValidString(bd) && bd.contains("</a>")) {
                    bd = HTMLTools.removeHtmlTags(bd).replaceAll("^[^>]*>", "");
                    if (bd.indexOf('') > -1) {
                        bd = bd.substring(0, bd.indexOf('')).replace(",", "").trim();
                    }
                    if (StringTools.isValidString(bd)) {
                        date = bd;
                    }
                }
            }

            if (xml.contains("> ?</td><td>")) {
                String dd = HTMLTools.extractTag(xml, "> ?</td><td>", "</td>");
                if (StringTools.isValidString(dd)) {
                    dd = HTMLTools.removeHtmlTags(dd);
                    dd = dd.substring(0, dd.indexOf('')).replace(",", "").trim();
                    if (StringTools.isValidString(dd)) {
                        date += "/" + dd;
                    }
                }
            }
            if (StringTools.isValidString(date)) {
                person.setYear(date);
            }

            if (xml.contains(">? ?</td><td>")) {
                String bp = HTMLTools.extractTag(xml, ">? ?</td><td>", "</td>");
                if (StringTools.isValidString(bp)) {
                    bp = HTMLTools.removeHtmlTags(bp);
                    if (StringTools.isValidString(bp) && !"-".equals(bp)) {
                        person.setBirthPlace(bp);
                    }
                }
            }

            String km = HTMLTools.extractTag(xml, ">? </td><td>", "</td>");
            if (StringTools.isValidString(km)) {
                person.setKnownMovies(Integer.parseInt(km));
            }

            StringBuilder bio = new StringBuilder();
            for (String item : HTMLTools.extractTags(xml, "<ul class=\"trivia\"", "</ul>", "<li class=\"trivia\"", "</li>")) {
                bio.append(HTMLTools.removeHtmlTags(item).replaceAll("\u0097", "-")).append(" ");
            }

            if (StringTools.isValidString(bio.toString())) {
                person.setBiography(StringTools.trimToLength(bio.toString(), biographyLength));
            }

            if (xml.contains("http://st.kinopoisk.ru/images/actor/" + kinopoiskId + ".jpg") && StringTools.isNotValidString(person.getPhotoURL())) {
                person.setPhotoURL("http://st.kinopoisk.ru/images/actor/" + kinopoiskId + ".jpg");
            }

            if (xml.contains("/film/" + kinopoiskId + "/cast/") && StringTools.isNotValidString(person.getBackdropURL())) {
                xml = httpClient.request(FILM_URL + kinopoiskId + "/cast/", CHARSET);
                if (StringTools.isValidString(xml)) {
                    String size = Movie.UNKNOWN;
                    if (xml.contains("/w_size/1600/")) {
                        size = "1600";
                    } else if (xml.contains("/w_size/1280/")) {
                        size = "1280";
                    } else if (xml.contains("/w_size/1024/")) {
                        size = "1024";
                    }
                    if (StringTools.isValidString(size)) {
                        String id = xml.substring(xml.indexOf("/w_size/" + size + "/") - 25, xml.indexOf("/w_size/" + size + "/"));
                        id = id.substring(id.indexOf("/picture/") + 9);
                        xml = httpClient.request("http://www.kinopoisk.ru/picture/" + id + "/w_size/" + size + "/", CHARSET);
                        if (StringTools.isValidString(xml)) {
                            int beginInx = xml.indexOf("http://st.kinopoisk.ru/im/wallpaper");
                            if (beginInx > -1) {
                                person.setBackdropURL(xml.substring(beginInx, xml.indexOf("\"", beginInx)));
                            }
                        }
                    }
                }
            }
        }

        if (!"imdb".equals(preferredRating)) {
            xml = httpClient.request(NAME_URL + kinopoiskId + "/sort/rating/", CHARSET);
            if (StringTools.isValidString(xml)) {
                Map<Float, Filmography> newFilmography = new TreeMap<>();
                for (String block : HTMLTools.extractTags(xml, "<center><div style='position: relative' ></div></center>", "<tr><td><br /><br /><br /><br /><br /><br /></td></tr>", "<tr><td colspan=3 height=4><spacer type=block height=4></td></tr>", "</div>        </td></tr>")) {
                    String job = HTMLTools.extractTag(block, "<div style=\"padding-left: 9px\" id=\"", "\">");
                    if (Filmography.JOB_PRODUCER.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_PRODUCER);
                    } else if (Filmography.JOB_DIRECTOR.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_DIRECTOR);
                    } else if (Filmography.JOB_WRITER.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_WRITER);
                    } else if (Filmography.JOB_ACTOR.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_ACTOR);
                    } else if (Filmography.JOB_EDITOR.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_EDITOR);
                    } else if ("himself".equals(job) || "herself".equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_THEMSELVES);
                    } else if (Filmography.JOB_DESIGN.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_DESIGN);
                    } else if (Filmography.JOB_OPERATOR.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_OPERATOR);
                    } else if (Filmography.JOB_COMPOSER.equals(job)) {
                        StringUtils.capitalize(Filmography.JOB_MUSIC);
                    } else if (job.contains("_titr_")) {
                        StringUtils.capitalize(Filmography.JOB_ACTOR);
                    }

                    if (!jobsInclude.contains(job)) {
                        continue;
                    }

                    for (String item : HTMLTools.extractTags(block + "endmarker", "<tr><td colspan=3 style=\"padding-left:20px\">", "endmarker", "<div class=\"item\">", "</div></div>")) {
                        String id = HTMLTools.extractTag(item, " href=\"/level/1/film/", "/\"");
                        String url = "http://www.kinopoisk.ru/level/1/film/" + id + "/";
                        String title = HTMLTools.extractTag(item, " href=\"/level/1/film/" + id + "/\"", "</a>").replaceAll("[^>]*>", "").replaceAll("\u00A0", " ").trim();
                        if (skipTV && (title.contains(" (?)") || title.contains(" ()"))) {
                            continue;
                        } else if (skipV && title.contains(" ()")) {
                            continue;
                        }
                        String year = Movie.UNKNOWN;
                        if (title.lastIndexOf('(') > -1) {
                            year = HTMLTools.extractTag(title.substring(title.lastIndexOf('(')), "(", ")");
                            title = title.replace("(" + year + ")", "").trim();
                        }
                        String name = HTMLTools.extractTag(item, "<span class=\"role\">", "</span>").replaceAll("\u00A0", " ").trim();
                        String character = Movie.UNKNOWN;
                        if (name.contains("... ")) {
                            String[] names = name.split("\\.\\.\\. ");
                            name = names[0].trim();
                            if ("Actor".equals(job)) {
                                character = names[1];
                            }
                        }
                        if (title.endsWith(", The")) {
                            title = "The " + title.replace(", The", "");
                        }
                        if (StringTools.isNotValidString(name)) {
                            name = title;
                        } else if (name.endsWith(", The")) {
                            name = "The " + name.replace(", The", "");
                        }
                        String ratingStr = HTMLTools.extractTag(item, "<div class=\"rating\"><a href=\"", "</a>").replaceAll("[^>]*>", "");
                        int rating = StringTools.parseRating(ratingStr);

                        float key = 101F - (rating + NumberUtils.toFloat("0." + id));

                        if (newFilmography.get(key) == null) {
                            Filmography film = new Filmography();
                            film.setId(KINOPOISK_PLUGIN_ID, id);
                            film.setName(title);
                            film.setTitle(name);
                            film.setOriginalTitle(name);
                            film.setYear(year);
                            film.setJob(job);
                            film.setCharacter(character);
                            film.setDepartment();
                            film.setRating(Integer.toString(rating));
                            film.setUrl(url);
                            newFilmography.put(key, film);
                        }
                    }
                }

                if (!newFilmography.isEmpty()) {
                    if ((!person.getFilmography().isEmpty()) && ("combine".equals(preferredRating) || "average".equals(preferredRating))) {
                        for (Filmography film : person.getFilmography()) {
                            String name = film.getName().replace("", "").replace("?", "").trim();
                            String title = film.getTitle().replace("", "").replace("?", "").trim();
                            String originalTitle = film.getOriginalTitle().replace("", "").replace("?", "").trim();

                            for (Filmography f : newFilmography.values()) {
                                String name2 = f.getName().replace("", "").replace("?", "").replace(": ? 1 ", ": ? I").replace(": ? 2 ", ": ? II").replace(": ? 3 ", ": ? III").replace(": ? 4 ", ": ? IV").replace(": ? 5 ", ": ? V").replace(": ? 6 ", ": ? VI").replace(": ? 7 ", ": ? VII").replace(": ? 8 ", ": ? VIII").replace(": ? 9 ", ": ? IX").replace(": ? 10 ", ": ? X").trim();
                                String title2 = f.getTitle().replace("", "").replace("?", "").replace(": ? 1 ", ": ? I").replace(": ? 2 ", ": ? II").replace(": ? 3 ", ": ? III").replace(": ? 4 ", ": ? IV").replace(": ? 5 ", ": ? V").replace(": ? 6 ", ": ? VI").replace(": ? 7 ", ": ? VII").replace(": ? 8 ", ": ? VIII").replace(": ? 9 ", ": ? IX").replace(": ? 10 ", ": ? X").trim();
                                String originalTitle2 = f.getOriginalTitle().replace("", "").replace("?", "").replace(": ? 1 ", ": ? I").replace(": ? 2 ", ": ? II").replace(": ? 3 ", ": ? III").replace(": ? 4 ", ": ? IV").replace(": ? 5 ", ": ? V").replace(": ? 6 ", ": ? VI").replace(": ? 7 ", ": ? VII").replace(": ? 8 ", ": ? VIII").replace(": ? 9 ", ": ? IX").replace(": ? 10 ", ": ? X").trim();

                                if (name.equalsIgnoreCase(name2) || name2.equalsIgnoreCase(title) || name2.equalsIgnoreCase(originalTitle)
                                        || title2.equalsIgnoreCase(name2) || title2.equalsIgnoreCase(title) || title2.equalsIgnoreCase(originalTitle)
                                        || originalTitle2.equalsIgnoreCase(name2) || originalTitle2.equalsIgnoreCase(title) || originalTitle2.equalsIgnoreCase(originalTitle)) {
                                    String id = f.getId(KINOPOISK_PLUGIN_ID);
                                    film.setId(KINOPOISK_PLUGIN_ID, id);
                                    film.setName(f.getName());
                                    film.setTitle(f.getTitle());
                                    film.setOriginalTitle(f.getOriginalTitle());
                                    film.setYear(f.getYear());
                                    film.setJob(f.getJob());
                                    film.setCharacter(f.getCharacter());
                                    film.setDepartment();
                                    film.setRating(Integer.toString((int) ("combine".equals(preferredRating)
                                            ? (NumberUtils.toFloat(f.getRating()) * 1000 + NumberUtils.toFloat(film.getRating()))
                                            : ((NumberUtils.toFloat(film.getRating()) + NumberUtils.toFloat(f.getRating())) / 2))));
                                    film.setUrl(f.getUrl());
                                }
                            }
                        }
                    } else if ("kinopoisk".equals(preferredRating) || (person.getFilmography().isEmpty())) {
                        person.setFilmography(new ArrayList<>(newFilmography.values()));
                    }
                }
            }
        }

        returnStatus = true;
    } catch (IOException | NumberFormatException error) {
        LOG.error("Failed retreiving KinopoiskPlugin data for person: {}", kinopoiskId);
        LOG.error(SystemTools.getStackTrace(error));
    }
    return returnStatus;
}

    private String getKinopoiskPersonId(String person, String mode) {
        String personId = Movie.UNKNOWN;
        try {
            String sb = person;
            sb = sb.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
            sb = "&m_act[find]=" + URLEncoder.encode(sb, "UTF-8").replace(" ", "+");

            for (int step = 0; step < 3 && StringTools.isNotValidString(personId); step++) {
                String xml = httpClient
                        .request("http://www.kinopoisk.ru/index.php?level=7&from=forma&result=adv&m_act[from]=forma"
                                + (step < 2 ? ("&m_act[what]=actor") : "") + sb
                                + (step == 0 ? ("&m_act[work]=" + mode) : ""), CHARSET);

                // Checking for zero results
                int beginIndex = xml.indexOf("class=\"search_results\"");
                if (beginIndex > 0) {
                    // Checking if we got the person page directly
                    int beginInx = xml.indexOf("id_actor = ");
                    if (beginInx == -1) {
                        // It's search results page, searching a link to the person page
                        beginIndex = xml.indexOf("/name/", beginIndex);
                        if (beginIndex != -1) {
                            StringTokenizer st = new StringTokenizer(xml.substring(beginIndex + 16), "/");
                            personId = st.nextToken();
                        }
                    } else {
                        // It's the person page
                        StringTokenizer st = new StringTokenizer(xml.substring(beginInx + 11), ";");
                        personId = st.nextToken();
                    }
                }
            }
        } catch (IOException ex) {
            LOG.error("Error : {}", ex.getMessage(), ex);
        }
        return personId;
    }
}