org.madsonic.service.MediaFileService.java Source code

Java tutorial

Introduction

Here is the source code for org.madsonic.service.MediaFileService.java

Source

/*
 This file is part of Subsonic.
    
 Subsonic 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
 (at your option) any later version.
    
 Subsonic 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 Subsonic.  If not, see <http://www.gnu.org/licenses/>.
    
 Copyright 2009 (C) Sindre Mehus
 */
package org.madsonic.service;

import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.madsonic.Logger;
import org.madsonic.dao.AlbumDao;
import org.madsonic.dao.ArtistDao;
import org.madsonic.dao.MediaFileDao;
import org.madsonic.dao.MusicFolderDao;
import org.madsonic.domain.Album;
import org.madsonic.domain.Artist;
import org.madsonic.domain.Genre;
import org.madsonic.domain.Genres;
import org.madsonic.domain.MediaFile;
import org.madsonic.domain.MediaFileComparator;
import org.madsonic.domain.MediaFile.MediaType;
import org.madsonic.domain.MusicFolder;
import org.madsonic.service.metadata.JaudiotaggerParser;
import org.madsonic.service.metadata.MetaData;
import org.madsonic.service.metadata.MetaDataParser;
import org.madsonic.service.metadata.MetaDataParserFactory;
import org.madsonic.util.FileUtil;
import org.madsonic.util.StringUtil;
import org.madsonic.util.UrlFile;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import static org.madsonic.domain.MediaFile.MediaType.*;

/**
 * Provides services for instantiating and caching media files and cover art.
 *
 * @author Sindre Mehus
 */
public class MediaFileService {

    private static final Logger LOG = Logger.getLogger(MediaFileService.class);

    private Ehcache mediaFileMemoryCache;

    private SecurityService securityService;
    private SettingsService settingsService;
    private MediaFileDao mediaFileDao;
    private MusicFolderDao musicFolderDao;
    private AlbumDao albumDao;
    private ArtistDao artistDao;
    private MetaDataParserFactory metaDataParserFactory;
    private boolean memoryCacheEnabled = true;

    /**
     * Returns a media file instance for the given file.  If possible, a cached value is returned.
     *
     * @param file A file on the local file system.
     * @return A media file instance, or null if not found.
     * @throws SecurityException If access is denied to the given file.
     */
    public MediaFile getMediaFile(File file) {
        return getMediaFile(file, settingsService.isFastCacheEnabled());
    }

    /**
     * Returns a media file instance for the given file.  If possible, a cached value is returned.
     *
     * @param file A file on the local file system.
     * @return A media file instance, or null if not found.
     * @throws SecurityException If access is denied to the given file.
     */
    public MediaFile getMediaFile(File file, boolean useFastCache) {

        // Look in fast memory cache first.
        MediaFile result = getFromMemoryCache(file);
        if (result != null) {
            return result;
        }

        if (!securityService.isReadAllowed(file)) {
            throw new SecurityException("Access denied to file " + file);
        }

        // Secondly, look in database.
        result = mediaFileDao.getMediaFile(file.getPath());
        if (result != null) {
            result = checkLastModified(result, useFastCache);
            putInMemoryCache(file, result);
            return result;
        }

        if (!FileUtil.exists(file)) {
            return null;
        }
        // Not found in database, must read from disk.
        result = createMediaFile(file);

        // Put in cache and database.
        putInMemoryCache(file, result);
        mediaFileDao.createOrUpdateMediaFile(result);

        return result;
    }

    public void createOrUpdateMediaFile(MediaFile mediaFile) {
        mediaFileDao.createOrUpdateMediaFile(mediaFile);
    }

    public Artist getArtistforName(String artistName) {
        if (artistName == null) {
            return null;
        }

        Artist a = new Artist();
        a = artistDao.getArtist(artistName);
        if (a == null) {
            a = artistDao.getArtistFolder(artistName);
        }
        return a;
    }

    public Integer getIdForTrack(String Artist, String TrackName) {
        return mediaFileDao.getIdForTrack(Artist, TrackName);
    }

    public Integer getIdsForAlbums(String albumName) {
        return mediaFileDao.getIdsForAlbums(albumName);
    }

    public Integer getIdsForAlbums(String artistName, String albumName) {
        return mediaFileDao.getIdsForAlbums(artistName, albumName);
    }

    public Integer getIDfromArtistname(String artistName) {
        return mediaFileDao.getIDfromArtistname(artistName);
    }

    @Deprecated
    public MediaFile getMediaFile(String artist, String alternate, String title) {
        return mediaFileDao.getMediaFile(artist, alternate, title);
    }

    @Deprecated
    public MediaFile getMediaFileTitle(String title) {
        return mediaFileDao.getMediaFileTitle(title);
    }

    public List<Artist> getAllArtists() {
        return artistDao.getAllArtists();
    }

    ///////////////////////////////////////////////////
    private MediaFile checkLastModified(MediaFile mediaFile, boolean useFastCache) {
        //      if (useFastCache || (mediaFile.getVersion() >= MediaFileDao.VERSION && mediaFile.getChanged().getTime() >= FileUtil.lastModified(mediaFile.getFile()))) {
        if (useFastCache || mediaFile.getChanged().getTime() >= FileUtil.lastModified(mediaFile.getFile())) {
            return mediaFile;
        }
        mediaFile = createMediaFile(mediaFile.getFile());
        mediaFileDao.createOrUpdateMediaFile(mediaFile);
        return mediaFile;
    }
    ///////////////////////////////////////////////////

    /**
     * Returns a media file instance for the given path name. If possible, a cached value is returned.
     *
     * @param pathName A path name for a file on the local file system.
     * @return A media file instance.
     * @throws SecurityException If access is denied to the given file.
     */
    public MediaFile getMediaFile(String pathName) {
        return getMediaFile(new File(pathName));
    }

    // TODO: Optimize with memory caching.
    public MediaFile getMediaFile(int id, int user_group_id) {
        MediaFile mediaFile = mediaFileDao.getMediaFile(id);
        if (mediaFile == null) {
            return null;
        }

        if (securityService.isAccessAllowed(mediaFile.getFile(), user_group_id) == false) {
            return null;
        } else {
            if (!securityService.isReadAllowed(mediaFile.getFile(), user_group_id)) {
                throw new SecurityException("Access denied to file " + mediaFile);
            }
        }

        return checkLastModified(mediaFile, settingsService.isFastCacheEnabled());
    }

    public MediaFile getMediaFile(int id) {
        MediaFile mediaFile = mediaFileDao.getMediaFile(id);
        if (mediaFile == null) {
            return null;
        }

        if (!mediaFile.getPath().startsWith("http")) {
            if (!securityService.isReadAllowed(mediaFile.getFile())) {
                throw new SecurityException("Access denied to file " + mediaFile);
            }
        }
        return checkLastModified(mediaFile, settingsService.isFastCacheEnabled());
    }

    public MediaFile getParentOf(MediaFile mediaFile) {
        if (mediaFile.getParentPath() == null) {
            return null;
        }
        return getMediaFile(mediaFile.getParentPath());
    }

    public List<MediaFile> getChildrenOf(String parentPath, boolean includeFiles, boolean includeDirectories,
            boolean sort) {
        return getChildrenOf(new File(parentPath), includeFiles, includeDirectories, sort);
    }

    public List<MediaFile> getChildrenOf(List<MediaFile> children, boolean includeFiles, boolean includeDirectories,
            boolean sort) {

        List<MediaFile> result = new ArrayList<MediaFile>();
        for (MediaFile child : children) {

            if (child.isDirectory() && includeDirectories) {
                result.add(child);
            }
            if (child.isFile() && includeFiles) {
                result.add(child);
            }
        }
        return result;

    }

    public List<MediaFile> getChildrenOf(File parent, boolean includeFiles, boolean includeDirectories,
            boolean sort) {
        return getChildrenOf(getMediaFile(parent), includeFiles, includeDirectories, sort);
    }

    /**
     * Returns all media files that are children of a given media file.
     *
     * @param includeFiles       Whether files should be included in the result.
     * @param includeDirectories Whether directories should be included in the result.
     * @param sort               Whether to sort files in the same directory.
     * @return All children media files.
     */
    public List<MediaFile> getChildrenOf(MediaFile parent, boolean includeFiles, boolean includeDirectories,
            boolean sort) {
        return getChildrenOf(parent, includeFiles, includeDirectories, sort, settingsService.isFastCacheEnabled());
    }

    /**
     * Returns all media files that are children of a given media file.
     *
     * @param includeFiles       Whether files should be included in the result.
     * @param includeDirectories Whether directories should be included in the result.
     * @param sort               Whether to sort files in the same directory.
     * @return All children media files.
     */
    public List<MediaFile> getChildrenOf(MediaFile parent, boolean includeFiles, boolean includeDirectories,
            boolean sort, boolean useFastCache) {

        if (!parent.isDirectory()) {
            return Collections.emptyList();
        }

        // Make sure children are stored and up-to-date in the database.
        if (!useFastCache) {
            updateChildren(parent);
        }

        List<MediaFile> result = new ArrayList<MediaFile>();
        for (MediaFile child : mediaFileDao.getChildrenOf(parent.getPath())) {
            child = checkLastModified(child, useFastCache);
            if (child.isDirectory() && includeDirectories) {
                settingsService.getNewaddedTimespan();

                if (isNewAdded(child, settingsService.getNewaddedTimespan())) {
                    child.setNewAdded(true);
                }

                result.add(child);
            }
            if (child.isFile() && includeFiles) {
                result.add(child);
            }
        }

        if (sort) {
            Comparator<MediaFile> comparator = new MediaFileComparator(settingsService.isSortAlbumsByFolder(),
                    settingsService.isSortFilesByFilename());
            // Note: Intentionally not using Collections.sort() since it can be problematic on Java 7.
            // http://www.oracle.com/technetwork/java/javase/compatibility-417013.html#jdk7
            Set<MediaFile> set = new TreeSet<MediaFile>(comparator);
            set.addAll(result);
            result = new ArrayList<MediaFile>(set);
        }

        return result;
    }

    public List<MediaFile> getChildrenSorted(List<MediaFile> result, boolean sort) {

        if (sort) {
            Comparator<MediaFile> comparator = new MediaFileComparator(settingsService.isSortAlbumsByFolder(),
                    settingsService.isSortFilesByFilename());
            Set<MediaFile> set = new TreeSet<MediaFile>(comparator);
            set.addAll(result);
            result = new ArrayList<MediaFile>(set);
        }

        return result;
    }

    public List<MediaFile> getTopTracks(String artist, int userGroupId) {
        return mediaFileDao.getTopTracks(artist, userGroupId);
    }

