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

org.tinymediamanager.scraper.kodi.KodiTvShowMetadataProvider Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 - 2019 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.scraper.kodi;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinymediamanager.scraper.MediaMetadata;
import org.tinymediamanager.scraper.MediaScrapeOptions;
import org.tinymediamanager.scraper.MediaSearchOptions;
import org.tinymediamanager.scraper.MediaSearchResult;
import org.tinymediamanager.scraper.entities.MediaArtwork;
import org.tinymediamanager.scraper.entities.MediaType;
import org.tinymediamanager.scraper.exceptions.MissingIdException;
import org.tinymediamanager.scraper.exceptions.ScrapeException;
import org.tinymediamanager.scraper.exceptions.UnsupportedMediaTypeException;
import org.tinymediamanager.scraper.mediaprovider.IMediaProvider;
import org.tinymediamanager.scraper.mediaprovider.ITvShowArtworkProvider;
import org.tinymediamanager.scraper.mediaprovider.ITvShowMetadataProvider;
import org.tinymediamanager.scraper.util.DOMUtils;
import org.tinymediamanager.scraper.util.StrgUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * This is the real Kodi meta data provider for TV shows
 * 
 * @author Manuel Laggner
 */
public class KodiTvShowMetadataProvider extends AbstractKodiMetadataProvider implements ITvShowMetadataProvider, ITvShowArtworkProvider {
  private static final Logger LOGGER       = LoggerFactory.getLogger(KodiTvShowMetadataProvider.class);
  private static final String EPISODEGUIDE = "episodeguide";

  public KodiTvShowMetadataProvider(KodiScraper scraper) {
    super(scraper);
  }

  @Override
  public List search(MediaSearchOptions options) throws ScrapeException {
    List results = _search(options);
    if (results.isEmpty() && options.getYear() > 0) {
      // nothing found, try w/o year
      LOGGER.info("Search found nothing, try again without year...");
      options.setYear(0);
      results = _search(options);
    }
    return results;
  }

  @Override
  public MediaMetadata getMetadata(MediaScrapeOptions options) throws ScrapeException, UnsupportedMediaTypeException {
    LOGGER.debug("Kodi: getMetadata(): " + options);
    // if (options.getResult() == null || !scraper.getProviderInfo().getId().equals(options.getResult().getProviderId())) {
    // throw new Exception("scraping with Kodi scrapers only with a prior result possible");
    // }

    switch (options.getType()) {
      case TV_SHOW:
        return _getMetadata(options);

      case TV_EPISODE:
        // called for every single episode, S/E in options...
        return getEpisodeMetadata(options);

      default:
        throw new UnsupportedMediaTypeException(options.getType());
    }
  }

  /**
   * for TV_SHOW only, called with getShowDetails
   */
  @Override
  protected void processXmlContent(String xmlDetails, MediaMetadata md, MediaSearchResult result) throws Exception {
    if (xmlDetails == null || StringUtils.isEmpty(xmlDetails)) {
      LOGGER.warn("Cannot process empty Xml Contents.");
      return;
    }

    LOGGER.debug("******* BEGIN XML ***********");
    LOGGER.debug(xmlDetails);
    LOGGER.debug("******* END XML ***********");

    Document xml = parseXmlString(xmlDetails);
    addMetadata(md, xml.getDocumentElement());

    String showId = md.getId(scraper.getProviderInfo().getId()).toString();
    // String episodeUrl = DOMUtils.getElementValue(xml.getDocumentElement(), EPISODEGUIDE);
    // might be multiple!!
    String episodeUrl = innerXml(DOMUtils.getElementByTagName(xml.getDocumentElement(), EPISODEGUIDE));
    if (StringUtils.isEmpty(episodeUrl)) {
      LOGGER.error("No Episode Data!");
    }
    else {
      KodiMetadataProvider.XML_CACHE.put(scraper.getProviderInfo().getId() + "_" + showId + "_" + "EPISODEGUIDE_URL", episodeUrl);
      md.addExtraData(EPISODEGUIDE, episodeUrl);
      result.setMetadata(md);
    }
    LOGGER.debug("MetaData: " + md.toString());
    KodiMetadataProvider.XML_CACHE.put(scraper.getProviderInfo().getId() + "_" + showId + "_" + result.getId(), xmlDetails);
  }

  private MediaMetadata getEpisodeMetadata(MediaScrapeOptions options) {
    MediaMetadata md = new MediaMetadata(scraper.getProviderInfo().getId());

    // get episode number and season number
    int seasonNr = options.getIdAsIntOrDefault(MediaMetadata.SEASON_NR, -1);
    int episodeNr = options.getIdAsIntOrDefault(MediaMetadata.EPISODE_NR, -1);

    if (seasonNr == -1 || episodeNr == -1) {
      seasonNr = options.getIdAsIntOrDefault(MediaMetadata.SEASON_NR_DVD, -1);
      episodeNr = options.getIdAsIntOrDefault(MediaMetadata.EPISODE_NR_DVD, -1);
    }

    if (seasonNr == -1 || episodeNr == -1) {
      LOGGER.warn("no aired date/season number/episode number found");
      return md; // not even date set? return
    }

    LOGGER.debug("search for S{} E{}", lz(seasonNr), lz(episodeNr));

    String showId = options.getIdAsString(scraper.getProviderInfo().getId());
    if (showId == null) {
      LOGGER.error("Could not find showId - please scrape show first!");
      return null;
    }

    // get XML - either from cache, or fetched
    String epXml = KodiMetadataProvider.XML_CACHE.get(scraper.getProviderInfo().getId() + "_" + showId + "_S" + lz(seasonNr) + "_E" + lz(episodeNr));
    if (epXml == null) {
      // get episodes list with showId
      MediaScrapeOptions mso = new MediaScrapeOptions(MediaType.TV_SHOW);
      mso.setId(scraper.getProviderInfo().getId(), showId);
      try {
        getEpisodeList(mso);
        // now it should be cached!
        epXml = KodiMetadataProvider.XML_CACHE.get(scraper.getProviderInfo().getId() + "_" + showId + "_S" + lz(seasonNr) + "_E" + lz(episodeNr));
      }
      catch (Exception e) {
        LOGGER.error("Could not fetch episodeslist!", e);
      }
    }

    Document epListDoc;
    try {
      epListDoc = parseXmlString(epXml);
      Element el = epListDoc.getDocumentElement();
      int season = DOMUtils.getElementIntValue(el, "season");
      int ep = DOMUtils.getElementIntValue(el, "epnum");
      String id = DOMUtils.getElementValue(el, "id");
      String title = DOMUtils.getElementValue(el, "title");
      KodiUrl epUrl = new KodiUrl(DOMUtils.getElementValue(el, "url"));

      LOGGER.info("Getting episode details S" + lz(season) + " E" + lz(ep) + " - " + title);
      String xmlDetails = processor.getEpisodeDetails(epUrl, id);
      LOGGER.debug("******** BEGIN EPISODE DETAILS XML ***********");
      LOGGER.debug(xmlDetails);
      LOGGER.debug("******** END EPISODE DETAILS XML ***********");

      // update again, using the episode specific data
      Document epDetailXml = parseXmlString(xmlDetails);
      Element epXmlEl = epDetailXml.getDocumentElement();

      addMetadata(md, epXmlEl);
      md.setEpisodeNumber(ep);
      md.setSeasonNumber(season);
      LOGGER.debug("MetaData: " + md.toString());

      // cache EPISODE MetaData as provideId_S00_E00
      KodiMetadataProvider.XML_CACHE.put(scraper.getProviderInfo().getId() + "_" + showId + "_S" + lz(season) + "_E" + lz(ep) + "_DETAIL",
          xmlDetails);

    }
    catch (Exception e) {
      LOGGER.error("Could not get episode details!");
    }

    return md;
  }

  @Override
  public List getEpisodeList(MediaScrapeOptions options) throws ScrapeException, MissingIdException {
    List episodeList = new ArrayList<>();

    String showId = options.getIdAsString(scraper.getProviderInfo().getId());
    if (StringUtils.isBlank(showId)) {
      LOGGER.error("Could not find showId - please scrape show first!");
      throw new MissingIdException("Could not find showId - please scrape show first!");
    }

    String episodeguide = KodiMetadataProvider.XML_CACHE.get(scraper.getProviderInfo().getId() + "_" + showId + "_" + "EPISODEGUIDE_URL");
    if (episodeguide == null || episodeguide.isEmpty()) {
      if (options.getMetadata() != null) {
        episodeguide = options.getMetadata().getExtraData(EPISODEGUIDE).toString();
      }
      // if (episodeguide == null || episodeguide.isEmpty()) {
      // no EP guide url? scrape show first!
      // _getMetadata(options); // TODO: does not work yet; need to SEARCH for url first :|
      // }
      if (episodeguide == null || episodeguide.isEmpty()) {
        LOGGER.error("Could not find episodenguid url - you wanna scrape the show first!");
        return episodeList;
      }
    }

    try {
      // get XML - either from cache, or fetched
      String epListXml = KodiMetadataProvider.XML_CACHE.get(scraper.getProviderInfo().getId() + "_" + showId + "_" + EPISODEGUIDE);
      if (epListXml == null) {
        KodiUrl url = new KodiUrl(episodeguide);
        epListXml = processor.getEpisodeList(url);
        KodiMetadataProvider.XML_CACHE.put(scraper.getProviderInfo().getId() + "_" + showId + "_" + EPISODEGUIDE, epListXml);
      }
      LOGGER.debug("******** BEGIN EPISODE LIST XML ***********");
      LOGGER.debug(epListXml);
      LOGGER.debug("******** END EPISODE LIST XML ***********");
      Document epListDoc = parseXmlString(epListXml);

      // 
      // Bender's Big Score
      // 2008-03-23
      // 1
      // 0
      // http://api.themoviedb.org/3/tv/615/season/0/episode/1?api_key=6889f6089877fd092454d00edb44a84d&language=en&append_to_response=credits,external_ids,images&include_image_language=en,en,null
      // 615|0|1
      // 

      NodeList nl = epListDoc.getElementsByTagName("episode");
      for (int i = 0; i < nl.getLength(); i++) {
        Element el = (Element) nl.item(i);

        int season = DOMUtils.getElementIntValue(el, "season");
        int ep = DOMUtils.getElementIntValue(el, "epnum");

        // cache episode XML for later details parsing
        String epXml = elementToString(el);
        if (epXml != null) {
          KodiMetadataProvider.XML_CACHE.put(scraper.getProviderInfo().getId() + "_" + showId + "_S" + lz(season) + "_E" + lz(ep), epXml);
        }

        MediaMetadata md = new MediaMetadata(scraper.getProviderInfo().getId());
        md.setEpisodeNumber(ep);
        md.setSeasonNumber(season);
        md.setTitle(DOMUtils.getElementValue(el, "title"));
        md.setId(scraper.getProviderInfo().getId(), DOMUtils.getElementValue(el, "id"));
        // String epUrl = DOMUtils.getElementValue(el, "url"); // cannot save in ME!!!
        try {
          md.setReleaseDate(StrgUtils.parseDate(DOMUtils.getElementValue(el, "aired")));
        }
        catch (Exception ignored) {
        }

        episodeList.add(md);
      }
    }
    catch (Exception e) {
      LOGGER.error("problem scraping: " + e.getMessage());
      throw new ScrapeException(e);
    }

    if (episodeList.isEmpty()) {
      LOGGER.warn("Could not find episodes - did you scrape the show recently?");
    }
    return episodeList;
  }

  @Override
  public List getPluginsForType(MediaType type) {
    return null;
  }

  @Override
  public List getArtwork(MediaScrapeOptions arg0) throws ScrapeException {
    LOGGER.debug("******* BEGIN ARTWORK XML FOR " + arg0.getArtworkType() + " ***********");
    List mas = new ArrayList<>();
    // scrape again to get Kodi XML (thank god we have a mem cachedUrl)
    try {
      MediaMetadata md = getMetadata(arg0);
      mas.addAll(md.getMediaArt(arg0.getArtworkType()));
      LOGGER.debug("******* END ARTWORK XML FOR " + arg0.getArtworkType() + " ***********");
    }
    catch (Exception e) {
      LOGGER.error("error getting artwork: " + e.getMessage());
      throw new ScrapeException(e);
    }
    return mas;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy