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

org.tinymediamanager.scraper.kodi.KodiScraper 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.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinymediamanager.scraper.MediaProviderInfo;
import org.tinymediamanager.scraper.entities.MediaType;
import org.tinymediamanager.scraper.mediaprovider.IMediaProvider;

public class KodiScraper implements IMediaProvider {
  private static final Logger          LOGGER    = LoggerFactory.getLogger(KodiScraper.class);
  private Map functions = new TreeMap<>();

  MediaType                            type;
  String                               language;
  String                               provider;
  File                                 addonFolder;
  String                               scraperXml;
  MediaProviderInfo                    providerInfo;
  List                         imports   = new ArrayList();

  @Override
  public MediaProviderInfo getProviderInfo() {
    return providerInfo;
  }

  /**
   * instantiates a new scraper and parse info from addon.xml
   * 
   * @param scraperFolder
   */
  public KodiScraper(File scraperFolder) {
    try {

      // =====================================================
      // parse addon.xml
      // =====================================================
      File info = new File(scraperFolder, "addon.xml");
      Document doc = Jsoup.parse(info, "UTF-8", "");

      Elements addon = doc.getElementsByTag("addon");

      addonFolder = scraperFolder;
      String id = addon.attr("id");
      String name = addon.attr("name");
      String version = addon.attr("version");
      String summary = "";
      String description = "";
      provider = addon.attr("provider-name");

      // TODO: parse additional info and add to description
      // 
      // 
      // all
      // 
      // 
      // 
      // 
      // 

      // parse addons for correct import
      for (Element el : doc.getElementsByTag("import")) {
        String imp = el.attr("addon");
        if (!imp.isEmpty() && imp.startsWith("metadata.common")) {
          LOGGER.debug("--> found common import: " + imp);
          imports.add(imp);
        }
      }

      // parse extensions
      for (Element el : doc.getElementsByAttribute("point")) {
        String point = el.attr("point");
        if (point.equals("xbmc.addon.metadata")) {
          Elements desc = el.getElementsByAttributeValue("lang", Locale.getDefault().getLanguage());
          if (desc.size() == 0) {
            // fallback EN
            desc = el.getElementsByAttributeValue("lang", "en");
          }
          for (Element d : desc) {
            if (d.nodeName().equals("summary")) {
              summary = d.text();
            }
            else if (d.nodeName().equals("description")) {
              description = d.text();
            }
          }
        }
        else if (point.contains("metadata.scraper")) {
          this.scraperXml = el.attr("library");
          this.language = el.attr("language");

          // more here http://wiki.xbmc.org/index.php?title=addon.xml#.3Cextension.3E
          if (point.equals("xbmc.metadata.scraper.movies")) {
            type = MediaType.MOVIE;
          }
          else if (point.equals("xbmc.metadata.scraper.tvshows")) {
            type = MediaType.TV_SHOW;
          }
          // else if (point.equals("xbmc.metadata.scraper.albums")) {
          // type = MediaType.ALBUM;
          // }
          // else if (point.equals("xbmc.metadata.scraper.artists")) {
          // type = MediaType.ARTIST;
          // }
          // else if (point.equals("xbmc.metadata.scraper.musicvideos")) {
          // type = MediaType.MUSICVIDEO;
          // }
          // else if (point.equals("xbmc.metadata.scraper.library")) {
          // type = MediaType.LIBRARY;
          // }
        }
      }
      providerInfo = new MediaProviderInfo(id, "Kodi: " + name, "

" + summary + "


" + description); providerInfo.setVersion(version); // deprecated solely for Kodi, so ok // ===================================================== // parse language files, if found // ===================================================== HashMap labelmap = new HashMap<>(); String lang = Locale.getDefault().getDisplayLanguage(Locale.ENGLISH); File langFolder = new File(scraperFolder, "resources/language/" + lang); if (!langFolder.exists()) { langFolder = new File(scraperFolder, "resources/language/English"); } File langFile = new File(langFolder, "strings.xml"); if (langFile.exists()) { // parse XML Document set = Jsoup.parse(langFile, "UTF-8", ""); Elements strings = set.getElementsByTag("string"); for (Element el : strings) { labelmap.put(el.id(), el.text()); } } else { langFile = new File(langFolder, "strings.po"); if (langFile.exists()) { // parse PO String labels = FileUtils.readFileToString(langFile); Pattern p = Pattern.compile("msgctxt \"#(.*?)\"\nmsgid \"(.*?)\"\nmsgstr \"(.*?)\""); Matcher m = p.matcher(labels); while (m.find()) { // msgctxt "#30030" // msgid "Certification prefix" // msgstr "" labelmap.put(m.group(1), m.group(3).isEmpty() ? m.group(2) : m.group(3)); } } } // ===================================================== // parse default settings and build TMM config // ===================================================== File settingsFile = new File(scraperFolder, "resources/settings.xml"); if (settingsFile.exists()) { Document set = Jsoup.parse(settingsFile, "UTF-8", ""); Elements settings = set.getElementsByTag("setting"); for (Element el : settings) { String setid = el.attr("id"); if (StringUtils.isEmpty(setid)) continue; String type = el.attr("type"); String defaultValue = el.attr("default"); String possibleValues[] = el.attr("values").split("\\|"); if (possibleValues.length == 1 && possibleValues[0].isEmpty()) { possibleValues = el.attr("lvalues").split("\\|"); // parse label values } // if it is a labelcode, replace with value for (int index = 0; index < possibleValues.length; index++) { String code = possibleValues[index]; if (code.startsWith("3") && code.length() == 5) { String labelName = labelmap.get(code); if (labelName != null) { labelName = labelName.replaceAll("\\[COLOR=(.*?)\\]", "").replaceAll("\\[/COLOR\\]", ""); possibleValues[index] = labelName; } } } String label = el.attr("label"); String labelName = labelmap.get(label); if (labelName != null) { // labelName = "" + labelName.replaceAll("\\[COLOR=(.*?)\\]", "").replaceAll("\\[/COLOR\\]", "") + ""; // nah, no html, look weird labelName = labelName.replaceAll("\\[COLOR=(.*?)\\]", "").replaceAll("\\[/COLOR\\]", ""); } // visible boolean visible = true; String vis = el.attr("visible"); if (vis.equalsIgnoreCase("false")) { visible = false; } // hidden|urlencoded boolean encrypt = false; String option = el.attr("option"); if (option.toLowerCase(Locale.ROOT).contains("hidden")) { encrypt = true; } switch (type) { case "bool": if (defaultValue.equalsIgnoreCase("true") || defaultValue.equalsIgnoreCase("false")) { this.providerInfo.getConfig().addBoolean(setid, Boolean.valueOf(defaultValue)); } else { LOGGER.warn("This is not a boolean '" + setid + "=" + defaultValue + "' - ignoring"); } break; case "select": case "labelenum": this.providerInfo.getConfig().addSelect(setid, possibleValues, defaultValue); break; case "enum": this.providerInfo.getConfig().addSelectIndex(setid, possibleValues, defaultValue); break; // case "action": case "text": this.providerInfo.getConfig().addText(setid, defaultValue, encrypt); break; default: continue; // not a know type, restart } if (!visible) { this.providerInfo.getConfig().getConfigObject(setid).setVisible(visible); } if (labelName != null) { this.providerInfo.getConfig().getConfigObject(setid).setKeyDescription(labelName); } } } // end parse settings // ===================================================== // parse Kodi saved setting values and update TMM config // ===================================================== File savedSettings = new File(KodiUtil.detectKodiUserFolder(), "userdata/addon_data/" + providerInfo.getId() + "/settings.xml"); if (savedSettings.exists()) { Document set = Jsoup.parse(savedSettings, "UTF-8", ""); Elements settings = set.getElementsByTag("setting"); for (Element el : settings) { String setid = el.attr("id"); String value = el.attr("value"); if (providerInfo.getConfig().getConfigKeyValuePairs().keySet().contains(setid)) { providerInfo.getConfig().setValue(setid, value); } } } // ===================================================== // load TMM config values // ===================================================== this.providerInfo.getConfig().load(); File logo = new File(scraperFolder, "icon.png"); if (logo.exists()) { providerInfo.setProviderLogo(logo.toURI().toURL()); } else { // new http://kodi.wiki/view/Add-on_structure#Kodi_v17_Krypton_and_up logo = new File(scraperFolder, "resources/icon.png"); if (logo.exists()) { providerInfo.setProviderLogo(logo.toURI().toURL()); } } } catch (IOException e) { e.printStackTrace(); } } /** * no dupes! */ public void addFunction(ScraperFunction func) { if (!functions.containsKey(func.getName())) { functions.put(func.getName(), func); } } public ScraperFunction getFunction(String name) { return functions.get(name); } public ScraperFunction[] getFunctions() { return functions.values().toArray(new ScraperFunction[functions.size()]); } public boolean containsFunction(String functionName) { return functions.containsKey(functionName); } public String getProvider() { return provider; } public void setProvider(String provider) { this.provider = provider; } public File getFolder() { return addonFolder; } public void setFolder(File folder) { this.addonFolder = folder; } public String getScraperXml() { return scraperXml; } public void setScraperXml(String scraperXml) { this.scraperXml = scraperXml; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((providerInfo.getId() == null) ? 0 : providerInfo.getId().hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; KodiScraper other = (KodiScraper) obj; if (providerInfo.getId() == null) { if (other.providerInfo.getId() != null) return false; } else if (!providerInfo.getId().equals(other.providerInfo.getId())) return false; return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy