Java tutorial
/* * Copyright 2012 - 2016 Manuel Laggner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.tinymediamanager.core.tvshow.entities; import static org.tinymediamanager.core.Constants.ACTORS; import static org.tinymediamanager.core.Constants.AIRED_EPISODE; import static org.tinymediamanager.core.Constants.AIRED_SEASON; import static org.tinymediamanager.core.Constants.DIRECTOR; import static org.tinymediamanager.core.Constants.DISPLAY_EPISODE; import static org.tinymediamanager.core.Constants.DISPLAY_SEASON; import static org.tinymediamanager.core.Constants.DVD_EPISODE; import static org.tinymediamanager.core.Constants.DVD_ORDER; import static org.tinymediamanager.core.Constants.DVD_SEASON; import static org.tinymediamanager.core.Constants.EPISODE; import static org.tinymediamanager.core.Constants.FIRST_AIRED; import static org.tinymediamanager.core.Constants.FIRST_AIRED_AS_STRING; import static org.tinymediamanager.core.Constants.MEDIA_SOURCE; import static org.tinymediamanager.core.Constants.SEASON; import static org.tinymediamanager.core.Constants.SEASON_POSTER; import static org.tinymediamanager.core.Constants.TAG; import static org.tinymediamanager.core.Constants.TAGS_AS_STRING; import static org.tinymediamanager.core.Constants.TITLE_FOR_UI; import static org.tinymediamanager.core.Constants.TVDB; import static org.tinymediamanager.core.Constants.TV_SHOW; import static org.tinymediamanager.core.Constants.WATCHED; import static org.tinymediamanager.core.Constants.WRITER; import java.io.File; import java.nio.file.Path; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.UUID; import java.util.Map.Entry; import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tinymediamanager.core.MediaEntityImageFetcherTask; import org.tinymediamanager.core.MediaFileType; import org.tinymediamanager.core.MediaSource; import org.tinymediamanager.core.Utils; import org.tinymediamanager.core.entities.MediaEntity; import org.tinymediamanager.core.entities.MediaFile; import org.tinymediamanager.core.threading.TmmTaskManager; import org.tinymediamanager.core.tvshow.TvShowList; import org.tinymediamanager.core.tvshow.TvShowMediaFileComparator; import org.tinymediamanager.core.tvshow.TvShowModuleManager; import org.tinymediamanager.core.tvshow.connector.TvShowEpisodeToXbmcNfoConnector; import org.tinymediamanager.scraper.MediaMetadata; import org.tinymediamanager.scraper.entities.MediaArtwork; import org.tinymediamanager.scraper.entities.MediaCastMember; import org.tinymediamanager.scraper.entities.MediaArtwork.MediaArtworkType; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; /** * The Class TvShowEpisode. * * @author Manuel Laggner */ public class TvShowEpisode extends MediaEntity implements Comparable<TvShowEpisode> { private static final Logger LOGGER = LoggerFactory.getLogger(TvShowEpisode.class); private static final Comparator<MediaFile> MEDIA_FILE_COMPARATOR = new TvShowMediaFileComparator(); @JsonProperty private int episode = -1; @JsonProperty private int season = -1; @JsonProperty private int dvdSeason = -1; @JsonProperty private int dvdEpisode = -1; @JsonProperty private int displaySeason = -1; @JsonProperty private int displayEpisode = -1; @JsonProperty @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private Date firstAired = null; @JsonProperty private String director = ""; @JsonProperty private String writer = ""; @JsonProperty private boolean disc = false; @JsonProperty private boolean multiEpisode = false; @JsonProperty private boolean watched = false; @JsonProperty private boolean subtitles = false; @JsonProperty private boolean isDvdOrder = false; @JsonProperty private UUID tvShowId = null; @JsonProperty private MediaSource mediaSource = MediaSource.UNKNOWN; // DVD, Bluray, etc @JsonProperty private List<TvShowActor> actors = new CopyOnWriteArrayList<>(); @JsonProperty private List<String> tags = new CopyOnWriteArrayList<>(); private TvShow tvShow = null; private Date lastWatched = null; /** * Instantiates a new tv show episode. To initialize the propertychangesupport after loading */ public TvShowEpisode() { // register for dirty flag listener super(); } @Override protected Comparator<MediaFile> getMediaFileComparator() { return MEDIA_FILE_COMPARATOR; } /** * (re)sets the path (when renaming tv show/season folder).<br> * Exchanges the beginning path from oldPath with newPath<br> */ public void replacePathForRenamedFolder(Path oldPath, Path newPath) { String p = getPathNIO().toAbsolutePath().toString(); p = p.replace(oldPath.toAbsolutePath().toString(), newPath.toAbsolutePath().toString()); setPath(p); } /** * create a deep copy of this episode * * @param source * the source episode */ public TvShowEpisode(TvShowEpisode source) { // the reference to the tv show and the media files are the only things we don't copy tvShow = source.tvShow; // clone media files for (MediaFile mf : source.getMediaFiles()) { addToMediaFiles(new MediaFile(mf)); } // clone the rest path = new String(source.path); title = new String(source.title); originalTitle = new String(source.originalTitle); year = new String(source.year); plot = new String(source.plot); rating = source.rating; for (Entry<MediaFileType, String> entry : source.artworkUrlMap.entrySet()) { artworkUrlMap.put(entry.getKey(), new String(entry.getValue())); } dateAdded = new Date(source.dateAdded.getTime()); scraped = source.scraped; ids.putAll(source.ids); episode = source.episode; season = source.season; dvdEpisode = source.dvdEpisode; dvdSeason = source.dvdSeason; isDvdOrder = source.isDvdOrder; if (source.firstAired != null) { firstAired = new Date(source.firstAired.getTime()); } director = source.director; writer = source.writer; disc = source.disc; watched = source.watched; votes = source.votes; subtitles = source.subtitles; actors.addAll(source.actors); } public Date getFirstAired() { return firstAired; } @JsonIgnore public void setFirstAired(Date newValue) { Date oldValue = this.firstAired; this.firstAired = newValue; firePropertyChange(FIRST_AIRED, oldValue, newValue); firePropertyChange(FIRST_AIRED_AS_STRING, oldValue, newValue); } public TvShowSeason getTvShowSeason() { if (tvShow == null) { return null; } return tvShow.getSeasonForEpisode(this); } public void setTvShowSeason() { // dummy for beansbinding } /** * Is this episode in a disc folder structure?. * * @return true/false */ public boolean isDisc() { return disc; } /** * This episode is in a disc folder structure. * * @param disc * true/false */ public void setDisc(boolean disc) { this.disc = disc; } /** * is this Episode a MultiEpisode? (same files added to another episode?) * * @return true/false */ public boolean isMultiEpisode() { return multiEpisode; } public void setMultiEpisode(boolean multiEpisode) { this.multiEpisode = multiEpisode; } /** * first aired date as yyyy-mm-dd<br> * https://xkcd.com/1179/ :P * * @return the date or empty string */ public String getFirstAiredFormatted() { if (this.firstAired == null) { return ""; } return new SimpleDateFormat("yyyy-MM-dd").format(this.firstAired); } /** * Gets the first aired as a string, formatted in the system locale. * * @return the first aired as string */ public String getFirstAiredAsString() { if (this.firstAired == null) { return ""; } return SimpleDateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(firstAired); } /** * convenient method to set the first aired date (parsed from string). * * @param aired * the new first aired * @throws ParseException * if string cannot be parsed! */ public void setFirstAired(String aired) throws ParseException { Pattern date = Pattern.compile("([0-9]{2})[_\\.-]([0-9]{2})[_\\.-]([0-9]{4})"); Matcher m = date.matcher(aired); if (m.find()) { this.firstAired = new SimpleDateFormat("dd-MM-yyyy") .parse(m.group(1) + "-" + m.group(2) + "-" + m.group(3)); } else { date = Pattern.compile("([0-9]{4})[_\\.-]([0-9]{2})[_\\.-]([0-9]{2})"); m = date.matcher(aired); if (m.find()) { this.firstAired = new SimpleDateFormat("yyyy-MM-dd") .parse(m.group(1) + "-" + m.group(2) + "-" + m.group(3)); } else { throw new ParseException("could not parse date from: " + aired, 0); } } } public TvShow getTvShow() { return tvShow; } public void setTvShow(TvShow newValue) { TvShow oldValue = this.tvShow; this.tvShow = newValue; this.tvShowId = tvShow.getDbId(); firePropertyChange(TV_SHOW, oldValue, newValue); } public UUID getTvShowDbId() { return tvShowId; } public int getEpisode() { if (isDvdOrder) { return dvdEpisode; } return episode; } public int getSeason() { if (isDvdOrder) { return dvdSeason; } return season; } public void setEpisode(int newValue) { if (isDvdOrder) { setDvdEpisode(newValue); } else { setAiredEpisode(newValue); } firePropertyChange(TITLE_FOR_UI, "", newValue); } public void setAiredEpisode(int newValue) { int oldValue = this.episode; this.episode = newValue; if (!isDvdOrder) { firePropertyChange(EPISODE, oldValue, newValue); } firePropertyChange(AIRED_EPISODE, oldValue, newValue); } public int getAiredEpisode() { return this.episode; } @Override public void setTitle(String newValue) { super.setTitle(newValue); firePropertyChange(TITLE_FOR_UI, "", newValue); } public void setSeason(int newValue) { if (isDvdOrder) { setDvdSeason(newValue); } else { setAiredSeason(newValue); } firePropertyChange(TITLE_FOR_UI, "", newValue); } public void setAiredSeason(int newValue) { int oldValue = this.season; this.season = newValue; if (!isDvdOrder) { firePropertyChange(SEASON, oldValue, newValue); } firePropertyChange(AIRED_SEASON, oldValue, newValue); } public int getAiredSeason() { return this.season; } /** * Gets the title for ui. * * @return the title for ui */ public String getTitleForUi() { StringBuilder titleForUi = new StringBuilder(); int episode = getEpisode(); int season = getSeason(); if (episode > 0 && season > 0) { titleForUi.append(String.format("S%02dE%02d - ", season, episode)); } titleForUi.append(title); return titleForUi.toString(); } /** * Initialize after loading. */ public void initializeAfterLoading() { super.initializeAfterLoading(); // remove empty tag and null values Utils.removeEmptyStringsFromList(tags); } /** * Write thumb image. */ public void writeThumbImage() { String thumbUrl = getArtworkUrl(MediaFileType.THUMB); if (StringUtils.isNotEmpty(thumbUrl)) { boolean firstImage = true; // create correct filename MediaFile mf = getMediaFiles(MediaFileType.VIDEO).get(0); String filename; String basename = FilenameUtils.getBaseName(mf.getFilename()); if (isDisc()) { basename = "VIDEO_TS"; // FIXME: BluRay? } switch (TvShowModuleManager.SETTINGS.getTvShowEpisodeThumbFilename()) { case FILENAME_THUMB_POSTFIX: filename = basename + "-thumb." + FilenameUtils.getExtension(thumbUrl); break; case FILENAME_THUMB: filename = basename + "." + FilenameUtils.getExtension(thumbUrl); break; case FILENAME_THUMB_TBN: filename = basename + ".tbn"; break; default: filename = ""; break; } if (StringUtils.isBlank(thumbUrl) || StringUtils.isBlank(filename)) { return; } // get image in thread MediaEntityImageFetcherTask task = new MediaEntityImageFetcherTask(this, thumbUrl, MediaArtworkType.THUMB, filename, firstImage); TmmTaskManager.getInstance().addImageDownloadTask(task); } } /** * Sets the metadata. * * @param metadata * the new metadata */ public void setMetadata(MediaMetadata metadata) { // check against null metadata (e.g. aborted request) if (metadata == null) { LOGGER.error("metadata was null"); return; } boolean writeNewThumb = false; setTitle(metadata.getTitle()); setPlot(metadata.getPlot()); setIds(metadata.getIds()); setAiredSeason(metadata.getSeasonNumber()); setAiredEpisode(metadata.getEpisodeNumber()); setDvdSeason(metadata.getDvdSeasonNumber()); setDvdEpisode(metadata.getDvdEpisodeNumber()); setFirstAired(metadata.getReleaseDate()); setDisplaySeason(metadata.getDisplaySeasonNumber()); setDisplayEpisode(metadata.getDisplayEpisodeNumber()); setRating(metadata.getRating()); setVotes(metadata.getVoteCount()); List<TvShowActor> actors = new ArrayList<>(); String director = ""; String writer = ""; for (MediaCastMember member : metadata.getCastMembers()) { switch (member.getType()) { case ACTOR: TvShowActor actor = new TvShowActor(); actor.setName(member.getName()); actor.setCharacter(member.getCharacter()); actor.setThumbUrl(member.getImageUrl()); actors.add(actor); break; case DIRECTOR: if (!StringUtils.isEmpty(director)) { director += ", "; } director += member.getName(); break; case WRITER: if (!StringUtils.isEmpty(writer)) { writer += ", "; } writer += member.getName(); break; default: break; } } setActors(actors); setDirector(director); setWriter(writer); for (MediaArtwork ma : metadata.getFanart()) { if (ma.getType() == MediaArtworkType.THUMB) { setArtworkUrl(ma.getDefaultUrl(), MediaFileType.THUMB); writeNewThumb = true; break; } } // update DB writeNFO(); saveToDb(); // should we write a new thumb? if (writeNewThumb) { writeThumbImage(); } } /** * Write nfo. */ public void writeNFO() { List<TvShowEpisode> episodesInNfo = new ArrayList<>(1); LOGGER.debug("write nfo: " + getTvShow().getTitle() + " S" + getSeason() + "E" + getEpisode()); // worst case: multi episode in multiple files // e.g. warehouse13.s01e01e02.Part1.avi/warehouse13.s01e01e02.Part2.avi for (MediaFile mf : getMediaFiles(MediaFileType.VIDEO)) { List<TvShowEpisode> eps = new ArrayList<TvShowEpisode>( TvShowList.getInstance().getTvEpisodesByFile(tvShow, mf.getFile())); for (TvShowEpisode ep : eps) { if (!episodesInNfo.contains(ep)) { episodesInNfo.add(ep); } } } TvShowEpisodeToXbmcNfoConnector.setData(episodesInNfo); } /** * Gets the checks for nfo file. * * @return the checks for nfo file */ public Boolean getHasNfoFile() { List<MediaFile> nfos = getMediaFiles(MediaFileType.NFO); if (nfos != null && nfos.size() > 0) { return true; } return false; } /** * Gets the checks for images. * * @return the checks for images */ public Boolean getHasImages() { if (StringUtils.isNotEmpty(getArtworkFilename(MediaFileType.THUMB))) { return true; } return false; } /** * Gets the writer. * * @return the writer */ public String getWriter() { return writer; } /** * Sets the director. * * @param newValue * the new director */ public void setDirector(String newValue) { String oldValue = this.director; this.director = newValue; firePropertyChange(DIRECTOR, oldValue, newValue); } /** * Sets the writer. * * @param newValue * the new writer */ public void setWriter(String newValue) { String oldValue = this.writer; this.writer = newValue; firePropertyChange(WRITER, oldValue, newValue); } /** * Gets the director. * * @return the director */ public String getDirector() { return director; } /** * Adds the actor. * * @param obj * the obj */ public void addActor(TvShowActor obj) { // and re-set episode path to the actor if (StringUtils.isBlank(obj.getEntityRoot())) { obj.setEntityRoot(getPathNIO().toString()); } actors.add(obj); firePropertyChange(ACTORS, null, this.getActors()); } /** * Gets the actors. * * @return the actors */ public List<TvShowActor> getActors() { List<TvShowActor> allActors = new ArrayList<>(); if (tvShow != null) { allActors.addAll(tvShow.getActors()); } allActors.addAll(actors); return allActors; } public List<TvShowActor> getGuests() { List<TvShowActor> allActors = new ArrayList<>(); allActors.addAll(actors); return allActors; } /** * Removes the actor. * * @param obj * the obj */ public void removeActor(TvShowActor obj) { actors.remove(obj); firePropertyChange(ACTORS, null, this.getActors()); } /** * Sets the actors. * * @param newActors * the new actors */ @JsonSetter public void setActors(List<TvShowActor> newActors) { // two way sync of actors List<TvShowActor> tvShowActors = new ArrayList<>(); // tvShow is null while loading if (getTvShow() != null) { tvShowActors.addAll(getTvShow().getActors()); } // first add the new ones for (TvShowActor actor : newActors) { if (!tvShowActors.contains(actor) && !actors.contains(actor)) { actors.add(actor); } } // second remove unused for (int i = actors.size() - 1; i >= 0; i--) { TvShowActor actor = actors.get(i); if (!newActors.contains(actor) || tvShowActors.contains(actor)) { actors.remove(actor); } } // and re-set episode path to the actors for (TvShowActor actor : actors) { if (StringUtils.isBlank(actor.getEntityRoot())) { actor.setEntityRoot(getPathNIO().toString()); } } firePropertyChange(ACTORS, null, this.getActors()); } /** * Checks if is watched. * * @return true, if is watched */ public boolean isWatched() { return watched; } /** * Sets the watched. * * @param newValue * the new watched */ public void setWatched(boolean newValue) { boolean oldValue = this.watched; this.watched = newValue; firePropertyChange(WATCHED, oldValue, newValue); } public Date getLastWatched() { return lastWatched; } public void setLastWatched(Date lastWatched) { this.lastWatched = lastWatched; } /** * Parses the nfo. * * @param episodeFile * the episode file * @return the list */ public static List<TvShowEpisode> parseNFO(File episodeFile) { List<TvShowEpisode> episodes = new ArrayList<>(1); String filename = episodeFile.getParent() + File.separator + FilenameUtils.getBaseName(episodeFile.getName()) + ".nfo"; episodes.addAll(TvShowEpisodeToXbmcNfoConnector.getData(new File(filename))); return episodes; } /** * Parses the nfo. * * @param episodeFile * the episode mediafile * @return the list */ public static List<TvShowEpisode> parseNFO(MediaFile episodeFile) { List<TvShowEpisode> episodes = new ArrayList<>(1); episodes.addAll(TvShowEpisodeToXbmcNfoConnector.getData(episodeFile.getFile())); return episodes; } /** * Gets the media info video format (i.e. 720p). * * @return the media info video format */ public String getMediaInfoVideoFormat() { List<MediaFile> videos = getMediaFiles(MediaFileType.VIDEO); if (videos.size() > 0) { MediaFile mediaFile = videos.get(0); return mediaFile.getVideoFormat(); } return ""; } /** * Gets the media info video codec (i.e. divx) * * @return the media info video codec */ public String getMediaInfoVideoCodec() { List<MediaFile> videos = getMediaFiles(MediaFileType.VIDEO); if (videos.size() > 0) { MediaFile mediaFile = videos.get(0); return mediaFile.getVideoCodec(); } return ""; } /** * Gets the media info audio codec (i.e mp3) and channels (i.e. 6 at 5.1 sound) * * @return the media info audio codec */ public String getMediaInfoAudioCodecAndChannels() { List<MediaFile> videos = getMediaFiles(MediaFileType.VIDEO); if (videos.size() > 0) { MediaFile mediaFile = videos.get(0); return mediaFile.getAudioCodec() + "_" + mediaFile.getAudioChannels(); } return ""; } /** * Gets the images to cache. * * @return the images to cache */ public List<Path> getImagesToCache() { // get files to cache List<Path> filesToCache = new ArrayList<>(); for (MediaFile mf : new ArrayList<>(getMediaFiles())) { if (mf.isGraphic()) { filesToCache.add(mf.getFileAsPath()); } } return filesToCache; } /* * (non-Javadoc) * * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(TvShowEpisode otherTvShowEpisode) { if (getTvShow() != otherTvShowEpisode.getTvShow()) { return getTvShow().getTitle().compareTo(otherTvShowEpisode.getTvShow().getTitle()); } if (getSeason() != otherTvShowEpisode.getSeason()) { return getSeason() - otherTvShowEpisode.getSeason(); } if (getEpisode() != otherTvShowEpisode.getEpisode()) { return getEpisode() - otherTvShowEpisode.getEpisode(); } // still nothing found? wtf - maybe some of those -1/-1 eps String filename1 = ""; try { filename1 = getMediaFiles(MediaFileType.VIDEO).get(0).getFilename(); } catch (Exception ignored) { } String filename2 = ""; try { filename2 = otherTvShowEpisode.getMediaFiles(MediaFileType.VIDEO).get(0).getFilename(); } catch (Exception ignored) { } return filename1.compareTo(filename2); } public List<MediaFile> getMediaFilesContainingAudioStreams() { List<MediaFile> mediaFilesWithAudioStreams = new ArrayList<>(1); // get the audio streams from the first video file List<MediaFile> videoFiles = getMediaFiles(MediaFileType.VIDEO); if (videoFiles.size() > 0) { MediaFile videoFile = videoFiles.get(0); mediaFilesWithAudioStreams.add(videoFile); } // get all extra audio streams for (MediaFile audioFile : getMediaFiles(MediaFileType.AUDIO)) { mediaFilesWithAudioStreams.add(audioFile); } return mediaFilesWithAudioStreams; } public List<MediaFile> getMediaFilesContainingSubtitles() { List<MediaFile> mediaFilesWithSubtitles = new ArrayList<>(1); for (MediaFile mediaFile : getMediaFiles(MediaFileType.VIDEO, MediaFileType.SUBTITLE)) { if (mediaFile.hasSubtitles()) { mediaFilesWithSubtitles.add(mediaFile); } } return mediaFilesWithSubtitles; } public boolean hasSubtitles() { if (this.subtitles) { return true; // can be set in GUI } if (getMediaFiles(MediaFileType.SUBTITLE).size() > 0) { return true; } for (MediaFile mf : getMediaFiles(MediaFileType.VIDEO)) { if (mf.hasSubtitles()) { return true; } } return false; } public void setSubtitles(boolean sub) { this.subtitles = sub; } public int getRuntimeFromMediaFiles() { int runtime = 0; for (MediaFile mf : getMediaFiles(MediaFileType.VIDEO)) { runtime += mf.getDuration(); } return runtime; } public int getRuntimeFromMediaFilesInMinutes() { return getRuntimeFromMediaFiles() / 60; } @Override public synchronized void callbackForWrittenArtwork(MediaArtworkType type) { } @Override public void saveToDb() { // update/insert this episode to the database TvShowList.getInstance().persistEpisode(this); } @Override public void deleteFromDb() { // delete this episode from the database TvShowList.getInstance().removeEpisodeFromDb(this); } /** * Event to trigger a season poster changed for the UI */ void setPosterChanged() { firePropertyChange(SEASON_POSTER, null, ""); } /** * checks if this TV show has been scraped.<br> * On a fresh DB, just reading local files, everything is again "unscraped". <br> * detect minimum of filled values as "scraped" * * @return isScraped */ @Override public boolean isScraped() { if (!scraped && !plot.isEmpty() && firstAired != null && getSeason() > -1 && getEpisode() > -1) { return true; } return scraped; } /** * Adds the to tags. * * @param newTag * the new tag */ public void addToTags(String newTag) { if (StringUtils.isBlank(newTag)) { return; } for (String tag : tags) { if (tag.equals(newTag)) { return; } } tags.add(newTag); firePropertyChange(TAG, null, newTag); firePropertyChange(TAGS_AS_STRING, null, newTag); } /** * Removes the from tags. * * @param removeTag * the remove tag */ public void removeFromTags(String removeTag) { tags.remove(removeTag); firePropertyChange(TAG, null, removeTag); firePropertyChange(TAGS_AS_STRING, null, removeTag); } /** * Sets the tags. * * @param newTags * the new tags */ @JsonSetter public void setTags(List<String> newTags) { // two way sync of tags // first, add new ones for (String tag : newTags) { if (!this.tags.contains(tag)) { this.tags.add(tag); } } // second remove old ones for (int i = this.tags.size() - 1; i >= 0; i--) { String tag = this.tags.get(i); if (!newTags.contains(tag)) { this.tags.remove(tag); } } firePropertyChange(TAG, null, newTags); firePropertyChange(TAGS_AS_STRING, null, newTags); } /** * Gets the tag as string. * * @return the tag as string */ public String getTagAsString() { StringBuilder sb = new StringBuilder(); for (String tag : tags) { if (!StringUtils.isEmpty(sb)) { sb.append(", "); } sb.append(tag); } return sb.toString(); } /** * Gets the tvdb id. * * @return the tvdb id */ public String getTvdbId() { Object obj = ids.get(TVDB); if (obj == null) { return ""; } return obj.toString(); } public List<String> getTags() { return this.tags; } public int getDvdSeason() { return dvdSeason; } public void setDvdSeason(int newValue) { int oldValue = this.dvdSeason; this.dvdSeason = newValue; if (isDvdOrder) { firePropertyChange(SEASON, oldValue, newValue); } firePropertyChange(DVD_SEASON, oldValue, newValue); } public int getDvdEpisode() { return dvdEpisode; } public void setDvdEpisode(int newValue) { int oldValue = this.dvdEpisode; this.dvdEpisode = newValue; if (isDvdOrder) { firePropertyChange(EPISODE, oldValue, newValue); } firePropertyChange(DVD_EPISODE, oldValue, newValue); } public void setDisplaySeason(int newValue) { int oldValue = this.displaySeason; this.displaySeason = newValue; firePropertyChange(DISPLAY_SEASON, oldValue, newValue); } public int getDisplaySeason() { return displaySeason; } public void setDisplayEpisode(int newValue) { int oldValue = this.displayEpisode; this.displayEpisode = newValue; firePropertyChange(DISPLAY_EPISODE, oldValue, newValue); } public int getDisplayEpisode() { return displayEpisode; } /** * is this episode in DVD order? * * @return episode in DVD order */ public boolean isDvdOrder() { return isDvdOrder; } public void setDvdOrder(boolean newValue) { boolean oldValue = this.isDvdOrder; this.isDvdOrder = newValue; firePropertyChange(DVD_ORDER, oldValue, newValue); } public MediaSource getMediaSource() { return mediaSource; } public void setMediaSource(MediaSource newValue) { MediaSource oldValue = this.mediaSource; this.mediaSource = newValue; firePropertyChange(MEDIA_SOURCE, oldValue, newValue); } /** * gets the basename (without stacking) * * @return the video base name (without stacking) */ public String getVideoBasenameWithoutStacking() { MediaFile mf = getMediaFiles(MediaFileType.VIDEO).get(0); return FilenameUtils.getBaseName(mf.getFilenameWithoutStacking()); } /** * <b>PHYSICALLY</b> deletes a complete episode by moving it to datasource backup folder<br> * DS\.backup\<moviename> */ public boolean deleteFilesSafely() { boolean result = true; List<MediaFile> mediaFiles = getMediaFiles(); for (MediaFile mf : mediaFiles) { if (!mf.deleteSafely(tvShow.getDataSource())) { result = false; } } return result; } }