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

com.moviejukebox.model.Movie 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.model;

import com.moviejukebox.model.Artwork.Artwork;
import com.moviejukebox.model.Artwork.ArtworkType;
import com.moviejukebox.plugin.MovieDatabasePlugin;
import com.moviejukebox.tools.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.pojava.datetime.DateTime;

/**
 * Movie bean
 *
 * @author jjulien
 * @author artem.gratchev
 */
@XmlType
public class Movie implements Comparable, Identifiable, IMovieBasicInformation {
    /*
     * Static & Final variables that are used for control and don't relate
     * specifically to the Movie object
     */

    private static final Logger logger = Logger.getLogger(Movie.class);
    public static final String UNKNOWN = "UNKNOWN";
    public static final String NOTRATED = "Not Rated";
    public static final String REMOVE = "Remove"; // All Movie objects with this type will be removed from library before index generation
    public static final String TYPE_MOVIE = "MOVIE";
    public static final String TYPE_TVSHOW = "TVSHOW";
    public static final String TYPE_UNKNOWN = UNKNOWN;
    public static final String TYPE_VIDEO_UNKNOWN = UNKNOWN;
    public static final String TYPE_VIDEO_HD = "HD";
    public static final String TYPE_BLURAY = "BLURAY"; // Used to indicate what physical format the video is
    public static final String TYPE_DVD = "DVD"; // Used to indicate what physical format the video is
    public static final String TYPE_FILE = "FILE"; // Used to indicate what physical format the video is
    public static final String TYPE_PERSON = "PERSON";
    public static final String SPACE_SLASH_SPACE = " / ";
    private String mjbVersion = UNKNOWN;
    private String mjbRevision = UNKNOWN;
    private DateTime mjbGenerationDate = null;
    /*
     * --------------------------------------------------------------------------------
     * Properties that control the object
     */
    private static final List SORT_IGNORE_PREFIXES = new ArrayList();
    private int highdef720 = PropertiesUtil.getIntProperty("highdef.720.width", "1280");    // Get the minimum width for a high-definition movies
    private int highdef1080 = PropertiesUtil.getIntProperty("highdef.1080.width", "1920");  // Get the minimum width for a high-definition movies
    private String[] ratingSource = PropertiesUtil.getProperty("mjb.rating.source", "average").split(",");
    private String tmpRatingIgnore = PropertiesUtil.getProperty("mjb.rating.ignore", "");
    private List ratingIgnore = StringTools.isValidString(tmpRatingIgnore) ? Arrays.asList(tmpRatingIgnore.split(",")) : new ArrayList();
    private static final Set GENRE_SKIP_LIST = new HashSet();   // List of genres to ignore
    private static String titleSortType = PropertiesUtil.getProperty("mjb.sortTitle", "title");
    /*
     * --------------------------------------------------------------------------------
     * Properties related to the Movie object itself
     */
    private String baseName;        // Safe name for generated files
    private String baseFilename;    // Base name for finding posters, nfos, banners, etc.
    private Map idMap = new HashMap(2);
    private String title = UNKNOWN;
    private String titleSort = UNKNOWN;
    private String strippedTitleSort = UNKNOWN; // Not saved, used to speedup the sort
    private String originalTitle = UNKNOWN;
    private String year = UNKNOWN;
    private String releaseDate = UNKNOWN;
    private Map ratings = new HashMap();
    private String plot = UNKNOWN;
    private String outline = UNKNOWN;
    private String quote = UNKNOWN;
    private String tagline = UNKNOWN;
    private String country = UNKNOWN;
    private String company = UNKNOWN;
    private String runtime = UNKNOWN;
    private String language = UNKNOWN;
    private String videoType = UNKNOWN;
    private String subtitles = UNKNOWN;
    private Set directors = new LinkedHashSet();
    private Map sets = new HashMap();
    private Collection genres = new TreeSet();
    private Set cast = new LinkedHashSet();
    private Set writers = new LinkedHashSet();
    private String container = UNKNOWN; // AVI, MKV, TS, etc.
    private Set codecs = new LinkedHashSet();
    private String resolution = UNKNOWN; // 1280x528
    private String aspect = UNKNOWN;
    private String videoSource = UNKNOWN;
    private String videoOutput = UNKNOWN;
    private float fps = 60;
    private String certification = UNKNOWN;
    private String showStatus = UNKNOWN;    // For TV shows a status such as "Continuing" or "Ended"
    private boolean scrapeLibrary;
    private boolean extra = Boolean.FALSE;  // TODO Move extra flag to movie file
    private boolean trailerExchange = Boolean.FALSE;    // Trailers
    private long trailerLastScan = 0;           // Trailers
    private Collection awards = new ArrayList();    // Issue 1901: Awards
    private Collection people = new ArrayList();  // Issue 1897: Cast enhancement
    private String budget = UNKNOWN;                                        // Issue 2012: Financial information about movie
    private Map openweek = new HashMap();
    private Map gross = new HashMap();
    private List didYouKnow = new ArrayList();        // Issue 2013: Add trivia
    private String libraryPath = UNKNOWN;
    private String movieType = TYPE_MOVIE;
    private String formatType = TYPE_FILE;
    private boolean overrideTitle = Boolean.FALSE;
    private boolean overrideYear = Boolean.FALSE;
    private int top250 = -1;
    private String libraryDescription = UNKNOWN;
    private long prebuf = -1;
    // Graphics URLs & files
    private Set artwork = new LinkedHashSet();
    private String posterURL = UNKNOWN; // The original, unaltered, poster
    private String posterFilename = UNKNOWN; // The poster filename
    private String detailPosterFilename = UNKNOWN; // The resized poster for skins
    private String thumbnailFilename = UNKNOWN; // The thumbnail version of the poster for skins
    private List footerFilename = new ArrayList(); // The footer image for skins
    private String fanartURL = UNKNOWN; // The fanart URL
    private String fanartFilename = UNKNOWN; // The resized fanart file
    private String bannerURL = UNKNOWN; // The TV Show banner URL
    private String bannerFilename = UNKNOWN; // The resized banner file
    private String clearArtURL = UNKNOWN;
    private String clearArtFilename = UNKNOWN;
    private String clearLogoURL = UNKNOWN;
    private String clearLogoFilename = UNKNOWN;
    private String seasonThumbURL = UNKNOWN;
    private String seasonThumbFilename = UNKNOWN;
    private String tvThumbURL = UNKNOWN;
    private String tvThumbFilename = UNKNOWN;
    private String movieDiscURL = UNKNOWN;
    private String movieDiscFilename = UNKNOWN;
    // File information
    private Date fileDate = null;
    private long fileSize = 0;
    private long lastModifiedTimeStamp = Long.valueOf(0);      // cache value
    private boolean watchedFile = Boolean.FALSE;    // Watched / Unwatched - Set from the .watched files
    private boolean watchedNFO = Boolean.FALSE;     // Watched / Unwatched - Set from the NFO file
    // Navigation data
    private String first = UNKNOWN;
    private String previous = UNKNOWN;
    private String next = UNKNOWN;
    private String last = UNKNOWN;
    private Map indexes = new HashMap();
    // Media file properties
    private Collection movieFiles = new TreeSet();
    private Collection extraFiles = new TreeSet();
    private Set dirtyFlags = EnumSet.noneOf(DirtyFlag.class);    // List of the dirty flags associated with the Movie
    private File file;
    private File containerFile;
    // Set information
    private boolean isSetMaster = Boolean.FALSE;    // True if movie actually is only a entry point to movies set.
    private int setSize = 0;                        // Amount of movies in set
    private MovieDatabasePlugin movieScanner = null;

    /*
     * --------------------------------------------------------------------------------
     * End of properties
     * --------------------------------------------------------------------------------
     */
    static {
        if (GENRE_SKIP_LIST.isEmpty()) {
            StringTokenizer st = new StringTokenizer(PropertiesUtil.getProperty("mjb.genre.skip", ""), ",;|");
            while (st.hasMoreTokens()) {
                GENRE_SKIP_LIST.add(st.nextToken().toLowerCase());
            }
        }
    }

    public void setMjbVersion(String mjbVersion) {
        if (StringTools.isNotValidString(mjbVersion)) {
            this.mjbVersion = SystemTools.getVersion();
        } else {
            this.mjbVersion = mjbVersion;
        }
    }

    @XmlElement
    public String getMjbVersion() {
        return mjbVersion;
    }

    public void setMjbRevision(String mjbRevision) {
        if (StringTools.isNotValidString(mjbRevision)) {
            this.mjbRevision = "0000";
        } else {
            this.mjbRevision = mjbRevision;
        }
    }

    @XmlElement
    public String getMjbRevision() {
        // If YAMJ is self compiled then the revision information may not exist.
        if (!(StringUtils.isBlank(mjbRevision) || (mjbRevision.equalsIgnoreCase("${env.SVN_REVISION}")))) {
            return mjbRevision;
        } else {
            return Movie.UNKNOWN;
        }
    }

    public void setMjbGenerationDateString(String mjbGenerationDate) {
        try {
            this.mjbGenerationDate = DateTime.parse(mjbGenerationDate);
        } catch (Exception error) {
            this.mjbGenerationDate = new DateTime();
        }
    }

    public void setMjbGenerationDate(DateTime mjbGenerationDate) {
        if (mjbGenerationDate == null) {
            this.mjbGenerationDate = new DateTime();
        } else {
            this.mjbGenerationDate = mjbGenerationDate;
        }
    }

    @XmlElement
    public String getMjbGenerationDateString() {
        return getMjbGenerationDate().toString(StringTools.getDateFormatLongString());
    }

    public DateTime getMjbGenerationDate() {
        if (this.mjbGenerationDate == null) {
            this.mjbGenerationDate = new DateTime();
        }
        return this.mjbGenerationDate;
    }

    public void addGenre(String genre) {
        if (StringTools.isValidString(genre) && !extra && !GENRE_SKIP_LIST.contains(genre.toLowerCase())) {
            setDirty(DirtyFlag.INFO);
            //logger.debug("Genre added: " + genre);
            genres.add(genre);
        }
    }

    public void addGenres(List genres) {
        for (String newGenre : genres) {
            addGenre(newGenre);
        }
    }

    public void addSet(String set) {
        addSet(set, null);
    }

    public void addSet(String set, Integer order) {
        if (StringTools.isValidString(set)) {
            setDirty(DirtyFlag.INFO);
            logger.debug("Set added: " + set + ", order: " + order);
            sets.put(set, order);
        }
    }

    public void addMovieFile(MovieFile movieFile) {
        if (movieFile != null) {
            setDirty(DirtyFlag.INFO);
            // Always replace MovieFile
            for (MovieFile mf : this.movieFiles) {
                if (mf.compareTo(movieFile) == 0) {
                    movieFile.setFile(mf.getFile());
                    movieFile.setInfo(mf.getInfo());
                }
            }
            //logger.debug("Movie addMovieFile: " + movieFile.getFilename());
            this.movieFiles.remove(movieFile);
            this.movieFiles.add(movieFile);
        }
    }

    public void addAward(AwardEvent award) {
        if (award != null) {
            setDirty(DirtyFlag.INFO);
            this.awards.add(award);
        }
    }

    public void addPerson(Filmography person) {
        if (person != null) {
            boolean duplicate = Boolean.FALSE;
            String name = person.getName();
            String job = person.getJob();
            for (Filmography p : people) {
                if (p.getName().equals(name) && p.getJob().equals(job)) {
                    duplicate = Boolean.TRUE;
                    break;
                }
            }
            if (!duplicate) {
                setDirty(DirtyFlag.INFO);
                people.add(person);
            }
        }
    }

    public void removePerson(Filmography person) {
        if (person != null) {
            people.remove(person);
        }
    }

    public void addPerson(String name) {
        addPerson(Movie.UNKNOWN, name, Movie.UNKNOWN, Movie.UNKNOWN, Movie.UNKNOWN, Movie.UNKNOWN);
    }

    public void addPerson(String key, String name) {
        addPerson(key, name, Movie.UNKNOWN, Movie.UNKNOWN, Movie.UNKNOWN, Movie.UNKNOWN);
    }

    public void addPerson(String key, String name, String url) {
        addPerson(key, name, url, Movie.UNKNOWN, Movie.UNKNOWN, Movie.UNKNOWN);
    }

    public void addPerson(String key, String name, String url, String job) {
        addPerson(key, name, url, job, Movie.UNKNOWN, Movie.UNKNOWN);
    }

    public void addPerson(String key, String name, String url, String job, String character) {
        addPerson(key, name, url, job, character, Movie.UNKNOWN);
    }

    public void addPerson(String key, String name, String url, String job, String character, String doublage) {
        if (StringUtils.isNotBlank(name)
                && StringUtils.isNotBlank(key)
                && StringUtils.isNotBlank(url)
                && StringUtils.isNotBlank(job)) {

            Filmography person = new Filmography();

            if (key.indexOf(':') > -1) {
                String[] keys = key.split(":");
                person.setId(keys[0], keys[1]);
            } else {
                person.setId(key);
            }

            if (name.indexOf(':') > -1) {
                String[] names = name.split(":");
                if (StringTools.isValidString(names[0])) {
                    person.setName(names[0]);
                    person.setTitle(names[1]);
                } else if (StringTools.isValidString(names[1])) {
                    person.setName(names[1]);
                } else {
                    person.setName(name);
                }
            } else {
                person.setName(name);
            }

            person.setUrl(url);
            person.setCharacter(character);
            person.setDoublage(doublage);
            person.setJob(job);
            person.setDepartment();

            int countActor = 0;
            if (person.getDepartment().equalsIgnoreCase(Filmography.DEPT_ACTORS)) {
                for (Filmography member : people) {
                    if (member.getDepartment().equalsIgnoreCase(Filmography.DEPT_ACTORS)) {
                        countActor++;
                    }
                }
            }

            person.setOrder(countActor);
            person.setCastId(people.size());
            person.setScrapeLibrary(scrapeLibrary);
            addPerson(person);
        }
    }

    public void removeMovieFile(MovieFile movieFile) {
        if (movieFile != null) {
            setDirty(DirtyFlag.INFO);
            for (MovieFile mf : this.movieFiles) {
                if (mf.compareTo(movieFile) == 0) {
                    this.movieFiles.remove(mf);
                    break;
                }
            }

        }
    }

    public boolean hasNewMovieFiles() {
        for (MovieFile movieFile : movieFiles) {
            if (movieFile.isNewFile()) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * Add a new extra file to the movie
     *
     * @param extraFile
     */
    public void addExtraFile(ExtraFile extraFile) {
        addExtraFile(extraFile, Boolean.TRUE);
    }

    /**
     * Add a new extra file to the movie without marking the movie as dirty.
     *
     * @param extraFile
     * @param isNewFile Use carefully as this will not cause the movie to be
     * marked as dirty and may not be written out
     */
    public void addExtraFile(ExtraFile extraFile, boolean isNewFile) {
        // Only add extraFile if it doesn't already exists
        if (extraFile != null && !this.extraFiles.contains(extraFile)) {
            if (isNewFile) {
                setDirty(DirtyFlag.INFO);
            }
            this.extraFiles.add(extraFile);
        }
    }

    public boolean hasNewExtraFiles() {
        for (MovieFile movieFile : extraFiles) {
            if (movieFile.isNewFile()) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    public String getStrippedTitleSort() {
        // If we have a strippedTitleSort, return that
        if (StringTools.isValidString(strippedTitleSort)) {
            return strippedTitleSort;
        }

        StringBuilder text = new StringBuilder(getStrippedTitle(getTitleSort()));
        int season = getSeason();

        // Added season to handle properly sorting the season numbers
        if (season >= 0) {
            if (season < 10) {
                text.append(" 0");
            } else {
                text.append(" ");
            }
            text.append(season);
        }
        // Added Year to handle movies like Ocean's Eleven (1960) and Ocean's Eleven (2001)
        text.append(" (").append(this.getYear()).append(") ");
        strippedTitleSort = text.toString();
        return strippedTitleSort;
    }

    /**
     * Remove the sorting strip prefix from the title
     *
     * @param title
     * @return
     */
    private String getStrippedTitle(String title) {
        String lowerTitle = title.toLowerCase();

        for (String prefix : SORT_IGNORE_PREFIXES) {
            if (lowerTitle.startsWith(prefix.toLowerCase())) {
                return new String(title.substring(prefix.length()));
            }
        }

        return title;
    }

    @Override
    public int compareTo(Movie anotherMovie) {
        return this.getStrippedTitleSort().compareToIgnoreCase(anotherMovie.getStrippedTitleSort());
    }

    @Deprecated
    public String getAudioCodec() {
        StringBuilder sb = new StringBuilder();
        boolean firstCodec = Boolean.TRUE;
        for (Codec audioCodec : codecs) {
            if (audioCodec.getCodecType() == CodecType.AUDIO) {
                if (firstCodec) {
                    firstCodec = Boolean.FALSE;
                } else {
                    sb.append(SPACE_SLASH_SPACE);
                }

                if (StringTools.isValidString(audioCodec.getCodecIdHint())) {
                    sb.append(audioCodec.getCodecIdHint());
                } else if (StringTools.isValidString(audioCodec.getCodec())) {
                    sb.append(audioCodec.getCodec());
                } else if (StringTools.isValidString(audioCodec.getCodecFormat())) {
                    sb.append(audioCodec.getCodecFormat());
                } else if (StringTools.isValidString(audioCodec.getCodecId())) {
                    sb.append(audioCodec.getCodecId());
                } else {
                    sb.append(Movie.UNKNOWN);
                }

                if (StringTools.isValidString(audioCodec.getCodecLanguage())) {
                    sb.append(" (");
                    sb.append(audioCodec.getCodecLanguage());
                    sb.append(")");
                }
            }
        }

        if (sb.length() > 0) {
            return sb.toString();
        } else {
            return UNKNOWN;
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#getBaseName()
     */
    @Override
    public String getBaseName() {
        return baseName;
    }

    public String getBaseFilename() {
        return baseFilename;
    }

    @XmlElementWrapper(name = "cast")
    @XmlElement(name = "actor")
    public Collection getCast() {
        return cast;
    }

    @XmlElementWrapper(name = "writers")
    @XmlElement(name = "writer")
    public Collection getWriters() {
        return writers;
    }

    public Collection getDidYouKnow() {
        return didYouKnow;
    }

    public String getCompany() {
        return company;
    }

    public String getContainer() {
        return container;
    }

    public String getCountry() {
        return country;
    }

    public Collection getFiles() {
        return movieFiles;
    }

    @XmlElementWrapper(name = "extras")
    @XmlElement(name = "extra")
    public Collection getExtraFiles() {
        return extraFiles;
    }

    @XmlElementWrapper(name = "awards")
    @XmlElement(name = "award")
    public Collection getAwards() {
        return awards;
    }

    @XmlElementWrapper(name = "people")
    @XmlElement(name = "person")
    public Collection getPeople() {
        return people;
    }

    public Collection getPerson(String department) {
        Collection pList = new ArrayList();
        for (Filmography p : people) {
            if (p.getDepartment().equals(department)) {
                pList.add(p.getTitle());
            }
        }
        return pList;
    }

    public String getCertification() {
        return certification;
    }

    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getFirst() {
        return first;
    }

    /**
     * Get the first logical file of the set of videos
     *
     * @return
     */
    public MovieFile getFirstFile() {

        for (MovieFile part : movieFiles) {
            if (part.getFirstPart() == 1) {
                return part;
            }
        }

        Iterator i = movieFiles.iterator();
        if (i.hasNext()) {
            return i.next();
        } else {
            return null;
        }
    }

    public float getFps() {
        return fps;
    }

    @XmlElementWrapper(name = "genres")
    @XmlElement(name = "genre")
    public Collection getGenres() {
        return genres;
    }

    public Collection getSetsKeys() {
        return sets.keySet();
    }

    public Integer getSetOrder(String set) {
        return sets.get(set);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.Identifiable#getId(java.lang.String)
     */
    @Override
    public String getId(String key) {
        String result = idMap.get(key);
        if (result != null) {
            return result;
        } else {
            return UNKNOWN;
        }
    }

    public Map getIdMap() {
        return idMap;
    }

    public String getGross(String country) {
        String result = gross.get(country);
        if (result != null) {
            return result;
        } else {
            return UNKNOWN;
        }
    }

    public Map getGross() {
        return gross;
    }

    public String getOpenWeek(String country) {
        String result = openweek.get(country);
        if (result != null) {
            return result;
        } else {
            return UNKNOWN;
        }
    }

    public Map getOpenWeek() {
        return openweek;
    }

    public static class MovieId {

        @XmlAttribute
        public String movieDatabase;
        @XmlValue
        public String value;
    }

    @XmlElement(name = "id")
    public List getMovieIds() {
        List list = new ArrayList();
        for (Entry e : idMap.entrySet()) {
            MovieId id = new MovieId();
            id.movieDatabase = e.getKey();
            id.value = e.getValue();
            list.add(id);
        }
        return list;
    }

    public void setMovieIds(List list) {
        idMap.clear();
        for (MovieId id : list) {
            idMap.put(id.movieDatabase, id.value);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#getLanguage()
     */
    @Override
    public String getLanguage() {
        return language;
    }

    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getLast() {
        return last;
    }

    @XmlElementWrapper(name = "files")
    @XmlElement(name = "file")
    public Collection getMovieFiles() {
        return movieFiles;
    }

    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getNext() {
        return next;
    }

    public String getPlot() {
        return plot;
    }

    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getPrevious() {
        return previous;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public String getResolution() {
        return resolution;
    }

    // Return the width of the movie
    public int getWidth() {
        int width;
        try {
            width = Integer.parseInt(new String(getResolution().substring(0, getResolution().indexOf("x"))));
        } catch (Exception error) {
            // This will catch the exception if mediainfo is not installed.
            width = 0;
        }
        return width;
    }

    public String getRuntime() {
        return runtime;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#getSeason()
     */
    @Override
    public int getSeason() {
        /*
         * Return the first season as the whole season
         *
         * This could be changed later to allow multi season movie objects. Do
         * not return a value for the set master.
         */
        if (movieFiles.size() > 0 && !isSetMaster) {
            return getFirstFile().getSeason();
        } else {
            // Strictly speaking this isn't "-1" its a non-existent season.
            return -1;
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#getTitle()
     */
    @Override
    public String getTitle() {
        return title;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#getTitleSort()
     */
    /**
     * Return the correct sort title based on the mjb.sortTitle parameter
     */
    @Override
    public String getTitleSort() {
        // If we have a titleSort, return that
        if (StringTools.isValidString(titleSort)) {
            return titleSort;
        }

        // There are three choices for the sort title: title, original, filename

        if ("title".equalsIgnoreCase(titleSortType)) {
            // Set the title sort (so this is only done once)
            setTitleSort(title);
            return titleSort;
        }

        if ("filename".equalsIgnoreCase(titleSortType)) {
            // Set the title sort (so this is only done once)
            setTitleSort(baseName);
            return titleSort;
        }

        if ("original".equalsIgnoreCase(titleSortType)) {
            if (StringTools.isValidString(originalTitle)) {
                // Set the title sort (so this is only done once)
                setTitleSort(originalTitle);
                return titleSort;
            } else {
                setTitleSort(title);
                return titleSort;
            }
        }

        // Set the title sort (so this is only done once)
        setTitleSort(title);
        return titleSort;
    }

    @Override
    public String getOriginalTitle() {
        return originalTitle;
    }

    public String getVideoOutput() {
        return videoOutput;
    }

    public String getVideoSource() {
        return videoSource;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#getYear()
     */
    @Override
    public String getYear() {
        return year;
    }

    public String getBudget() {
        return budget;
    }

    public String getSubtitles() {
        return subtitles;
    }

    public void setDirty(DirtyFlag dirtyType, boolean dirty) {
        if (dirty) {
            dirtyFlags.add(dirtyType);
        } else {
            dirtyFlags.remove(dirtyType);
        }
    }

    public void setDirty(DirtyFlag dirtyType) {
        dirtyFlags.add(dirtyType);
    }

    /**
     * Clear ALL the dirty flags, and just set DirtyFlag.INFO to the passed
     * value
     *
     * @param dirty
     */
    public void setDirty(boolean dirty) {
        clearDirty();
        setDirty(DirtyFlag.INFO, dirty);
    }

    /**
     * Returns true if ANY of the dirty flags are set. Use with caution, it's
     * better to test individual flags as you need them, rather than this
     * generic flag
     *
     * @return
     */
    public boolean isDirty() {
        if (dirtyFlags.isEmpty()) {
            return Boolean.FALSE;
        } else {
            return Boolean.TRUE;
        }
    }

    public String showDirty() {
        if (dirtyFlags.isEmpty()) {
            return "NOT DIRTY";
        } else {
            return dirtyFlags.toString();
        }
    }

    public Set getDirty() {
        return dirtyFlags;
    }

    public void clearDirty() {
        dirtyFlags.clear();
    }

    public boolean isDirty(DirtyFlag dirtyType) {
        return dirtyFlags.contains(dirtyType);
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#isTVShow()
     */
    @XmlAttribute(name = "isTV")
    @Override
    public boolean isTVShow() {
        return (this.movieType.equals(TYPE_TVSHOW) || getSeason() != -1);
    }

    @XmlTransient
    public boolean isBluray() {
        return this.formatType.equals(TYPE_BLURAY);
    }

    @XmlTransient
    public boolean isDVD() {
        return this.formatType.equals(TYPE_DVD);
    }

    @XmlTransient
    public boolean isFile() {
        return this.formatType.equals(TYPE_FILE);
    }

    @XmlTransient
    public boolean isHD() {
        // Depreciated this check in favour of the width check
        // return this.videoType.equals(TYPE_VIDEO_HD) || videoOutput.indexOf("720") != -1 || videoOutput.indexOf("1080") != -1;
        return (getWidth() >= highdef720);
    }

    @XmlTransient
    public boolean isHD1080() {
        return (getWidth() >= highdef1080);
    }

    @XmlTransient
    public boolean is3D() {
        return (getVideoSource().indexOf("3D") > -1);
    }

    public void setBaseName(String baseName) {
        this.baseName = validateString(baseName, this.baseName);
    }

    public void setBaseFilename(String baseFilename) {
        this.baseFilename = validateString(baseFilename, this.baseFilename);
    }

    public void addDidYouKnow(String fact) {
        if (fact != null && !didYouKnow.contains(fact)) {
            setDirty(DirtyFlag.INFO);
            didYouKnow.add(fact);
        }
    }

    public void setDidYouKnow(List facts) {
        if (facts != null && !facts.isEmpty()) {
            didYouKnow = facts;
            setDirty(DirtyFlag.INFO);
        }
    }

    public void clearDidYouKnow() {
        didYouKnow.clear();
        setDirty(DirtyFlag.INFO);
    }

    public void clearAwards() {
        awards.clear();
        setDirty(DirtyFlag.INFO);
    }

    public void addActor(String actor) {
        if (StringTools.isNotValidString(actor)) {
            return;
        }

        if (!cast.contains(actor.trim())) {
            setDirty(DirtyFlag.INFO);
            cast.add(actor.trim());
        }
    }

    public void addActor(String actorKey, String actorName, String character, String actorUrl, String doublage) {
        if (actorName != null) {
            String name = actorName;
            if (actorName.indexOf(':') > -1) {
                String[] names = actorName.split(":");
                if (StringTools.isValidString(names[1])) {
                    name = names[1];
                } else if (StringTools.isValidString(names[0])) {
                    name = names[0];
                }
            }

            name = name.trim();
            boolean found = Boolean.FALSE;
            for (Filmography p : people) {
                if (p.getName().equalsIgnoreCase(name) && p.getDepartment().equals(Filmography.DEPT_ACTORS)) {
                    found = Boolean.TRUE;
                    break;
                }
            }

            if (!found) {
                addActor(name);
                addPerson(actorKey, actorName, actorUrl, StringUtils.capitalize(Filmography.JOB_ACTOR), character, doublage);
            }
        }
    }

    public void setCast(Collection cast) {
        if (cast != null && !cast.isEmpty()) {
            clearCast();
            this.cast.addAll(cast);
            Collection pList = new ArrayList();

            for (Filmography p : people) {
                if (p.getDepartment().equals(Filmography.DEPT_ACTORS)) {
                    pList.add(p);
                }
            }

            for (Filmography p : pList) {
                removePerson(p);
            }

            for (String member : cast) {
                addActor(Movie.UNKNOWN, member, Movie.UNKNOWN, Movie.UNKNOWN, Movie.UNKNOWN);
            }
        }
    }

    public void clearCast() {
        setDirty(DirtyFlag.INFO);
        cast.clear();
    }

    public void addWriter(String writer) {
        if (writer != null && !writers.contains(writer)) {
            setDirty(DirtyFlag.INFO);
            writers.add(writer);
        }
    }

    public void addWriter(String writerKey, String writerName, String writerUrl) {
        if (writerName != null) {
            String name = writerName;
            if (writerName.contains(":")) {
                String[] names = writerName.split(":");
                if (StringTools.isValidString(names[1])) {
                    name = names[1];
                } else if (StringTools.isValidString(names[0])) {
                    name = names[0];
                }
            }

            name = name.trim();
            boolean found = Boolean.FALSE;

            for (Filmography p : people) {
                if (p.getName().equalsIgnoreCase(name) && p.getDepartment().equals(Filmography.DEPT_WRITING)) {
                    found = Boolean.TRUE;
                    break;
                }
            }

            if (!found) {
                addWriter(name);
                addPerson(writerKey, writerName, writerUrl, "Writer");
            }
        }
    }

    public void setWriters(Collection writers) {
        if (writers != null && !writers.isEmpty()) {
            clearWriters();
            this.writers.addAll(writers);
            Collection pList = new ArrayList();
            for (Filmography p : people) {
                if (p.getDepartment().equals(Filmography.DEPT_WRITING)) {
                    pList.add(p);
                }
            }
            for (Filmography p : pList) {
                removePerson(p);
            }
            for (String member : writers) {
                addWriter(Movie.UNKNOWN, member, Movie.UNKNOWN);
            }
        }
    }

    public void clearWriters() {
        setDirty(DirtyFlag.INFO);
        writers.clear();
    }

    public void setCompany(String company) {
        this.company = validateString(company, this.company);
    }

    public void setContainer(String container) {
        this.container = validateString(container, this.container);
    }

    public void setCountry(String country) {
        this.country = validateString(country, this.country);
    }

    /**
     * Get just one director from the collection
     *
     * @return
     */
    public String getDirector() {
        if (directors != null && !directors.isEmpty()) {
            return directors.iterator().next();
        } else {
            return UNKNOWN;
        }
    }

    public Collection getDirectors() {
        return directors;
    }

    public void setDirectors(Collection directors) {
        if (directors != null && !directors.isEmpty()) {
            clearDirectors();
            this.directors.addAll(directors);
            Collection pList = new ArrayList();
            for (Filmography p : people) {
                if (p.getDepartment().equals(Filmography.DEPT_DIRECTING)) {
                    pList.add(p);
                }
            }
            for (Filmography p : pList) {
                removePerson(p);
            }
            for (String member : directors) {
                addDirector(Movie.UNKNOWN, member, Movie.UNKNOWN);
            }
        }
    }

    public void clearDirectors() {
        setDirty(DirtyFlag.INFO);
        directors.clear();
    }

    public void addDirector(String director) {
        if (director != null && !directors.contains(director)) {
            setDirty(DirtyFlag.INFO);
            directors.add(director);
        }
    }

    public void addDirector(String key, String name, String URL) {
        if (name != null) {
            String directorName = name;
            if (name.contains(":")) {
                String[] names = name.split(":");
                if (StringTools.isValidString(names[1])) {
                    directorName = names[1];
                } else if (StringTools.isValidString(names[0])) {
                    directorName = names[0];
                }
            }

            directorName = directorName.trim();
            boolean found = Boolean.FALSE;
            for (Filmography p : people) {
                if (p.getName().equalsIgnoreCase(directorName) && p.getDepartment().equals(Filmography.DEPT_DIRECTING)) {
                    found = Boolean.TRUE;
                    break;
                }
            }
            if (!found) {
                addDirector(directorName);
                addPerson(key, name, URL, "Director");
            }
        }
    }

    public void setFirst(String first) {
        this.first = validateString(first, this.first);
    }

    public void setFps(float fps) {
        //Prevent wrong result caused by floating point rounding by allowing difference of 0.1 fpsS
        if (Math.abs(fps - this.fps) > 0.1) {
            setDirty(DirtyFlag.INFO);
            this.fps = fps;
        }
    }

    public void setGenres(Collection genresToAdd) {
        if (!extra) {
            // Only check if the skip list isn't empty
            if (!GENRE_SKIP_LIST.isEmpty()) {
                // remove any unwanted genres from the new collection
                Collection genresFinal = new TreeSet();

                Iterator genreIterator = genresToAdd.iterator();
                while (genreIterator.hasNext()) {
                    String genreToAdd = genreIterator.next();
                    if (!GENRE_SKIP_LIST.contains(genreToAdd.toLowerCase())) {
                        genresFinal.add(genreToAdd);
                    }
                }

                // Add the trimmed genre list
                this.genres = genresFinal;
            } else {
                // No need to remove genres, so add them all
                this.genres = genresToAdd;
            }

            setDirty(DirtyFlag.INFO);
        }
    }

    public void setSets(Map sets) {
        setDirty(DirtyFlag.INFO);
        this.sets = sets;
    }

    public Map getSets() {
        return sets;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.Identifiable#setId(java.lang.String,
     * java.lang.String)
     */
    @Override
    public void setId(String key, String id) {
        if (StringTools.isValidString(key) && StringTools.isValidString(id) && !id.equalsIgnoreCase(this.getId(key))) {
            setDirty(DirtyFlag.INFO);
            this.idMap.put(key, id);
        }
    }

    public void setId(String key, int id) {
        setId(key, Integer.toString(id));
    }

    public void setGross(String country, String value) {
        if (StringTools.isValidString(country) && StringTools.isValidString(value) && !value.equalsIgnoreCase(this.getGross(country))) {
            setDirty(DirtyFlag.INFO);
            this.gross.put(country, value);
        }
    }

    public void setGross(Map gross) {
        if (gross != null) {
            this.gross = gross;
        }
    }

    public void setOpenWeek(String country, String value) {
        if (StringTools.isValidString(country) && StringTools.isValidString(value) && !value.equalsIgnoreCase(this.getOpenWeek(country))) {
            setDirty(DirtyFlag.INFO);
            this.openweek.put(country, value);
        }
    }

    public void setOpenWeek(Map openweek) {
        if (openweek != null) {
            this.openweek = openweek;
        }
    }

    public void setLanguage(String language) {
        this.language = validateString(language, this.language);
    }

    public void setCertification(String certification) {
        this.certification = validateString(certification, this.certification);
    }

    public void setLast(String last) {
        this.last = validateString(last, this.last);
    }

    public void setMovieFiles(Collection movieFiles) {
        setDirty(DirtyFlag.INFO);
        this.movieFiles = movieFiles;
    }

    public void setExtraFiles(Collection extraFiles) {
        setDirty(DirtyFlag.INFO);
        this.extraFiles = extraFiles;
    }

    public void setAwards(Collection awards) {
        setDirty(DirtyFlag.INFO);
        this.awards = awards;
    }

    public void setPeople(Collection people) {
        setDirty(DirtyFlag.INFO);
        this.people = people;
    }

    public void setNext(String next) {
        this.next = validateString(next, this.next);
    }

    public void setPlot(String plot) {
        this.plot = validateString(plot, this.plot);
    }

    public String getOutline() {
        return outline;
    }

    public void setOutline(String outline) {
        this.outline = validateString(outline, this.outline);
    }

    public void setPrevious(String previous) {
        this.previous = validateString(previous, this.previous);
    }

    public int getRating() {
        if (ratings == null || ratings.isEmpty()) {
            return -1;
        }

        for (String site : ratingSource) {
            if ("average".equalsIgnoreCase(site)) {
                // Return the average of the ratings
                int rating = 0;
                int count = 0;

                for (String ratingSite : ratings.keySet()) {
                    if (ratingIgnore.size() > 0) {
                        if (ratingIgnore.contains(ratingSite)) {
                            continue;
                        } else {
                            boolean found = Boolean.FALSE;
                            for (String ignoreName : ratingIgnore) {
                                if (ratingSite.indexOf(ignoreName) == 0) {
                                    found = Boolean.TRUE;
                                    break;
                                }
                            }
                            if (found) {
                                continue;
                            }
                        }
                    }
                    rating += ratings.get(ratingSite);
                    count++;
                }

                return (count > 0 ? (rating / count) : 0);
            }

            if (ratings.containsKey(site)) {
                return ratings.get(site);
            }
        }

        // No ratings found, so return -1
        return -1;
    }

    public int getRating(String site) {
        if (ratings.containsKey(site)) {
            return ratings.get(site);
        } else {
            return -1;
        }
    }

    public Map getRatings() {
        return ratings;
    }

    public void addRating(String site, int rating) {
        if (StringTools.isValidString(site)) {
            if (ratings.containsKey(site)) {
                if (ratings.get(site) != rating) {
                    ratings.remove(site);
                    ratings.put(site, rating);
                    setDirty(DirtyFlag.INFO);
                }
            } else {
                ratings.put(site, rating);
            }
        }
    }

    public void setRatings(Map ratings) {
        if (ratings != null && !ratings.isEmpty()) {
            this.ratings = ratings;
        }
    }

    public void setReleaseDate(String releaseDate) {
        this.releaseDate = validateString(releaseDate, this.releaseDate);
    }

    public void setResolution(String resolution) {
        this.resolution = validateString(resolution, this.resolution);
    }

    public void setResolution(String width, String height) {
        if (StringTools.isValidString(width) && StringTools.isValidString(height)) {
            setResolution(width + "x" + height);
        }
    }

    public void setRuntime(String runtime) {
        if (StringUtils.isBlank(runtime)) {
            this.runtime = UNKNOWN;
            return;
        }

        if (!runtime.equalsIgnoreCase(this.runtime)) {
            setDirty(DirtyFlag.INFO);
            // Escape the first "0" AlloCine gives sometimes
            if (runtime.startsWith("0")) {
                this.runtime = new String(runtime.substring(1)).trim();
            } else {
                this.runtime = runtime.trim();
            }
        }
    }

    public void setSubtitles(String subtitles) {
        this.subtitles = validateString(subtitles, this.subtitles);
    }

    public void setTitle(String name) {
        String newName;
        if (StringUtils.isBlank(name)) {
            newName = UNKNOWN;
        } else {
            newName = name;
        }

        if (!newName.equals(this.title)) {
            setDirty(DirtyFlag.INFO);
            this.title = newName;

            // If we don't have a original title, then use the title
            if (StringTools.isNotValidString(originalTitle)) {
                setOriginalTitle(newName);
            }
        }
    }

    public void setTitleSort(String title) {
        String newTitle;
        if (StringUtils.isBlank(title)) {
            newTitle = UNKNOWN;
        } else {
            newTitle = title;
        }

        if (!newTitle.equals(this.titleSort)) {
            int idx = 0;
            while (idx < newTitle.length() && !Character.isLetterOrDigit(newTitle.charAt(idx))) {
                idx++;
            }

            // Issue 1908: Replace all non-standard characters in the title sort
            this.titleSort = StringTools.stringMapReplacement(new String(newTitle.substring(idx)));
            setDirty(DirtyFlag.INFO);
        }
    }

    public void setOriginalTitle(String originalTitle) {
        this.originalTitle = validateString(originalTitle, this.originalTitle);
    }

    /**
     * Get the video codec. You should use the getCodec methods instead
     *
     * @return
     * @deprecated
     */
    @Deprecated
    public String getVideoCodec() {
        for (Codec videoCodec : codecs) {
            if (videoCodec.getCodecType() == CodecType.VIDEO) {
                if (StringTools.isValidString(videoCodec.getCodecIdHint())) {
                    return videoCodec.getCodecIdHint();
                }
                if (StringTools.isValidString(videoCodec.getCodec())) {
                    return videoCodec.getCodec();
                }
                if (StringTools.isValidString(videoCodec.getCodecFormat())) {
                    return videoCodec.getCodecFormat();
                }
                if (StringTools.isValidString(videoCodec.getCodecId())) {
                    return videoCodec.getCodecId();
                }
            }
        }

        return UNKNOWN;
    }

    public void setVideoOutput(String videoOutput) {
        this.videoOutput = validateString(videoOutput, this.videoOutput);
    }

    public void setVideoSource(String videoSource) {
        this.videoSource = validateString(videoSource, this.videoSource);
    }

    public void setYear(String year) {
        this.year = validateString(year, this.year);
    }

    public void setBudget(String budget) {
        this.budget = validateString(budget, this.budget);
    }

    public String getQuote() {
        return quote;
    }

    public void setQuote(String quote) {
        this.quote = validateString(quote, this.quote);
    }

    /**
     * Validate the testString to ensure it is correct before setting the Dirty
     * INFO flag if it is different
     *
     * @param testString
     * @param currentString
     * @return
     */
    private String validateString(String testString, String currentString) {
        String newString;
        if (StringUtils.isBlank(testString)) {
            newString = UNKNOWN;
        } else {
            newString = testString;
        }

        if (!newString.equalsIgnoreCase(currentString)) {
            setDirty(DirtyFlag.INFO);
        }
        return newString;
    }

    @XmlTransient
    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    @XmlTransient
    public File getContainerFile() {
        return containerFile;
    }

    public void setContainerFile(File containerFile) {
        this.containerFile = containerFile;
    }

    public long getLastModifiedTimestamp() {
        if (!isSetMaster()) {
            synchronized (this) {
                if (lastModifiedTimeStamp == 0L) {
                    for (MovieFile mf : getMovieFiles()) {
                        lastModifiedTimeStamp = Math.max(lastModifiedTimeStamp, mf.getLastModified());
                    }
                }
                //make sure the fileDate is correct too
                addFileDate(new Date(lastModifiedTimeStamp));
                return lastModifiedTimeStamp;
            }
        } else {
            // Sets don't hold the time/date of their files, so just return the time of the file
            return getFileDate().getTime();
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[Movie ");
        for (Map.Entry e : idMap.entrySet()) {
            sb.append("[id_").append(e.getKey()).append("=").append(e.getValue()).append("]");
        }
        sb.append("[title=").append(title).append("]");
        sb.append("[titleSort=").append(titleSort).append("]");
        sb.append("[year=").append(year).append("]");
        sb.append("[releaseDate=").append(releaseDate).append("]");
        sb.append("[ratings=").append(ratings).append("]");
        sb.append("[top250=").append(top250).append("]");
        sb.append("[posterURL=").append(posterURL).append("]");
        sb.append("[bannerURL=").append(bannerURL).append("]");
        sb.append("[fanartURL=").append(fanartURL).append("]");
        sb.append("[plot=").append(plot).append("]");
        sb.append("[outline=").append(outline).append("]");
        sb.append("[director=").append(directors.toString()).append("]");
        sb.append("[country=").append(country).append("]");
        sb.append("[company=").append(company).append("]");
        sb.append("[runtime=").append(runtime).append("]");
        sb.append("[season=").append(getSeason()).append("]");
        sb.append("[language=").append(language).append("]");
        sb.append("[subtitles=").append(subtitles).append("]");
        sb.append("[container=").append(container).append("]"); // AVI, MKV, TS, etc.
        sb.append("[codecs=").append(codecs.toString()).append("]");
        sb.append("[resolution=").append(resolution).append("]"); // 1280x528
        sb.append("[videoSource=").append(videoSource).append("]");
        sb.append("[videoOutput=").append(videoOutput).append("]");
        sb.append("[fps=").append(fps).append("]");
        sb.append("[certification=").append(certification).append("]");
        sb.append("[cast=").append(cast).append("]");
        sb.append("[writers=").append(writers).append("]");
        sb.append("[genres=").append(genres).append("]");
        sb.append("[libraryDescription=").append(libraryDescription).append("]");
        sb.append("[prebuf=").append(prebuf).append("]");
        sb.append("]");
        return sb.toString();
    }

    /**
     * Sets the "extra" flag to mark this file as an extra. Will trigger the
     * "dirty" setting too
     *
     * @param extra Boolean flag, true=extra file, false=normal file
     */
    public void setExtra(boolean extra) {
        setDirty(DirtyFlag.INFO);
        this.extra = extra;
        if (extra) {
            genres.clear();
        }
    }

    /**
     * This function will return true if the movie can have trailers.
     *
     * @param movie
     * @return
     */
    public boolean canHaveTrailers() {
        if (isExtra() || getMovieType().equals(Movie.TYPE_TVSHOW)) {
            return Boolean.FALSE;
        } else {
            return Boolean.TRUE;
        }
    }

    public void setTrailerExchange(Boolean trailerExchange) {
        if (this.trailerExchange != trailerExchange) {
            setDirty(DirtyFlag.INFO);
            this.trailerExchange = trailerExchange;
        }
    }

    /**
     * Set the date of the last trailers scan
     *
     * @param lastScan date of the last trailers scan
     */
    public void setTrailerLastScan(String lastScan) {
        try {
            SimpleDateFormat sdf = StringTools.getDateFormat();
            setTrailerLastScan(sdf.parse(lastScan).getTime());
        } catch (Exception error) {
            setTrailerLastScan(0);
        }
    }

    /**
     * Set the date of the last trailers scan
     *
     * @param lastScan date of the last trailers scan (milliseconds offset from
     * the Epoch)
     */
    public void setTrailerLastScan(long lastScan) {
        if (lastScan != this.trailerLastScan) {
            setDirty(DirtyFlag.INFO);
            this.trailerLastScan = lastScan;
        }
    }

    /**
     * Get the date of the last trailers scan
     *
     * @return the date of the last trailers scan (milliseconds offset from the
     * Epoch)
     */
    public long getTrailerLastScan() {
        return trailerLastScan;
    }

    /**
     * @return Boolean flag indicating if this file is an extra
     */
    @XmlAttribute(name = "isExtra")
    public boolean isExtra() {
        return extra;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#isTrailerExchange()
     */
    @XmlJavaTypeAdapter(BooleanYesNoAdapter.class)
    @Override
    public Boolean isTrailerExchange() {
        return trailerExchange;
    }

    public void setMovieType(String movieType) {
        this.movieType = validateString(movieType, this.movieType);
    }

    public String getMovieType() {
        return this.movieType;
    }

    public void setFormatType(String formatType) {
        this.formatType = validateString(formatType, this.formatType);
    }

    public String getFormatType() {
        return this.formatType;
    }

    public void setVideoType(String videoType) {
        this.videoType = validateString(videoType, this.videoType);
    }

    public String getVideoType() {
        return this.videoType;
    }

    public String getLibraryPath() {
        return libraryPath;
    }

    public void setLibraryPath(String libraryPath) {
        this.libraryPath = libraryPath;
    }

    @Deprecated
    public String getAudioChannels() {
        StringBuilder sb = new StringBuilder();
        boolean firstChannel = Boolean.TRUE;

        for (Codec codec : codecs) {
            if (codec.getCodecType().equals(CodecType.AUDIO)) {
                if (firstChannel) {
                    firstChannel = Boolean.FALSE;
                } else {
                    sb.append(SPACE_SLASH_SPACE);
                }
                sb.append(codec.getCodecChannels());
            }
        }
        return sb.toString();
    }

    @XmlTransient
    public boolean isOverrideTitle() {
        return overrideTitle;
    }

    public void setOverrideTitle(boolean overrideTitle) {
        this.overrideTitle = overrideTitle;
    }

    public int getTop250() {
        return top250;
    }

    public void setTop250(int top250) {
        if (top250 != this.top250) {
            setDirty(DirtyFlag.INFO);
            this.top250 = top250;
        }
    }

    public String getLibraryDescription() {
        return libraryDescription;
    }

    public void setLibraryDescription(String libraryDescription) {
        this.libraryDescription = validateString(libraryDescription, this.libraryDescription);
    }

    public long getPrebuf() {
        return prebuf;
    }

    public void setPrebuf(long prebuf) {
        this.prebuf = prebuf;
    }

    @XmlTransient
    public boolean isScrapeLibrary() {
        return scrapeLibrary;
    }

    public void setScrapeLibrary(boolean scrapeLibrary) {
        this.scrapeLibrary = scrapeLibrary;
    }

    public static void addSortIgnorePrefixes(String prefix) {
        SORT_IGNORE_PREFIXES.add(prefix);
    }

    /**
     * Take the passed DTO and update the movie information
     *
     * @param dto
     */
    public void mergeFileNameDTO(MovieFileNameDTO dto) {
        setTitle(dto.getTitle());
        setExtra(dto.isExtra());

        Codec tempCodec;
        if (StringTools.isValidString(dto.getAudioCodec())) {
            tempCodec = new Codec(CodecType.AUDIO);
            tempCodec.setCodec(dto.getAudioCodec());
            tempCodec.setCodecSource(CodecSource.FILENAME);
            addCodec(tempCodec);
        }

        if (StringTools.isValidString(dto.getVideoCodec())) {
            tempCodec = new Codec(CodecType.VIDEO);
            tempCodec.setCodec(dto.getVideoCodec());
            tempCodec.setCodecSource(CodecSource.FILENAME);
            addCodec(tempCodec);
        }

        setVideoSource(dto.getVideoSource());
        setContainer(dto.getContainer());
        setFps(dto.getFps() > 0 ? dto.getFps() : 60);
        setMovieIds(dto.getMovieIds());

        if (dto.getSeason() > -1) {
            // Mark the movie as a TV Show
            setMovieType(Movie.TYPE_TVSHOW);
        }

        for (MovieFileNameDTO.SetDTO set : dto.getSets()) {
            addSet(set.getTitle(), set.getIndex() >= 0 ? set.getIndex() : null);
        }
        setYear(dto.getYear() > 0 ? String.valueOf(dto.getYear()) : null);
        setLanguage(dto.getLanguages().size() > 0 ? dto.getLanguages().get(0) : null);

        if (dto.getHdResolution() != null) {
            setVideoType(TYPE_VIDEO_HD);

            // Check if the videoOutput is UNKNOWN and clear it if it is
            if (StringTools.isNotValidString(videoOutput)) {
                videoOutput = "";
            }

            switch (dto.getFps()) {
                case 23:
                    videoOutput = "1080p 23.976Hz";
                    break;
                case 24:
                    videoOutput = "1080p 24Hz";
                    break;
                case 25:
                    videoOutput = "1080p 25Hz";
                    break;
                case 29:
                    videoOutput = "1080p 29.97Hz";
                    break;
                case 30:
                    videoOutput = "1080p 30Hz";
                    break;
                case 50:
                    if (StringUtils.isNotBlank(videoOutput)) {
                        videoOutput += " ";
                    }
                    videoOutput += "50Hz";
                    break;
                case 59:
                    videoOutput = "1080p 59.94Hz";
                    break;
                case 60:
                    if (StringUtils.isNotBlank(videoOutput)) {
                        videoOutput += " ";
                    }
                    videoOutput += "60Hz";
                    break;
                default:
                    if (StringUtils.isBlank(videoOutput)) {
                        videoOutput = Movie.UNKNOWN;
                    } else {
                        videoOutput += " 60Hz";
                    }
            }
        } else {
            switch (dto.getFps()) {
                case 23:
                    videoOutput = "23p";
                    break;
                case 24:
                    videoOutput = "24p";
                    break;
                case 29:
                case 30:
                case 60:
                    videoOutput = "NTSC";
                    break;
                case 25:
                case 49:
                case 50:
                    videoOutput = "PAL";
                    break;
                default:
                    videoOutput = Movie.UNKNOWN;
                    break;
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see com.moviejukebox.model.IMovieBasicInformation#isSetMaster()
     */
    @XmlAttribute(name = "isSet")
    @Override
    public boolean isSetMaster() {
        return isSetMaster;
    }

    public void setSetMaster(boolean isSetMaster) {
        this.isSetMaster = isSetMaster;
    }

    @XmlAttribute(name = "setSize")
    public int getSetSize() {
        return this.setSize;
    }

    public void setSetSize(final int size) {
        this.setSize = size;
    }

    /**
     * Store the latest filedate for a set of movie files. Synchronized so that
     * the comparisons don't overlap
     *
     * @param fileDate
     */
    public synchronized void addFileDate(Date fileDate) {
        if (this.fileDate == null) {
            this.fileDate = fileDate;
        } else if (fileDate.after(this.fileDate)) {
            this.fileDate = fileDate;
        }
    }

    /**
     * Overwrite the file date
     *
     * @param fileDate
     */
    public synchronized void setFileDate(Date fileDate) {
        this.fileDate = fileDate;
    }

    public Date getFileDate() {
        return fileDate;
    }

    public void setFileSize(long fileSize) {
        this.fileSize = this.fileSize + fileSize;
    }

    public long getFileSize() {
        return fileSize;
    }

    public String getFileSizeString() {
        return StringTools.formatFileSize(fileSize);
    }

    public void setAspectRatio(String aspectRatio) {
        this.aspect = aspectRatio;
    }

    public String getAspectRatio() {
        return aspect;
    }

    // ***** All the graphics methods go here *****
    // ***** Posters
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getPosterURL() {
        return posterURL;
    }

    /**
     * Should be called only from ArtworkScanner. Avoid calling this inside
     * MoviePlugin Also called from MovieNFOScanner
     *
     * @param posterURL
     */
    public void setPosterURL(String posterURL) {
        if (StringTools.isValidString(posterURL)) {
            // If the artwork URL is different, then change it, otheriwse leave alone.
            if (!posterURL.equalsIgnoreCase(this.posterURL)) {
                setDirty(DirtyFlag.POSTER, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.posterURL = posterURL;
            }
        } else {
            this.posterURL = UNKNOWN;
        }
    }

    @XmlElement(name = "posterFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getPosterFilename() {
        return posterFilename;
    }

    public void setPosterFilename(String posterFilename) {
        if (StringTools.isValidString(posterFilename)) {
            this.posterFilename = posterFilename;
        } else {
            this.posterFilename = UNKNOWN;
        }
    }

    @XmlElement(name = "detailPosterFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getDetailPosterFilename() {
        return detailPosterFilename;
    }

    public void setDetailPosterFilename(String detailPosterFilename) {
        if (StringTools.isValidString(detailPosterFilename)) {
            this.detailPosterFilename = detailPosterFilename;
        } else {
            this.detailPosterFilename = UNKNOWN;
        }
    }

    // ***** Thumbnails
    @XmlElement(name = "thumbnail")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getThumbnailFilename() {
        return this.thumbnailFilename;
    }

    public void setThumbnailFilename(String thumbnailFilename) {
        if (StringTools.isValidString(thumbnailFilename)) {
            this.thumbnailFilename = thumbnailFilename;
        } else {
            this.thumbnailFilename = UNKNOWN;
        }
    }

    // ***** Footer
    @XmlElement(name = "footer")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public List getFooterFilename() {
        return this.footerFilename;
    }

    public void setFooterFilename(String footerFilename, Integer inx) {
        // Can't change the passed parameter
        String ff;
        if (StringUtils.isBlank(footerFilename)) {
            ff = UNKNOWN;
        } else {
            ff = FileTools.makeSafeFilename(footerFilename);
        }

        if (this.footerFilename.size() <= inx) {
            while (this.footerFilename.size() < inx) {
                this.footerFilename.add(UNKNOWN);
            }
            this.footerFilename.add(ff);
        } else {
            this.footerFilename.set(inx, ff);
        }
    }

    // ***** Fanart
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getFanartURL() {
        return fanartURL;
    }

    public void setFanartURL(String fanartURL) {
        if (StringTools.isValidString(fanartURL)) {
            if (!fanartURL.equalsIgnoreCase(this.fanartURL)) {
                setDirty(DirtyFlag.FANART, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);

                this.fanartURL = fanartURL;
            }
        } else {
            this.fanartURL = UNKNOWN;
        }
    }

    @XmlElement(name = "fanartFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getFanartFilename() {
        return fanartFilename;
    }

    public void setFanartFilename(String fanartFilename) {
        if (StringTools.isValidString(fanartFilename)) {
            this.fanartFilename = fanartFilename;
        } else {
            this.fanartFilename = UNKNOWN;
        }
    }

    // ***** Banners
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getBannerURL() {
        return bannerURL;
    }

    public void setBannerURL(String bannerURL) {
        if (StringTools.isValidString(bannerURL)) {
            if (!bannerURL.equalsIgnoreCase(this.bannerURL)) {
                setDirty(DirtyFlag.BANNER, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.bannerURL = bannerURL;
            }
        } else {
            this.bannerURL = UNKNOWN;
        }
    }

    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getBannerFilename() {
        return bannerFilename;
    }

    public void setBannerFilename(String bannerFilename) {
        if (StringTools.isValidString(bannerFilename)) {
            this.bannerFilename = bannerFilename;
        } else {
            this.bannerFilename = UNKNOWN;
        }
    }

    // ***** ClearLogo
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getClearLogoURL() {
        return clearLogoURL;
    }

    public void setClearLogoURL(String clearLogoURL) {
        if (StringTools.isValidString(clearLogoURL)) {
            if (!clearLogoURL.equalsIgnoreCase(this.clearLogoURL)) {
                setDirty(DirtyFlag.CLEARLOGO, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.clearLogoURL = clearLogoURL;
            }
        } else {
            this.clearArtURL = UNKNOWN;
        }
    }

    @XmlElement(name = "clearLogoFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getClearLogoFilename() {
        return clearLogoFilename;
    }

    public void setClearLogoFilename(String clearLogoFilename) {
        if (StringTools.isValidString(clearLogoFilename)) {
            this.clearLogoFilename = clearLogoFilename;
        } else {
            this.clearLogoFilename = UNKNOWN;
        }
    }

    // ***** ClearArt
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getClearArtURL() {
        return clearArtURL;
    }

    public void setClearArtURL(String clearArtURL) {
        if (StringTools.isValidString(clearArtURL)) {
            if (!clearArtURL.equalsIgnoreCase(this.clearArtURL)) {
                setDirty(DirtyFlag.CLEARART, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.clearArtURL = clearArtURL;
            }
        } else {
            this.clearArtURL = UNKNOWN;
        }
    }

    @XmlElement(name = "clearArtFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getClearArtFilename() {
        return clearArtFilename;
    }

    public void setClearArtFilename(String clearArtFilename) {
        if (StringTools.isValidString(clearArtFilename)) {
            this.clearArtFilename = clearArtFilename;
        } else {
            this.clearArtFilename = UNKNOWN;
        }
    }

    // ***** TvThumb
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getTvThumbURL() {
        return tvThumbURL;
    }

    public void setTvThumbURL(String tvThumbURL) {
        if (StringTools.isValidString(tvThumbURL)) {
            if (!tvThumbURL.equalsIgnoreCase(this.tvThumbURL)) {
                setDirty(DirtyFlag.TVTHUMB, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.tvThumbURL = tvThumbURL;
            }
        } else {
            this.tvThumbURL = UNKNOWN;
        }
    }

    @XmlElement(name = "tvThumbFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getTvThumbFilename() {
        return tvThumbFilename;
    }

    public void setTvThumbFilename(String tvThumbFilename) {
        if (StringTools.isValidString(tvThumbFilename)) {
            this.tvThumbFilename = tvThumbFilename;
        } else {
            this.tvThumbFilename = UNKNOWN;
        }
    }

    // ***** SeasonThumb
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getSeasonThumbURL() {
        return seasonThumbURL;
    }

    public void setSeasonThumbURL(String seasonThumbURL) {
        if (StringTools.isValidString(seasonThumbURL)) {
            if (!seasonThumbURL.equalsIgnoreCase(this.seasonThumbURL)) {
                setDirty(DirtyFlag.SEASONTHUMB, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.seasonThumbURL = seasonThumbURL;
            }
        } else {
            this.seasonThumbURL = UNKNOWN;
        }
    }

    @XmlElement(name = "seasonThumbFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getSeasonThumbFilename() {
        return seasonThumbFilename;
    }

    public void setSeasonThumbFilename(String seasonThumbFilename) {
        if (StringTools.isValidString(seasonThumbFilename)) {
            this.seasonThumbFilename = seasonThumbFilename;
        } else {
            this.seasonThumbFilename = UNKNOWN;
        }
    }

    // ***** MovieDisc
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getMovieDiscURL() {
        return movieDiscURL;
    }

    public void setMovieDiscURL(String movieDiscURL) {
        if (StringTools.isValidString(movieDiscURL)) {
            if (!movieDiscURL.equalsIgnoreCase(this.movieDiscURL)) {
                setDirty(DirtyFlag.MOVIEDISC, Boolean.TRUE);
                setDirty(DirtyFlag.INFO);
                this.movieDiscURL = movieDiscURL;
            }
        } else {
            this.movieDiscURL = UNKNOWN;
        }
    }

    @XmlElement(name = "movieDiscFile")
    @XmlJavaTypeAdapter(UrlCodecAdapter.class)
    public String getMovieDiscFilename() {
        return movieDiscFilename;
    }

    public void setMovieDiscFilename(String movieDiscFilename) {
        if (StringTools.isValidString(movieDiscFilename)) {
            this.movieDiscFilename = movieDiscFilename;
        } else {
            this.movieDiscFilename = UNKNOWN;
        }
    }

    // ***** END of graphics *****
    public Map getIndexes() {
        return indexes;
    }

    public void addIndex(String key, String index) {
        if (key != null && index != null) {
            indexes.put(key, index);
        }
    }

    public void setIndexes(Map indexes) {
        this.indexes = new HashMap(indexes);
    }

    @XmlTransient
    public boolean isOverrideYear() {
        return overrideYear;
    }

    public void setOverrideYear(boolean overrideYear) {
        this.overrideYear = overrideYear;
    }

    public String getTagline() {
        return tagline;
    }

    public void setTagline(String tagline) {
        this.tagline = validateString(tagline, this.tagline);
    }

    // Read the watched flag
    @XmlElement(name = "isWatched")
    public boolean isWatched() {
        // The watched NFO should override the watched file status
        return (watchedFile || watchedNFO);
    }

    @XmlTransient
    public boolean isWatchedNFO() {
        return watchedNFO;
    }

    @XmlTransient
    public boolean isWatchedFile() {
        return watchedFile;
    }

    // Set the watched flag for files
    public void setWatchedFile(boolean watched) {
        this.watchedFile = watched;
    }

    // Set the watched flag for NFO
    public void setWatchedNFO(boolean watched) {
        this.watchedNFO = watched;
    }

    @XmlElement
    public String getWatchedDateString() {
        long returnDate = getWatchedDate();

        if (returnDate == 0) {
            return Movie.UNKNOWN;
        } else {
            return new DateTime(returnDate).toString(StringTools.getDateFormatLongString());
        }
    }

    /**
     * Look at the associated movie files and return the latest date a file was
     * watched
     *
     * @return
     */
    public long getWatchedDate() {
        long returnDate = 0;

        for (MovieFile mf : movieFiles) {
            if (mf.isWatched() && mf.getWatchedDate() > returnDate) {
                returnDate = mf.getWatchedDate();
            }
        }

        return returnDate;
    }

    /**
     * @return the artwork
     */
    public Set getArtwork() {
        return artwork;
    }

    /**
     * @param artwork the artwork to set
     */
    public void setArtwork(Set artwork) {
        this.artwork = artwork;
    }

    public void addArtwork(Artwork artwork) {
        //TODO: Check to see if the artwork source/type/url exists and add to it rather than overwrite or append to the list
        this.artwork.add(artwork);
    }

    /**
     * Check to see if an artwork already exists.
     *
     * @param artwork
     * @return
     */
    @SuppressWarnings("unused")
    private Artwork artworkExists(Artwork artwork) {
        for (Artwork artworkTest : getArtwork(artwork.getType())) {
            if (artworkTest.equals(artwork)) {
                return artworkTest;
            }
        }
        return null;
    }

    public Collection getArtwork(ArtworkType artworkType) {
        Collection artworkList = new LinkedHashSet();

        for (Artwork tempArtwork : this.artwork) {
            if (tempArtwork.getType() == artworkType) {
                artworkList.add(tempArtwork);
            }
        }

        return artworkList;
    }

    public String getShowStatus() {
        return showStatus;
    }

    public void setShowStatus(String showStatus) {
        this.showStatus = showStatus;
    }

    public void setMovieScanner(MovieDatabasePlugin movieScanner) {
        if (movieScanner != null) {
            this.movieScanner = movieScanner;
        }
    }

    @XmlTransient
    public MovieDatabasePlugin getMovieScanner() {
        return movieScanner;
    }

    /**
     * Copy the movie
     *
     * @param aMovie
     * @return
     */
    public static Movie newInstance(Movie aMovie) {
        Movie newMovie = new Movie();

        newMovie.baseName = aMovie.baseName;
        newMovie.baseFilename = aMovie.baseFilename;

        newMovie.title = aMovie.title;
        newMovie.titleSort = aMovie.titleSort;
        newMovie.originalTitle = aMovie.originalTitle;
        newMovie.year = aMovie.year;
        newMovie.releaseDate = aMovie.releaseDate;
        newMovie.plot = aMovie.plot;
        newMovie.outline = aMovie.outline;
        newMovie.quote = aMovie.quote;
        newMovie.tagline = aMovie.tagline;
        newMovie.country = aMovie.country;
        newMovie.company = aMovie.company;
        newMovie.runtime = aMovie.runtime;
        newMovie.language = aMovie.language;
        newMovie.videoType = aMovie.videoType;
        newMovie.subtitles = aMovie.subtitles;
        newMovie.container = aMovie.container;
        newMovie.resolution = aMovie.resolution;
        newMovie.aspect = aMovie.aspect;
        newMovie.videoSource = aMovie.videoSource;
        newMovie.videoOutput = aMovie.videoOutput;
        newMovie.fps = aMovie.fps;
        newMovie.certification = aMovie.certification;
        newMovie.showStatus = aMovie.showStatus;
        newMovie.scrapeLibrary = aMovie.scrapeLibrary;
        newMovie.extra = aMovie.extra;
        newMovie.trailerExchange = aMovie.trailerExchange;
        newMovie.trailerLastScan = aMovie.trailerLastScan;
        newMovie.libraryPath = aMovie.libraryPath;
        newMovie.movieType = aMovie.movieType;
        newMovie.formatType = aMovie.formatType;
        newMovie.overrideTitle = aMovie.overrideTitle;
        newMovie.overrideYear = aMovie.overrideYear;
        newMovie.top250 = aMovie.top250;
        newMovie.libraryDescription = aMovie.libraryDescription;
        newMovie.prebuf = aMovie.prebuf;
        newMovie.posterURL = aMovie.posterURL;
        newMovie.posterFilename = aMovie.posterFilename;
        newMovie.detailPosterFilename = aMovie.detailPosterFilename;
        newMovie.thumbnailFilename = aMovie.thumbnailFilename;
        newMovie.fanartURL = aMovie.fanartURL;
        newMovie.fanartFilename = aMovie.fanartFilename;
        newMovie.bannerURL = aMovie.bannerURL;
        newMovie.bannerFilename = aMovie.bannerFilename;
        newMovie.clearArtURL = aMovie.clearArtURL;
        newMovie.clearArtFilename = aMovie.clearArtFilename;
        newMovie.clearLogoURL = aMovie.clearLogoURL;
        newMovie.clearLogoFilename = aMovie.clearLogoFilename;
        newMovie.tvThumbURL = aMovie.tvThumbURL;
        newMovie.tvThumbFilename = aMovie.tvThumbFilename;
        newMovie.seasonThumbURL = aMovie.seasonThumbURL;
        newMovie.seasonThumbFilename = aMovie.seasonThumbFilename;
        newMovie.movieDiscURL = aMovie.movieDiscURL;
        newMovie.movieDiscFilename = aMovie.movieDiscFilename;
        newMovie.fileDate = aMovie.fileDate;
        newMovie.fileSize = aMovie.fileSize;
        newMovie.watchedFile = aMovie.watchedFile;
        newMovie.watchedNFO = aMovie.watchedNFO;
        newMovie.first = aMovie.first;
        newMovie.previous = aMovie.first;
        newMovie.next = aMovie.next;
        newMovie.last = aMovie.last;
        newMovie.file = aMovie.file;
        newMovie.containerFile = aMovie.containerFile;
        newMovie.isSetMaster = aMovie.isSetMaster;
        newMovie.setSize = aMovie.setSize;

        newMovie.idMap = new HashMap(aMovie.idMap);
        newMovie.ratings = new HashMap(aMovie.ratings);
        newMovie.directors = new LinkedHashSet(aMovie.directors);
        newMovie.sets = new HashMap(aMovie.sets);
        newMovie.genres = new TreeSet(aMovie.genres);
        newMovie.cast = new LinkedHashSet(aMovie.cast);
        newMovie.writers = new LinkedHashSet(aMovie.writers);
        newMovie.awards = new ArrayList(aMovie.awards);
        newMovie.people = new ArrayList(aMovie.people);
        newMovie.artwork = new LinkedHashSet(aMovie.artwork);
        newMovie.indexes = new HashMap(aMovie.indexes);
        newMovie.movieFiles = new TreeSet(aMovie.movieFiles);
        newMovie.extraFiles = new TreeSet(aMovie.extraFiles);
        newMovie.dirtyFlags = EnumSet.copyOf(aMovie.dirtyFlags);
        newMovie.codecs = new LinkedHashSet(aMovie.codecs);
        newMovie.footerFilename = new ArrayList(aMovie.footerFilename);

        return newMovie;
    }

    public Set getCodecs() {
        return codecs;
    }

    public void setCodecs(Set codecs) {
        this.codecs = codecs;
        setDirty(DirtyFlag.INFO);
    }

    /**
     * Add a codec to the video file.
     *
     * @param newCodec
     */
    public void addCodec(final Codec newCodec) {
        // Check to see if the codec already exists
        boolean alreadyExists = Boolean.FALSE;
        // Store the codecs to delete in an array to prevent a concurent modification exception
        ArrayList codecsToDelete = new ArrayList();

        for (Codec existingCodec : codecs) {
            if (existingCodec.getCodecType() != newCodec.getCodecType()) {
                // Codecs are not the same type.
                continue;
            }

            // Checks to see if the Type, Codec, Language and Channels are the same
            if (existingCodec.equals(newCodec)) {
                // Check to see if the codec is better than an existing one
                if (existingCodec.getCodecSource().isBetter(newCodec.getCodecSource())) {
                    // Found an existing codec which is better
                    alreadyExists = Boolean.TRUE;
                    break;
                } else {
                    // Found an existing codec, but newer is better source
                    codecsToDelete.add(existingCodec);
                }

                break;
            }
        }

        // Delete codecs if it is not required
        for (Codec codecToDelete : codecsToDelete) {
            codecs.remove(codecToDelete);
            setDirty(DirtyFlag.INFO);
        }
        codecsToDelete.clear();

        // If the codec does not exist, add it to the list
        if (!alreadyExists) {
            this.codecs.add(newCodec);
            setDirty(DirtyFlag.INFO);
        }
    }

    public static List getSortIgnorePrefixes() {
        return SORT_IGNORE_PREFIXES;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy