All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.moviejukebox.scanner.MovieNFOScanner Maven / Gradle / Ivy

There is a newer version: 2.9
Show newest version
/*
 *      Copyright (c) 2004-2012 YAMJ Members
 *      http://code.google.com/p/moviejukebox/people/list
 *
 *      Web: http://code.google.com/p/moviejukebox/
 *
 *      This software is licensed under a Creative Commons License
 *      See this page: http://code.google.com/p/moviejukebox/wiki/License
 *
 *      For any reuse or distribution, you must make clear to others the
 *      license terms of this work.
 */
package com.moviejukebox.scanner;

import com.moviejukebox.model.DirtyFlag;
import com.moviejukebox.model.Movie;
import com.moviejukebox.model.MovieFile;
import com.moviejukebox.plugin.DatabasePluginController;
import com.moviejukebox.tools.FileTools;
import com.moviejukebox.tools.GenericFileFilter;
import com.moviejukebox.tools.PropertiesUtil;
import static com.moviejukebox.tools.PropertiesUtil.FALSE;
import static com.moviejukebox.tools.StringTools.appendToPath;
import static com.moviejukebox.tools.StringTools.isValidString;
import com.moviejukebox.writer.MovieNFOReader;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.pojava.datetime.DateTimeConfig;

/**
 * NFO file parser.
 *
 * Search a NFO file for IMDb URL.
 *
 * @author jjulien
 */
public class MovieNFOScanner {

    private static final Logger logger = Logger.getLogger(MovieNFOScanner.class);
    private static final String logMessage = "MovieNFOScanner: ";
    // Types of nodes
    private static final String TYPE_MOVIE = "movie";
    private static final String TYPE_TVSHOW = "tvshow";
    private static final String TYPE_EPISODE = "episodedetails";
    // Other properties
    private static final String xbmcTvNfoName = "tvshow";
    // For now, this is deprecated and we should see if there are issues before looking at a solution as the DOM Parser seems a lot more stable
//    private static String forceNFOEncoding = PropertiesUtil.getProperty("mjb.forceNFOEncoding", "AUTO");
    private static String NFOdirectory = PropertiesUtil.getProperty("filename.nfo.directory", "");
    private static final boolean acceptAllNFO = PropertiesUtil.getBooleanProperty("filename.nfo.acceptAllNfo", FALSE);
    private static String nfoExtRegex;
    private static final String[] NFOExtensions = PropertiesUtil.getProperty("filename.nfo.extensions", "NFO").split(",");
    private static Pattern partPattern = Pattern.compile("(?i)(?:(?:CD)|(?:DISC)|(?:DISK)|(?:PART))([0-9]+)");
    private static boolean archiveScanRar = PropertiesUtil.getBooleanProperty("mjb.scanner.archivescan.rar", FALSE);
    private static boolean skipTvNfoFiles = PropertiesUtil.getBooleanProperty("filename.nfo.skipTVNFOFiles", FALSE);

    static {
        if (acceptAllNFO) {
            logger.info(logMessage + "Accepting all NFO files in the directory");
        }

        // Construct regex for filtering NFO files
        // Target format is: ".*\\(ext1|ext2|ext3|..|extN)"
        {
            boolean first = Boolean.TRUE;
            StringBuilder regexBuilder = new StringBuilder("(?i).*\\.("); // Start of REGEX
            for (String ext : NFOExtensions) {
                if (first) {
                    first = Boolean.FALSE;
                } else {
                    regexBuilder.append("|"); // Add seperator
                }
                regexBuilder.append(ext).append("$"); // Add extension
            }
            regexBuilder.append(")"); // End of REGEX
            nfoExtRegex = regexBuilder.toString();
        }

        // Set the date format to dd-MM-yyyy
        DateTimeConfig.globalEuropeanDateFormat();
    }

    /**
     * Process the NFO file.
     *
     * Will either process the file as an XML NFO file or just pick out the poster and fanart URLs
     *
     * Scanning for site specific URLs should be done by each plugin
     *
     * @param movie
     * @param movieDB
     */
    public static void scan(Movie movie, List nfoFiles) {
        for (File nfoFile : nfoFiles) {
            logger.debug(logMessage + "Scanning NFO file for IDs: " + nfoFile.getName());
            // Set the NFO as dirty so that the information will be re-scanned at the appropriate points.
            movie.setDirty(DirtyFlag.NFO);

            String nfo = FileTools.readFileToString(nfoFile);
            boolean parsedXmlNfo = Boolean.FALSE;   // Was the NFO XML parsed correctly or at all

            if (StringUtils.containsIgnoreCase(nfo, "<" + TYPE_MOVIE)
                    || StringUtils.containsIgnoreCase(nfo, "<" + TYPE_TVSHOW)
                    || StringUtils.containsIgnoreCase(nfo, "<" + TYPE_EPISODE)) {
                parsedXmlNfo = MovieNFOReader.parseNfo(nfoFile, movie);
            }

            // If the XML wasn't found or parsed correctly, then fall back to the old method
            if (parsedXmlNfo) {
                logger.debug(logMessage + "Successfully scanned " + nfoFile.getName() + " as XBMC format");
            } else {
                DatabasePluginController.scanNFO(nfo, movie);

                logger.debug(logMessage + "Scanning NFO for Poster URL");
                int urlStartIndex = 0;
                while (urlStartIndex >= 0 && urlStartIndex < nfo.length()) {
                    int currentUrlStartIndex = nfo.indexOf("http://", urlStartIndex);
                    if (currentUrlStartIndex >= 0) {
                        int currentUrlEndIndex = nfo.indexOf("jpg", currentUrlStartIndex);
                        if (currentUrlEndIndex < 0) {
                            currentUrlEndIndex = nfo.indexOf("JPG", currentUrlStartIndex);
                        }
                        if (currentUrlEndIndex >= 0) {
                            int nextUrlStartIndex = nfo.indexOf("http://", currentUrlStartIndex);
                            // look for shortest http://
                            while ((nextUrlStartIndex != -1) && (nextUrlStartIndex < currentUrlEndIndex + 3)) {
                                currentUrlStartIndex = nextUrlStartIndex;
                                nextUrlStartIndex = nfo.indexOf("http://", currentUrlStartIndex + 1);
                            }

                            // Check to see if the URL has  at the beginning and ignore it if it does (Issue 706)
                            if ((currentUrlStartIndex < 8)
                                    || (new String(nfo.substring(currentUrlStartIndex - 8, currentUrlStartIndex)).compareToIgnoreCase("") != 0)) {
                                String foundUrl = new String(nfo.substring(currentUrlStartIndex, currentUrlEndIndex + 3));

                                // Check for some invalid characters to see if the URL is valid
                                if (foundUrl.contains(" ") || foundUrl.contains("*")) {
                                    urlStartIndex = currentUrlStartIndex + 3;
                                } else {
                                    logger.debug(logMessage + "Poster URL found in nfo = " + foundUrl);
                                    movie.setPosterURL(new String(nfo.substring(currentUrlStartIndex, currentUrlEndIndex + 3)));
                                    urlStartIndex = -1;
                                    movie.setDirty(DirtyFlag.POSTER, Boolean.TRUE);
                                }
                            } else {
                                logger.debug(logMessage + "Poster URL ignored in NFO because it's a fanart URL");
                                // Search for the URL again
                                urlStartIndex = currentUrlStartIndex + 3;
                            }
                        } else {
                            urlStartIndex = currentUrlStartIndex + 3;
                        }
                    } else {
                        urlStartIndex = -1;
                    }
                }
            }
        }
    }

    /**
     * Search for the NFO file in the library structure
     *
     * @param movie The movie bean to locate the NFO for
     * @return A List structure of all the relevant NFO files
     */
    public static List locateNFOs(Movie movie) {
        List nfoFiles = new ArrayList();
        GenericFileFilter fFilter;

        File currentDir = movie.getFirstFile().getFile();

        if (currentDir == null) {
            return nfoFiles;
        }

        String baseFileName = currentDir.getName();
        String pathFileName = currentDir.getAbsolutePath();

        // Get the folder if it's a BluRay disk
        if (pathFileName.toUpperCase().contains(File.separator + "BDMV" + File.separator)) {
            currentDir = new File(FileTools.getParentFolder(currentDir));
            baseFileName = currentDir.getName();
            pathFileName = currentDir.getAbsolutePath();
        }

        if (archiveScanRar && pathFileName.toLowerCase().contains(".rar")) {
            currentDir = new File(FileTools.getParentFolder(currentDir));
            baseFileName = currentDir.getName();
            pathFileName = currentDir.getAbsolutePath();
        }

        // If "pathFileName" is a file then strip the extension from the file.
        if (currentDir.isFile()) {
            pathFileName = FilenameUtils.removeExtension(pathFileName);
            baseFileName = FilenameUtils.removeExtension(baseFileName);
        } else {
            // *** First step is to check for VIDEO_TS
            // The movie is a directory, which indicates that this is a VIDEO_TS file
            // So, we should search for the file moviename.nfo in the sub-directory
            checkNFO(nfoFiles, pathFileName + new String(pathFileName.substring(pathFileName.lastIndexOf(File.separator))));
        }

        // TV Show specific scanning
        if (movie.isTVShow()) {
            // Check for the "tvshow.nfo" filename in the parent directory
            checkNFO(nfoFiles, movie.getFile().getParentFile().getParent() + File.separator + xbmcTvNfoName);

            // Check for individual episode files
            if (!skipTvNfoFiles) {
                String mfFilename;

                for (MovieFile mf : movie.getMovieFiles()) {
                    mfFilename = mf.getFile().getParent().toUpperCase();

                    if (mfFilename.contains("BDMV")) {
                        mfFilename = FileTools.getParentFolder(mf.getFile());
                        mfFilename = new String(mfFilename.substring(mfFilename.lastIndexOf(File.separator) + 1));
                    } else {
                        mfFilename = FilenameUtils.removeExtension(mf.getFile().getName());
                    }

                    checkNFO(nfoFiles, mf.getFile().getParent() + File.separator + mfFilename);
                }
            }
        }

        // *** Second step is to check for the filename.nfo file
        // This file should be named exactly the same as the video file with an extension of "nfo" or "NFO"
        // E.G. C:\Movies\Bladerunner.720p.avi => Bladerunner.720p.nfo
        checkNFO(nfoFiles, pathFileName);

        if (isValidString(NFOdirectory)) {
            // *** Next step if we still haven't found the nfo file is to
            // search the NFO directory as specified in the moviejukebox.properties file
            String sNFOPath = FileTools.getDirPathWithSeparator(movie.getLibraryPath()) + NFOdirectory;
            checkNFO(nfoFiles, sNFOPath + File.separator + baseFileName);
        }

        // *** Next step is to check for a directory wide NFO file.
        if (acceptAllNFO) {
            /*
             * If any NFO file in this directory will do, then we search for all we can find
             *
             * NOTE: for scanning efficiency, it is better to first search for specific filenames before we start doing
             * filtered "listfiles" which scans all the files;
             *
             * A movie collection with all moviefiles in one directory could take tremendously longer if for each
             * moviefile found, the entire directory must be listed!!
             *
             * Therefore, we first check for specific filenames (cfr. old behaviour) before doing an entire scan of the
             * directory -- and only if the user has decided to accept any NFO file!
             */

            // Check the current directory
            fFilter = new GenericFileFilter(nfoExtRegex);
            checkRNFO(nfoFiles, currentDir.getParentFile(), fFilter);

            // Also check the directory above, for the case where movies are in a multi-part named directory (CD/PART/DISK/Etc.)
            Matcher allNfoMatch = partPattern.matcher(currentDir.getAbsolutePath());
            if (allNfoMatch.find()) {
                logger.debug(logMessage + "Found multi-part directory, checking parent directory for NFOs");
                checkRNFO(nfoFiles, currentDir.getParentFile().getParentFile(), fFilter);
            }
        } else {
            // This file should be named the same as the directory that it is in
            // E.G. C:\TV\Chuck\Season 1\Season 1.nfo
            // We search up through all containing directories up to the library root

            if (currentDir != null) {
                // Check the current directory for the video filename
                fFilter = new GenericFileFilter("(?i)" + movie.getBaseFilename() + nfoExtRegex);
                checkRNFO(nfoFiles, currentDir, fFilter);
            }
        }

        // Recurse through the directories to the library root looking for NFO files
        String libraryRootPath = new File(movie.getLibraryPath()).getAbsolutePath();
        while (currentDir != null && !currentDir.getAbsolutePath().equals(libraryRootPath)) {
            //fFilter.setPattern("(?i)" + currentDir.getName() + nfoExtRegex);
            //checkRNFO(nfos, currentDir, fFilter);
            currentDir = currentDir.getParentFile();
            if (currentDir != null) {
                final String path = currentDir.getPath();
                // Path is not empty
                if (!path.isEmpty()) {
                    // Path is not the root
                    if (!path.endsWith(File.separator)) {
                        checkNFO(nfoFiles, appendToPath(path, currentDir.getName()));
                    }
                }
            }
        }

        // we added the most specific ones first, and we want to parse those the last,
        // so nfo files in sub-directories can override values in directories above.
        Collections.reverse(nfoFiles);

        return nfoFiles;
    }

    /**
     * Search the current directory for all NFO files using a file filter
     *
     * @param nfoFiles
     * @param currentDir
     * @param fFilter
     */
    private static void checkRNFO(List nfoFiles, File currentDir, GenericFileFilter fFilter) {
        File[] fFiles = currentDir.listFiles(fFilter);
        if (fFiles != null && fFiles.length > 0) {
            for (File foundFile : fFiles) {
                logger.debug(logMessage + "Found " + foundFile.getName());
                nfoFiles.add(foundFile);
            }
        }
    }

    /**
     * Check to see if the passed filename exists with nfo extensions
     *
     * @param checkNFOfilename (NO EXTENSION)
     * @return blank string if not found, filename if found
     */
    private static void checkNFO(List nfoFiles, String checkNFOfilename) {
        File nfoFile;

        for (String ext : NFOExtensions) {
            nfoFile = FileTools.fileCache.getFile(checkNFOfilename + "." + ext);
            if (nfoFile.exists()) {
                logger.debug(logMessage + "Found " + nfoFile.getAbsolutePath());
                nfoFiles.add(nfoFile);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy