Java tutorial
/* * Copyright (c) 2004-2015 YAMJ Members * https://github.com/organizations/YAMJ/teams * * This file is part of the Yet Another Media Jukebox (YAMJ). * * YAMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * YAMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with YAMJ. If not, see <http://www.gnu.org/licenses/>. * * Web: https://github.com/YAMJ/yamj-v3 * */ package org.yamj.core.service.metadata.online; import com.omertron.thetvdbapi.model.Actor; import com.omertron.thetvdbapi.model.Episode; import java.util.*; import javax.annotation.PostConstruct; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.yamj.core.configuration.ConfigServiceWrapper; import org.yamj.core.database.model.Season; import org.yamj.core.database.model.Series; import org.yamj.core.database.model.VideoData; import org.yamj.core.database.model.dto.CreditDTO; import org.yamj.core.database.model.type.JobType; import org.yamj.core.service.metadata.nfo.InfoDTO; import org.yamj.core.tools.MetadataTools; import org.yamj.core.tools.OverrideTools; import org.yamj.core.tools.web.TemporaryUnavailableException; @Service("tvdbScanner") public class TheTVDbScanner implements ISeriesScanner { public static final String SCANNER_ID = "tvdb"; private static final Logger LOG = LoggerFactory.getLogger(TheTVDbScanner.class); @Autowired private OnlineScannerService onlineScannerService; @Autowired private TheTVDbApiWrapper tvdbApiWrapper; @Autowired private ConfigServiceWrapper configServiceWrapper; @Override public String getScannerName() { return SCANNER_ID; } @PostConstruct public void init() { LOG.info("Initialize TheTVDb scanner"); // register this scanner onlineScannerService.registerSeriesScanner(this); } @Override public String getSeriesId(Series series) { return getSeriesId(series, false); } private String getSeriesId(Series series, boolean throwTempError) { String id = series.getSourceDbId(SCANNER_ID); if (StringUtils.isBlank(id)) { id = tvdbApiWrapper.getSeriesId(series.getTitle(), series.getStartYear(), throwTempError); series.setSourceDbId(SCANNER_ID, id); } return id; } @Override public ScanResult scan(Series series) { com.omertron.thetvdbapi.model.Series tvdbSeries = null; List<Actor> tvdbActors = null; try { boolean throwTempError = configServiceWrapper.getBooleanProperty("thetvdb.throwError.tempUnavailable", Boolean.TRUE); String tvdbId = getSeriesId(series, throwTempError); if (StringUtils.isBlank(tvdbId)) { LOG.debug("TVDb id not available '{}'", series.getTitle()); return ScanResult.MISSING_ID; } tvdbSeries = tvdbApiWrapper.getSeries(tvdbId, throwTempError); tvdbActors = tvdbApiWrapper.getActors(tvdbSeries.getId(), throwTempError); } catch (TemporaryUnavailableException ex) { // check retry int maxRetries = this.configServiceWrapper.getIntProperty("thetvdb.maxRetries.tvshow", 0); if (series.getRetries() < maxRetries) { return ScanResult.RETRY; } } if (tvdbSeries == null || StringUtils.isBlank(tvdbSeries.getId()) || tvdbActors == null) { LOG.error("Can't find informations for series '{}'", series.getTitle()); return ScanResult.ERROR; } series.setSourceDbId(SCANNER_ID, tvdbSeries.getId()); series.setSourceDbId(ImdbScanner.SCANNER_ID, tvdbSeries.getImdbId()); if (OverrideTools.checkOverwriteTitle(series, SCANNER_ID)) { series.setTitle(tvdbSeries.getSeriesName(), SCANNER_ID); } if (OverrideTools.checkOverwritePlot(series, SCANNER_ID)) { series.setPlot(tvdbSeries.getOverview(), SCANNER_ID); } if (OverrideTools.checkOverwriteOutline(series, SCANNER_ID)) { series.setOutline(tvdbSeries.getOverview(), SCANNER_ID); } if (StringUtils.isNumeric(tvdbSeries.getRating())) { try { series.addRating(SCANNER_ID, (int) (Float.parseFloat(tvdbSeries.getRating()) * 10)); } catch (NumberFormatException nfe) { LOG.warn("Failed to convert TVDB rating '{}' to an integer: {}", tvdbSeries.getRating(), nfe.getMessage()); } } if (OverrideTools.checkOverwriteYear(series, SCANNER_ID)) { String faDate = tvdbSeries.getFirstAired(); if (StringUtils.isNotBlank(faDate) && (faDate.length() >= 4)) { try { series.setStartYear(Integer.parseInt(faDate.substring(0, 4)), SCANNER_ID); } catch (Exception ignore) { // ignore error if year is invalid } } } if (OverrideTools.checkOverwriteGenres(series, SCANNER_ID)) { series.setGenreNames(new LinkedHashSet<>(tvdbSeries.getGenres()), SCANNER_ID); } if (OverrideTools.checkOverwriteStudios(series, SCANNER_ID)) { String studioName = StringUtils.trimToNull(tvdbSeries.getNetwork()); if (studioName != null) { Set<String> studioNames = Collections.singleton(studioName); series.setStudioNames(studioNames, SCANNER_ID); } } // CAST & CREW Set<CreditDTO> actors = new LinkedHashSet<>(); if (this.configServiceWrapper.isCastScanEnabled(JobType.ACTOR)) { for (Actor actor : tvdbActors) { actors.add(new CreditDTO(SCANNER_ID, JobType.ACTOR, actor.getName(), actor.getRole())); } } // SCAN SEASONS this.scanSeasons(series, tvdbSeries, actors); return ScanResult.OK; } private void scanSeasons(Series series, com.omertron.thetvdbapi.model.Series tvdbSeries, Set<CreditDTO> actors) { for (Season season : series.getSeasons()) { // use values from series if (OverrideTools.checkOverwriteTitle(season, SCANNER_ID)) { season.setTitle(tvdbSeries.getSeriesName(), SCANNER_ID); } if (OverrideTools.checkOverwritePlot(season, SCANNER_ID)) { season.setPlot(tvdbSeries.getOverview(), SCANNER_ID); } if (OverrideTools.checkOverwriteOutline(season, SCANNER_ID)) { season.setOutline(tvdbSeries.getOverview(), SCANNER_ID); } if (OverrideTools.checkOverwriteYear(season, SCANNER_ID)) { // get season year from minimal first aired of episodes String seriesId = season.getSeries().getSourceDbId(SCANNER_ID); Date year = this.getSeasonYear(seriesId, season.getSeason()); if (year == null) { // try first aired from series as fall-back if (StringUtils.isNotBlank(tvdbSeries.getFirstAired())) { year = MetadataTools.parseToDate(tvdbSeries.getFirstAired().trim()); } } season.setPublicationYear(MetadataTools.extractYearAsInt(year), SCANNER_ID); } // mark season as done season.setTvSeasonDone(); // scan episodes this.scanEpisodes(season, actors); } } private Date getSeasonYear(String seriesId, int season) { List<Episode> episodeList = tvdbApiWrapper.getSeasonEpisodes(seriesId, season); if (CollectionUtils.isEmpty(episodeList)) { return null; } Date yearDate = null; for (Episode episode : episodeList) { if (StringUtils.isNotBlank(episode.getFirstAired())) { Date parsedDate = MetadataTools.parseToDate(episode.getFirstAired().trim()); if (parsedDate != null) { if (yearDate == null) { yearDate = parsedDate; } else if (parsedDate.before(yearDate)) { yearDate = parsedDate; } } } } return yearDate; } private void scanEpisodes(Season season, Set<CreditDTO> actors) { if (season.isTvEpisodesScanned(SCANNER_ID)) { // nothing to do anymore return; } String seriesId = season.getSeries().getSourceDbId(SCANNER_ID); List<Episode> episodeList = tvdbApiWrapper.getSeasonEpisodes(seriesId, season.getSeason()); for (VideoData videoData : season.getVideoDatas()) { if (videoData.isTvEpisodeDone(SCANNER_ID)) { // nothing to do if already done continue; } Episode episode = findEpisode(episodeList, season.getSeason(), videoData.getEpisode()); if (episode == null) { // mark episode as not found videoData.setTvEpisodeNotFound(); } else { videoData.setSourceDbId(SCANNER_ID, episode.getId()); if (OverrideTools.checkOverwriteTitle(videoData, SCANNER_ID)) { videoData.setTitle(episode.getEpisodeName(), SCANNER_ID); } if (OverrideTools.checkOverwritePlot(videoData, SCANNER_ID)) { videoData.setPlot(episode.getOverview(), SCANNER_ID); } if (OverrideTools.checkOverwriteOutline(videoData, SCANNER_ID)) { videoData.setOutline(episode.getOverview(), SCANNER_ID); } if (OverrideTools.checkOverwriteReleaseDate(videoData, SCANNER_ID)) { Date releaseDate = MetadataTools.parseToDate(episode.getFirstAired()); videoData.setReleaseDate(releaseDate, SCANNER_ID); } // directors if (this.configServiceWrapper.isCastScanEnabled(JobType.DIRECTOR)) { for (String director : episode.getDirectors()) { videoData.addCreditDTO(new CreditDTO(SCANNER_ID, JobType.DIRECTOR, director)); } } // writers if (this.configServiceWrapper.isCastScanEnabled(JobType.WRITER)) { for (String writer : episode.getWriters()) { videoData.addCreditDTO(new CreditDTO(SCANNER_ID, JobType.WRITER, writer)); } } // actors videoData.addCreditDTOS(actors); // guest stars if (this.configServiceWrapper.isCastScanEnabled(JobType.GUEST_STAR)) { for (String guestStar : episode.getGuestStars()) { videoData.addCreditDTO(new CreditDTO(SCANNER_ID, JobType.GUEST_STAR, guestStar)); } } // mark episode as done videoData.setTvEpisodeDone(); } } } /** * Locate the specific episode from the list of episodes * * @param episodeList * @param seasonNumber * @param episodeNumber * @return */ private static Episode findEpisode(List<Episode> episodeList, int seasonNumber, int episodeNumber) { if (CollectionUtils.isEmpty(episodeList)) { return null; } for (Episode episode : episodeList) { if (episode.getSeasonNumber() == seasonNumber && episode.getEpisodeNumber() == episodeNumber) { return episode; } } return null; } @Override public boolean scanNFO(String nfoContent, InfoDTO dto, boolean ignorePresentId) { // if we already have the ID, skip the scanning of the NFO file if (!ignorePresentId && StringUtils.isNotBlank(dto.getId(SCANNER_ID))) { return Boolean.TRUE; } // scan for IMDb ID ImdbScanner.scanImdbID(nfoContent, dto, ignorePresentId); LOG.trace("Scanning NFO for TheTVDB ID"); // http://www.allocine.fr/...=XXXXX.html try { String compareString = nfoContent.toUpperCase(); int idx = compareString.indexOf("THETVDB.COM"); if (idx > -1) { int beginIdx = compareString.indexOf("&ID="); int length = 4; if (beginIdx < idx) { beginIdx = compareString.indexOf("?ID="); } if (beginIdx < idx) { beginIdx = compareString.indexOf("&SERIESID="); length = 10; } if (beginIdx < idx) { beginIdx = compareString.indexOf("?SERIESID="); length = 10; } if (beginIdx > idx) { int endIdx = compareString.indexOf("&", beginIdx + 1); String id; if (endIdx > -1) { id = compareString.substring(beginIdx + length, endIdx); } else { id = compareString.substring(beginIdx + length); } if (StringUtils.isNotBlank(id)) { String sourceId = id.trim(); dto.addId(SCANNER_ID, sourceId); LOG.debug("TheTVDB ID found in NFO: {}", sourceId); dto.addId(SCANNER_ID, sourceId); return Boolean.TRUE; } } } } catch (Exception ex) { LOG.trace("NFO scanning error", ex); } LOG.debug("No TheTVDB ID found in NFO"); return Boolean.FALSE; } }