    /**
     * Returns whether the given file is the root of a media folder.
     *
     * @see MusicFolder
     */
    public boolean isRoot(MediaFile mediaFile) {
        for (MusicFolder musicFolder : settingsService.getAllMusicFolders(false, true)) {
            if (mediaFile.getPath().equals(musicFolder.getPath().getPath())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns all genres in the music collection.
     *
     * @return Sorted list of genres.
     */
    public List<String> getGenres(int user_group_id) {
        return mediaFileDao.getGenres(user_group_id);
    }

    public List<Genre> getGenres(boolean sortByAlbum) {
        return mediaFileDao.getGenres(sortByAlbum);
    }

    public List<String> getGenres() {
        return mediaFileDao.getGenre();
    }

    public List<Genre> getGenreSongList() {
        return mediaFileDao.getGenreSongList();
    }

    public List<Genre> getGenreArtistList() {
        return mediaFileDao.getGenreArtistList();
    }

    public List<Genre> getGenreAlbumList() {
        return mediaFileDao.getGenreAlbumList();
    }

    public List<String> getArtistGenres() {
        return mediaFileDao.getArtistGenres();
    }

    public int getGenreSongCount() {
        return mediaFileDao.getGenreSongCount();
    }

    public int getGenreArtistCount() {
        return mediaFileDao.getGenreArtistCount();
    }

    public int getGenreAlbumCount() {
        return mediaFileDao.getGenreAlbumCount();
    }

    public List<String> getArtistGenresforFolder(List<MusicFolder> musicFoldersToUse, int userGroupId) {

        List<String> tempList = new ArrayList<String>();
        for (MusicFolder mf : musicFoldersToUse) {
            tempList.addAll(mediaFileDao.getArtistGenresforFolder(mf.getId(), userGroupId));
        }

        // capitalize and filter out duplicates
        HashSet<String> hs = new HashSet<String>();

        for (String genre : tempList) {
            genre = WordUtils.capitalize(genre);
            hs.add(genre);
        }
        tempList.clear();
        tempList.addAll(hs);
        Collections.sort(tempList);

        return tempList;
    }

    //    public List<String> getLowerGenres() {
    //        return mediaFileDao.getLowerGenres();
    //    }    

    public List<String> getMoods() {
        return mediaFileDao.getMoods();
    }

    public List<String> getLowerMoods() {
        return mediaFileDao.getLowerMoods();
    }

    /**
     * Returns the most frequently played albums.
     *
     * @param offset Number of albums to skip.
     * @param count  Maximum number of albums to return.
     * @return The most frequently played albums.
     */
    public List<MediaFile> getMostFrequentlyPlayedAlbums(int offset, int count, int user_group_id) {
        return mediaFileDao.getMostFrequentlyPlayedAlbums(offset, count, user_group_id);
    }

    /**
     * Returns the most recently played albums.
     *
     * @param offset Number of albums to skip.
     * @param count  Maximum number of albums to return.
     * @return The most recently played albums.
     */
    public List<MediaFile> getMostRecentlyPlayedAlbums(int offset, int count, int user_group_id) {
        return mediaFileDao.getMostRecentlyPlayedAlbums(offset, count, user_group_id);
    }

    /**
     * Returns the most recently added albums.
     *
     * @param offset Number of albums to skip.
     * @param count  Maximum number of albums to return.
     * @return The most recently added albums.
     */
    public List<MediaFile> getNewestAlbums(int offset, int count, int user_group_id) {
        return mediaFileDao.getNewestAlbums(offset, count, user_group_id);
    }

    public List<MediaFile> getNewestAlbums(MusicFolder musicFolder, int offset, int count, int user_group_id) {
        return mediaFileDao.getNewestAlbums(musicFolder, offset, count, user_group_id);
    }

    /**
     * Returns the most recently starred albums.
     *
     * @param offset   Number of albums to skip.
     * @param count    Maximum number of albums to return.
     * @param username Returns albums starred by this user.
     * @return The most recently starred albums for this user.
     */
    public List<MediaFile> getStarredAlbums(int offset, int count, String username) {
        return mediaFileDao.getStarredAlbums(offset, count, username);
    }

    public List<MediaFile> getArtists(int offset, int count, String username, int user_group_id) {
        return mediaFileDao.getArtists(offset, count, username, user_group_id);
    }

    public List<MediaFile> getStarredArtists(int offset, int count, String username) {
        return mediaFileDao.getStarredArtists(offset, count, username);
    }

    /**
     * Returns albums in alphabetial order.
     *
     *
     * @param offset Number of albums to skip.
     * @param count  Maximum number of albums to return.
     * @param byArtist Whether to sort by artist name
     * @return Albums in alphabetical order.
     */
    public List<MediaFile> getAlphabetialAlbums(int offset, int count, boolean byArtist, int user_group_id) {
        return mediaFileDao.getAlphabetialAlbums(offset, count, byArtist, user_group_id);
    }

    /**
     * Returns albums within a year range.
     *
     * @param offset Number of albums to skip.
     * @param count  Maximum number of albums to return.
     * @param fromYear The first year in the range.
     * @param toYear The last year in the range.
     * @return Albums in the year range.
     */
    public List<MediaFile> getAlbumsByYear(int offset, int count, int fromYear, int toYear, int user_group_id) {
        return mediaFileDao.getAlbumsByYear(offset, count, fromYear, toYear, user_group_id);
    }

    /**
     * Returns albums in a genre.
     *
     * @param offset Number of albums to skip.
     * @param count  Maximum number of albums to return.
     * @param genre The genre name.
     * @return Albums in the genre.
     */
    public List<MediaFile> getAlbumsByGenre(int offset, int count, String genre, int user_group_id) {
        return mediaFileDao.getAlbumsByGenre(offset, count, genre, user_group_id);
    }

    public List<MediaFile> getArtistsByGenre(int offset, int count, String genre, int user_group_id) {
        return mediaFileDao.getArtistsByGenre(genre, offset, count, user_group_id);
    }

    public Date getMediaFileStarredDate(int id, String username) {
        return mediaFileDao.getMediaFileStarredDate(id, username);
    }

    public void populateStarredDate(List<MediaFile> mediaFiles, String username) {
        for (MediaFile mediaFile : mediaFiles) {
            populateStarredDate(mediaFile, username);
        }
    }

    public void populateStarredDate(MediaFile mediaFile, String username) {
        Date starredDate = mediaFileDao.getMediaFileStarredDate(mediaFile.getId(), username);
        mediaFile.setStarredDate(starredDate);
    }

    private void updateChildren(MediaFile parent) {

        // Check timestamps.
        if (parent.getChildrenLastUpdated().getTime() >= parent.getChanged().getTime()) {
            return;
        }

        List<MediaFile> storedChildren = mediaFileDao.getChildrenOf(parent.getPath());
        Map<String, MediaFile> storedChildrenMap = new HashMap<String, MediaFile>();
        for (MediaFile child : storedChildren) {
            storedChildrenMap.put(child.getPath(), child);
        }

        List<File> children = filterMediaFiles(FileUtil.listFiles(parent.getFile()));
        for (File child : children) {
            if (storedChildrenMap.remove(child.getPath()) == null) {
                // Add children that are not already stored.
                mediaFileDao.createOrUpdateMediaFile(createMediaFile(child));
            }
        }

        // Delete children that no longer exist on disk.
        for (String path : storedChildrenMap.keySet()) {
            mediaFileDao.deleteMediaFile(path);
        }

        // Update timestamp in parent.
        parent.setChildrenLastUpdated(parent.getChanged());
        parent.setPresent(true);
        mediaFileDao.createOrUpdateMediaFile(parent);
    }

    public List<File> filterMediaFiles(File[] candidates) {
        List<File> result = new ArrayList<File>();
        for (File candidate : candidates) {
            String suffix = FilenameUtils.getExtension(candidate.getName()).toLowerCase();
            try {
                if (!isExcluded(candidate)
                        && (FileUtil.isDirectory(candidate) || isAudioFile(suffix) || isVideoFile(suffix))) {
                    result.add(candidate);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
            }
        }
        return result;
    }

    private boolean isAudioFile(String suffix) {
        for (String s : settingsService.getMusicFileTypesAsArray()) {
            if (suffix.equals(s.toLowerCase())) {
                return true;
            }
        }
        return false;
    }

    private boolean isVideoFile(String suffix) {
        for (String s : settingsService.getVideoFileTypesAsArray()) {
            if (suffix.equals(s.toLowerCase())) {
                return true;
            }
        }
        return false;
    }

    private Set<String> excludes;

    /**
     * Returns whether the given file is excluded, i.e., whether it is listed in 'madsonic_exclude.txt' in
     * the current directory.
     *
     * @param file The child file in question.
     * @return Whether the child file is excluded.
     */
    private boolean isExcluded(File file) throws IOException {

        if (file.getName().startsWith(".") || file.getName().startsWith("@eaDir")
                || file.getName().toLowerCase().equals("thumbs.db")
                || file.getName().toLowerCase().equals("extrafanart")) {
            return true;
        }

        File excludeFile = new File(file.getParentFile().getPath(), "madsonic_exclude.txt");
        if (excludeFile.exists()) {
            excludes = new HashSet<String>();

            LOG.debug("## excludeFile found: " + excludeFile);
            String[] lines = StringUtil.readLines(new FileInputStream(excludeFile));
            for (String line : lines) {
                excludes.add(line.toLowerCase());
            }
            return excludes.contains(file.getName().toLowerCase());
        }

        return false;
    }

    private MediaFile createMediaFile(File file) {

        MediaFile existingFile = mediaFileDao.getMediaFile(file.getPath());
        MediaFile mediaFile = new MediaFile();
        Date lastModified = new Date(FileUtil.lastModified(file));
        mediaFile.setPath(file.getPath());
        mediaFile.setFolder(securityService.getRootFolderForFile(file));
        mediaFile.setParentPath(file.getParent());
        mediaFile.setChanged(lastModified);
        mediaFile.setLastScanned(new Date());
        mediaFile.setPlayCount(existingFile == null ? 0 : existingFile.getPlayCount());
        mediaFile.setLastPlayed(existingFile == null ? null : existingFile.getLastPlayed());
        mediaFile.setComment(existingFile == null ? null : existingFile.getComment());
        mediaFile.setChildrenLastUpdated(new Date(0));
        mediaFile.setCreated(lastModified);

        //      mediaFile.setMediaType(ALBUM);
        mediaFile.setMediaType(DIRECTORY);

        // ######### Read Comment.txt file ####
        String comment = null;
        comment = checkForCommentFile(file);
        if (checkForCommentFile(file) != null) {
            mediaFile.setComment(comment);
        }

        Boolean folderParsing = settingsService.isFolderParsingEnabled();
        Boolean albumSetParsing = settingsService.isAlbumSetParsingEnabled();

        // #### Scan Foldernamer for Year #####
        if (StringUtil.truncatedYear(file.getName()) != null && StringUtil.truncatedYear(file.getName()) > 10) {
            mediaFile.setYear(StringUtil.truncatedYear(file.getName()));
        }

        //TODO:SCAN ALBUMS
        try {
            if (isRoot(mediaFile)) {
                mediaFile.setMediaType(DIRECTORY);
            }
        } catch (Exception x) {
            LOG.error("Failed to get parent", x);
        }

        mediaFile.setPresent(true);

        //  ################# Look for cover art. ##################
        try {
            FilenameFilter PictureFilter = new FilenameFilter() {
                public boolean accept(File dir, String name) {
                    String lowercaseName = name.toLowerCase();
                    if (lowercaseName.endsWith("png")) {
                        return true;
                    } else if (lowercaseName.endsWith("jpg")) {
                        return true;
                    } else if (lowercaseName.endsWith("jpeg")) {
                        return true;
                    } else if (lowercaseName.endsWith("gif")) {
                        return true;
                    } else if (lowercaseName.endsWith("bmp")) {
                        return true;
                    } else {
                        return false;
                    }
                }
            };
            File[] Albumchildren = FileUtil.listFiles(file, PictureFilter, true);
            File coverArt = findCoverArt(Albumchildren);
            if (coverArt != null) {
                mediaFile.setCoverArtPath(coverArt.getPath());
            }
        } catch (IOException x) {
            LOG.error("Failed to find cover art for DIRECTORY ", x);
        }

        //  ################# MEDIA_TYPE MUSIC #####################
        if (file.isFile()) {

            MetaDataParser parser = metaDataParserFactory.getParser(file);
            if (parser != null) {
                MetaData metaData = parser.getMetaData(file);
                mediaFile.setArtist(metaData.getArtist());
                mediaFile.setAlbumArtist(metaData.getAlbumArtist());
                mediaFile.setAlbumName(metaData.getAlbumName());
                mediaFile.setTitle(metaData.getTitle());
                mediaFile.setDiscNumber(metaData.getDiscNumber());
                mediaFile.setTrackNumber(metaData.getTrackNumber());
                mediaFile.setGenre(metaData.getGenre());
                mediaFile.setMood(metaData.getMood());
                mediaFile.setYear(metaData.getYear());
                mediaFile.setDurationSeconds(metaData.getDurationSeconds());
                mediaFile.setBitRate(metaData.getBitRate());
                mediaFile.setVariableBitRate(metaData.getVariableBitRate());
                mediaFile.setHeight(metaData.getHeight());
                mediaFile.setWidth(metaData.getWidth());
            }
            String format = StringUtils
                    .trimToNull(StringUtils.lowerCase(FilenameUtils.getExtension(mediaFile.getPath())));
            mediaFile.setFormat(format);
            mediaFile.setFileSize(FileUtil.length(file));
            mediaFile.setMediaType(getMediaType(mediaFile));

            // #### Scan for YouTube url #####
            if (file.getPath().toLowerCase().endsWith("url")) {
                mediaFile.setMediaType(URL);

                UrlFile urlFile = null;
                String YoutubeId = null;

                try {
                    urlFile = new UrlFile(file.getPath());
                    YoutubeId = StringUtil.getYoutubeVideoId(urlFile.getString("InternetShortcut", "URL", null));

                } catch (IOException e) {
                }
                mediaFile.setData(YoutubeId);
                mediaFile.setDurationSeconds(0);
            }

        } else {
            // ############## Is this an album? ########################
            if (!isRoot(mediaFile)) {
                File[] children = FileUtil.listFiles(file);
                File firstChild = null;
                for (File child : filterMediaFiles(children)) {
                    if (FileUtil.isFile(child)) {
                        firstChild = child;
                        break;
                    }
                }

                if (firstChild != null) {
                    mediaFile.setMediaType(ALBUM);

                    Integer FOLDER_TYPE = musicFolderDao.getMusicFolder(mediaFile.getFolder()).getType();
                    switch (FOLDER_TYPE) {
                    case 5:
                        mediaFile.setMediaType(VIDEOSET);
                        break;
                    case 6:
                        mediaFile.setMediaType(VIDEOSET);
                        break;
                    default:
                        break;
                    }

                    // ######### Read Comment.txt file ####
                    comment = checkForCommentFile(file);
                    if (checkForCommentFile(file) != null && mediaFile.getComment() == null) {
                        mediaFile.setComment(comment);
                    }

                    // ######### Guess artist/album name and year. #######
                    MetaDataParser parser = metaDataParserFactory.getParser(firstChild);
                    if (parser != null) {
                        MetaData metaData = parser.getMetaData(firstChild);

                        //                      mediaFile.setArtist(metaData.getArtist());
                        mediaFile.setArtist(StringUtils.isBlank(metaData.getAlbumArtist()) ? metaData.getArtist()
                                : metaData.getAlbumArtist());

                        // ########## SET Genre & Yeas for album #########
                        mediaFile.setGenre(metaData.getGenre());
                        if (metaData.getYear() != null) {
                            mediaFile.setYear(metaData.getYear());
                            mediaFile.setYear(metaData.getYear());
                        }

                        // ########## BETTER Albumset Detection #########   

                        String _albumName;
                        String _albumSetName;

                        if (albumSetParsing) {

                            String AlbumSetName = parser.guessAlbum(firstChild, mediaFile.getArtist());
                            if (folderParsing) {
                                AlbumSetName = StringUtil.truncateYear(AlbumSetName);
                            }

                            String parentAlbumSetName = parser.guessArtist(firstChild);

                            //         mediaFile.setArtist(parser.guessArtist(mediaFile.getFile()));
                            String searchAlbum = searchforAlbumSetName(AlbumSetName, AlbumSetName);

                            if (AlbumSetName == searchAlbum) {

                                _albumName = metaData.getAlbumName();
                                _albumSetName = AlbumSetName;

                                if (folderParsing) {
                                    _albumName = StringUtil.truncateYear(_albumName);
                                    _albumSetName = StringUtil.truncateYear(_albumSetName);
                                }

                                mediaFile.setAlbumName(_albumName);
                                mediaFile.setAlbumSetName(_albumSetName);
                            } else {

                                _albumName = metaData.getAlbumName();
                                _albumSetName = metaData.getAlbumName();

                                if (folderParsing) {
                                    _albumName = StringUtil.truncateYear(_albumName);
                                    _albumSetName = searchforAlbumSetName(StringUtil.truncateYear(AlbumSetName),
                                            StringUtil.truncateYear(parentAlbumSetName));
                                }

                                else {
                                    _albumSetName = searchforAlbumSetName(AlbumSetName, parentAlbumSetName);
                                }
                            }

                        } else {

                            _albumName = metaData.getAlbumName();
                            _albumSetName = metaData.getAlbumName();
                        }

                        mediaFile.setAlbumName(_albumName);
                        mediaFile.setAlbumSetName(_albumSetName);
                        LOG.debug("## MediaType ALBUMSET: " + _albumSetName);
                    }

                    // ####### Look for cover art. ########
                    try {
                        File coverArt = findCoverArt(children);
                        if (coverArt != null) {
                            mediaFile.setCoverArtPath(coverArt.getPath());
                        }
                    } catch (IOException x) {
                        LOG.error("Failed to find cover art.", x);
                    }

                } else {

                    // ##### Look for child type  #######
                    File firstAudioChild = null;
                    File firstFolderChild = null;

                    for (File child : filterMediaFiles(children)) {

                        if (FileUtil.isDirectory(child)) {
                            firstFolderChild = child;
                        }
                        if (FileUtil.isFile(child)) {
                            firstAudioChild = child;
                            break;
                        }
                    }

                    if (firstFolderChild != null) {
                        File[] firstAlbumChild = FileUtil.listFiles(firstFolderChild);
                        for (File child : filterMediaFiles(firstAlbumChild)) {
                            if (FileUtil.isFile(child)) {
                                MetaDataParser ChildParser = metaDataParserFactory.getParser(child);
                                if (ChildParser != null) {
                                    MetaData metaDataChild = ChildParser.getMetaData(child);
                                    mediaFile.setGenre(metaDataChild.getGenre());
                                    if (metaDataChild.getYear() != null) {
                                        mediaFile.setYear(metaDataChild.getYear());
                                    }
                                }
                                break;
                            } else if (FileUtil.isDirectory(child)) {
                                for (File subchild : filterMediaFiles(FileUtil.listFiles(child))) {
                                    if (FileUtil.isFile(subchild)) {
                                        MetaDataParser ChildParser = metaDataParserFactory.getParser(subchild);
                                        if (ChildParser != null) {
                                            MetaData metaDataChild = ChildParser.getMetaData(subchild);

                                            if (metaDataChild.getGenre() != null)

                                            {
                                                if (mediaFile.getGenre() != metaDataChild.getGenre())
                                                    mediaFile.setGenre(metaDataChild.getGenre());
                                            }

                                            if (metaDataChild.getYear() != null) {
                                                mediaFile.setYear(metaDataChild.getYear());
                                            }
                                        }
                                        break;
                                    }

                                }
                            }

                        }
                    }
                    if (firstAudioChild != null) {
                        MetaDataParser ChildParser = metaDataParserFactory.getParser(firstAudioChild);
                        if (ChildParser != null) {
                            MetaData metaDataChild = ChildParser.getMetaData(firstAudioChild);
                            mediaFile.setGenre(metaDataChild.getGenre());
                            if (metaDataChild.getYear() != null) {
                                mediaFile.setYear(metaDataChild.getYear());
                            }
                        }
                    }

                    // ######## ALBUMSET ###############
                    if (firstAudioChild == null) {
                        mediaFile.setMediaType(ALBUMSET);

                        Integer FOLDER_TYPE = musicFolderDao.getMusicFolder(mediaFile.getFolder()).getType();
                        switch (FOLDER_TYPE) {
                        case 5:
                            mediaFile.setMediaType(VIDEOSET);
                            break;
                        case 6:
                            mediaFile.setMediaType(VIDEOSET);
                            break;
                        default:
                            break;
                        }

                        String _artist = new File(file.getParent()).getName();
                        String _albumName = file.getName();
                        String _albumSetName = file.getName();

                        if (folderParsing) {
                            _artist = StringUtil.truncateYear(_artist);
                            _albumName = StringUtil.truncateYear(_albumName);
                            _albumSetName = StringUtil.truncateYear(_albumSetName);
                        }

                        mediaFile.setArtist(_artist);
                        mediaFile.setAlbumName(_albumName);
                        mediaFile.setAlbumSetName(_albumSetName);
                        //              LOG.info("## MediaType ALBUMSET: " + _albumSetName));
                    } else {
                        String _artist = file.getName();
                        if (folderParsing) {
                            _artist = StringUtil.truncateYear(_artist);
                        }
                        mediaFile.setArtist(_artist);
                    }

                    if (mediaFile.getArtist() != null) {
                        if (mediaFile.getParentPath().contains(mediaFile.getArtist())) {
                            mediaFile.setMediaType(ALBUMSET);
                            String _artist = new File(file.getParent()).getName();
                            if (folderParsing) {
                                _artist = StringUtil.truncateYear(_artist);
                            }
                            mediaFile.setArtist(_artist);
                            //                  LOG.info("## MediaType ALBUMSET: " + _artist );
                        }

                        // ######## ARTIST ###############
                        if (mediaFile.getCoverArtPath() == null) {
                            mediaFile.setMediaType(ARTIST);
                            String _artist = file.getName();
                            if (folderParsing) {
                                _artist = StringUtil.truncateYear(_artist);
                            }
                            mediaFile.setArtist(_artist);
                            LOG.debug("## MediaType ARTIST: " + _artist);
                        }
                    }

                    // ######## ARTIST ###############
                    if (mediaFile.getCoverArtPath() != null) {
                        String lowercaseName = (mediaFile.getCoverArtPath().toLowerCase());
                        if (lowercaseName.contains("artist.")) {
                            mediaFile.setMediaType(ARTIST);
                            String _artist = file.getName();
                            if (folderParsing) {
                                _artist = StringUtil.truncateYear(_artist);
                            }
                            mediaFile.setArtist(_artist);
                            LOG.debug("## MediaType ARTIST: " + _artist);
                        }
                    }

                    // ######## VIDEOSET ###############
                    if (mediaFile.getCoverArtPath() != null) {
                        String lowercaseName = (mediaFile.getCoverArtPath().toLowerCase());
                        if (lowercaseName.contains("video.") || lowercaseName.contains("sesson")) {
                            mediaFile.setMediaType(VIDEOSET);
                            LOG.debug("## MediaType VIDEOSET: " + file.getName());
                        }
                    }

                    // ################## Look for Artist flag ###############
                    setMediaTypeFlag(mediaFile, file, "MULTI.TAG", MULTIARTIST);
                    setMediaTypeFlag(mediaFile, file, "ARTIST.TAG", ARTIST);
                    setMediaTypeFlag(mediaFile, file, "DIR.TAG", DIRECTORY);
                    setMediaTypeFlag(mediaFile, file, "SET.TAG", ALBUMSET);
                    setMediaTypeFlag(mediaFile, file, "ALBUM.TAG", ALBUM);
                    setMediaTypeFlag(mediaFile, file, "VIDEO.TAG", VIDEOSET);
                }
            }
        }
        return mediaFile;
    }

    private boolean isNewAdded(MediaFile mediafile, String TimeSpan) {

        try {

            Date LastSystemScan = null;
            Integer timespan = 0;

            if (TimeSpan.contains("OneHour")) {
                timespan = 1;
            }
            ;
            if (TimeSpan.contains("OneDay")) {
                timespan = 2;
            }
            ;
            if (TimeSpan.contains("OneWeek")) {
                timespan = 3;
            }
            ;
            if (TimeSpan.contains("OneMonth")) {
                timespan = 4;
            }
            ;
            if (TimeSpan.contains("TwoMonth")) {
                timespan = 5;
            }
            ;
            if (TimeSpan.contains("ThreeMonth")) {
                timespan = 6;
            }
            ;
            if (TimeSpan.contains("SixMonth")) {
                timespan = 7;
            }
            ;
            if (TimeSpan.contains("OneYear")) {
                timespan = 8;
            }
            ;

            switch (timespan) {
            case 1:
                LastSystemScan = new Date(System.currentTimeMillis() - 3600 * 1000);
                break;
            case 2:
                LastSystemScan = new Date(System.currentTimeMillis() - 24 * 3600 * 1000);
                break;
            case 3:
                LastSystemScan = new Date(System.currentTimeMillis() - 7L * 24 * 3600 * 1000);
                break;
            case 4:
                LastSystemScan = new Date(System.currentTimeMillis() - 30L * 24 * 3600 * 1000);
                break;
            case 5:
                LastSystemScan = new Date(System.currentTimeMillis() - 60L * 24 * 3600 * 1000);
                break;
            case 6:
                LastSystemScan = new Date(System.currentTimeMillis() - 90L * 24 * 3600 * 1000);
                break;
            case 7:
                LastSystemScan = new Date(System.currentTimeMillis() - 182L * 24 * 3600 * 1000);
                break;
            case 8:
                LastSystemScan = new Date(System.currentTimeMillis() - 365L * 24 * 3600 * 1000);
                break;
            default:

            }

            //      Date LastSystemScan = ThreeMonth;
            Date LastMediaScan = mediafile.getCreated();

            Calendar calLastSystemScan = Calendar.getInstance();
            Calendar calLastMediaScan = Calendar.getInstance();

            calLastMediaScan.setTime(LastMediaScan);
            calLastSystemScan.setTime(LastSystemScan);

            if (calLastSystemScan.before(calLastMediaScan)) {
                return true;
            }
            return false;

            //      System.out.println("Current date(" + new SimpleDateFormat("yyyy.MM.dd hh:mm").format(calLastSystemScan.getTime()) + ")");
            //      System.out.println("Scandat date(" + new SimpleDateFormat("yyyy.MM.dd hh:mm").format(calLastMediaScan.getTime()) + ")");
            //      
            //       long milliseconds1 = calLastSystemScan.getTimeInMillis();
            //      long milliseconds2 = calLastMediaScan.getTimeInMillis();
            //      long diff = milliseconds2 - milliseconds1;
            //      
            //      long diffDays = diff / (24 * 60 * 60 * 1000);
            //      long diffHours = diff / (60 * 60 * 1000);
            //      
            //      System.out.println("Time in days: " + diffDays  + " days.");
            //      System.out.println("Time in hours: " + diffHours  + " hours.");

        } catch (Exception x) {
            LOG.error("Failed to get TimeSpan.", x);
        }
        return false;

    }

    /**
     * Set MediaType if File TAG Flag is found
     */
    private boolean setMediaTypeFlag(MediaFile mediaFile, File file, final String flagname, MediaType mediaType) {

        FilenameFilter FlagFilter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                String lowercaseName = name.toLowerCase();
                if (lowercaseName.contains(flagname.toLowerCase())) {
                    return true;
                } else {
                    return false;
                }
            }
        };
        File[] FlagChildren = FileUtil.listFiles(file, FlagFilter, true);
        for (File candidate : FlagChildren) {
            if (candidate.isFile()) {
                mediaFile.setMediaType(mediaType);
                LOG.debug("## Found FileTag " + mediaType + ": " + file.getName());
                return true;
            }
        }
        return false;
    }

    /**
     * Returns a converted Albumsetname for the given parent folder.
     */
    private String searchforAlbumSetName(String albumname, String parentalbumname) {
        int i = 1;
        boolean AlbumSetFound;
        while (i < 16) {
            String[] strArray = { "CD" + i, "CD " + i, "DISC" + i, "DISC " + i, "DISK" + i, "DISK " + i, "TEIL" + i,
                    "TEIL " + i, "PART" + i, "PART " + i };
            AlbumSetFound = false;

            for (String searchTerm : strArray) {
                if (albumname.toLowerCase().contains(searchTerm.toLowerCase())) {
                    AlbumSetFound = true;
                }
            }
            if (AlbumSetFound == true) {
                parentalbumname = parentalbumname + " - Disk" + i;
            }
            i++;
        }
        return parentalbumname;
    }

    private String checkForCommentFile(File file) {

        File commentFile = new File(file.getPath(), "comment.txt");
        if (commentFile.exists()) {
            LOG.info("## CommentFile found: " + commentFile);
            Path path = Paths.get(commentFile.getPath());
            List<String> lines = null;
            String listString = "";

            try {
                lines = Files.readAllLines(path, StandardCharsets.UTF_8);
            } catch (IOException e) {
                LOG.warn("## error reading commentfile: " + commentFile);
                //            e.printStackTrace();
            }

            if (lines == null) {
                try {
                    lines = Files.readAllLines(path, StandardCharsets.ISO_8859_1);
                } catch (IOException e) {
                    LOG.warn("## error reading commentfile: " + commentFile);
                    //                  e.printStackTrace();
                }
            }

            for (String s : lines) {
                s = s.replace("", "'");
                listString += s + " \\\\";
            }

            return listString;
        }
        return null;
    }

    private MediaFile.MediaType getMediaType(MediaFile mediaFile) {
        String path = mediaFile.getPath().toLowerCase();
        if (path.toLowerCase().endsWith("url")) {
            return VIDEO;
        }
        if (isVideoFile(mediaFile.getFormat())) {
            return VIDEO;
        }
        String genre = StringUtils.trimToEmpty(mediaFile.getGenre()).toLowerCase();
        if (path.contains("podcast") || genre.contains("podcast")) {
            return PODCAST;
        }
        if (path.contains("audiobook") || genre.contains("audiobook") || path.contains("audio book")
                || genre.contains("audio book")) {
            return AUDIOBOOK;
        }
        return MUSIC;
    }

    @Deprecated // refreshMediaFileRank ->  refreshMediaFile
    public void refreshMediaFileRank(MediaFile mediaFile) {
        mediaFileDao.createOrUpdateMediaFile(mediaFile);
        mediaFileMemoryCache.remove(mediaFile.getFile());
        mediaFileMemoryCache.put(new Element(mediaFile.getFile(), mediaFile));
    }

    public void refreshMediaFile(MediaFile mediaFile) {
        MediaFile newMediaFile = new MediaFile();
        newMediaFile = createMediaFile(mediaFile.getFile());
        newMediaFile.setRank(mediaFile.getRank());
        mediaFileDao.createOrUpdateMediaFile(newMediaFile);
        mediaFileMemoryCache.remove(newMediaFile.getFile());
    }

    private void putInMemoryCache(File file, MediaFile mediaFile) {
        if (memoryCacheEnabled) {
            mediaFileMemoryCache.put(new Element(file, mediaFile));
        }
    }

    private MediaFile getFromMemoryCache(File file) {
        if (!memoryCacheEnabled) {
            return null;
        }
        Element element = mediaFileMemoryCache.get(file);
        return element == null ? null : (MediaFile) element.getObjectValue();
    }

    public void setMemoryCacheEnabled(boolean memoryCacheEnabled) {
        this.memoryCacheEnabled = memoryCacheEnabled;
        if (!memoryCacheEnabled) {
            mediaFileMemoryCache.removeAll();
        }
    }

    /**
     * Returns a cover art image for the given media file.
     */
    public File getCoverArt(MediaFile mediaFile) {
        if (mediaFile.getCoverArtFile() != null) {
            return mediaFile.getCoverArtFile();
        }
        MediaFile parent = getParentOf(mediaFile);
        return parent == null ? null : parent.getCoverArtFile();
    }

    /**
     * Finds a cover art image for the given directory, by looking for it on the disk.
     */
    private File findCoverArt(File[] candidates) throws IOException {
        for (String mask : settingsService.getCoverArtFileTypesAsArray()) {
            for (File candidate : candidates) {
                if (candidate.isFile() && candidate.getName().toUpperCase().endsWith(mask.toUpperCase())
                        && !candidate.getName().startsWith(".")) {
                    return candidate;
                }
            }
        }

        // Look for embedded images in audiofiles. (Only check first audio file encountered).
        JaudiotaggerParser parser = new JaudiotaggerParser();
        for (File candidate : candidates) {
            if (parser.isApplicable(candidate)) {
                if (parser.isImageAvailable(getMediaFile(candidate))) {
                    return candidate;
                } else {
                    return null;
                }
            }
        }
        return null;
    }

    public void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    public void setSettingsService(SettingsService settingsService) {
        this.settingsService = settingsService;
    }

    public void setMediaFileMemoryCache(Ehcache mediaFileMemoryCache) {
        this.mediaFileMemoryCache = mediaFileMemoryCache;
    }

    public void setMediaFileDao(MediaFileDao mediaFileDao) {
        this.mediaFileDao = mediaFileDao;
    }

    public void setMusicFolderDao(MusicFolderDao musicFolderDao) {
        this.musicFolderDao = musicFolderDao;
    }

    /**
     * Returns all media files that are children, grand-children etc of a given media file.
     * Directories are not included in the result.
     *
     * @param sort Whether to sort files in the same directory.
     * @return All descendant music files.
     */
    public List<MediaFile> getDescendantsOf(MediaFile ancestor, boolean sort) {

        if (ancestor.isFile()) {
            return Arrays.asList(ancestor);
        }

        List<MediaFile> result = new ArrayList<MediaFile>();

        for (MediaFile child : getChildrenOf(ancestor, true, true, sort)) {
            if (child.isDirectory()) {
                result.addAll(getDescendantsOf(child, sort));
            } else {
                result.add(child);
            }
        }
        return result;
    }

    public void setMetaDataParserFactory(MetaDataParserFactory metaDataParserFactory) {
        this.metaDataParserFactory = metaDataParserFactory;
    }

    public void updateMediaFile(MediaFile mediaFile) {
        mediaFileDao.createOrUpdateMediaFile(mediaFile);
    }

    /**
     * Increments the play count and last played date for the given media file 
     */
    public void updateStatisticsUser(String username, MediaFile mediaFile) {
        mediaFileDao.setPlayCountForUser(username, mediaFile);
    }

    /**
     * Increments the play count and last played date for the given media file and its
     * directory and album.
     */
    public void incrementPlayCount(MediaFile file) {
        Date now = new Date();
        file.setLastPlayed(now);
        file.setPlayCount(file.getPlayCount() + 1);
        updateMediaFile(file);

        MediaFile parent = getParentOf(file);
        if (!isRoot(parent)) {
            parent.setLastPlayed(now);
            parent.setPlayCount(parent.getPlayCount() + 1);
            updateMediaFile(parent);
        }

        Album album = albumDao.getAlbum(file.getAlbumArtist(), parent.getAlbumSetName());

        if (album != null) {
            album.setLastPlayed(now);
            album.setPlayCount(album.getPlayCount() + 1);
            albumDao.createOrUpdateAlbum(album);
        }
    }

    public void clearMemoryCache() {
        mediaFileMemoryCache.removeAll();
    }

    public List<Album> getAlbumsForArtist(String artist) {
        return albumDao.getAlbumsForArtist(artist);
    }

    public List<Album> getAllAlbums() {
        return albumDao.getAllAlbums();
    }

    public void setAlbumDao(AlbumDao albumDao) {
        this.albumDao = albumDao;
    }

    public void setArtistDao(ArtistDao artistDao) {
        this.artistDao = artistDao;
    }

    public List<String> getArtistFolder(String sortableName) {
        return artistDao.getArtistsForFolder(sortableName);
    }

}