package org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi; import java.io.IOException; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URLEncoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import org.lodder.subtools.multisubdownloader.subtitleproviders.podnapisi.model.PodnapisiSubtitleDescriptor; import org.lodder.subtools.sublibrary.Manager; import org.lodder.subtools.sublibrary.ManagerException; import org.lodder.subtools.sublibrary.ManagerSetupException; import org.lodder.subtools.sublibrary.data.XmlRPC; import org.lodder.subtools.sublibrary.xml.XMLHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class JPodnapisiApi extends XmlRPC { private Date lastCheck; private Manager manager; public static final int maxAge = 90000; private static final Logger LOGGER = LoggerFactory.getLogger(JPodnapisiApi.class); public JPodnapisiApi(String useragent, Manager manager) { super(useragent, "http://ssp.podnapisi.net:8000/RPC2/"); this.manager = manager; } private void login() throws Exception { Map<?, ?> response = invoke("initiate", new String[] {getUserAgent()}); setToken(response.get("session").toString()); String nonce = response.get("nonce").toString(); String username = "jbiersubsdownloader"; String password = "jbiersubsdownloader3"; try { final MessageDigest md = MessageDigest.getInstance("MD5"); md.reset(); final MessageDigest sha = MessageDigest.getInstance("SHA-256"); sha.reset(); final byte[] md5Digest = md.digest(password.getBytes("UTF-8")); final BigInteger md5Number = new BigInteger(1, md5Digest); final String md5String = md5Number.toString(16); sha.update(md5String.getBytes("UTF-8")); sha.update(nonce.getBytes("UTF-8")); final BigInteger shaNumber = new BigInteger(1, sha.digest()); final String shaString = shaNumber.toString(16); Map<?, ?> responseLogin = invoke("authenticate", new String[] {getToken(), username, shaString}); lastCheck = new Date(System.currentTimeMillis() + maxAge); if (!responseLogin.get("status").toString().equals("200")) setToken(null); } catch (NoSuchAlgorithmException e) { LOGGER.error("API PODNAPISI login", e); setToken(null); } } public boolean isLoggedOn() { return this.getToken() != null; } @SuppressWarnings("unchecked") public List<PodnapisiSubtitleDescriptor> searchSubtitles(String[] filehash, String sublanguageid) throws Exception { checkLoginStatus(); List<PodnapisiSubtitleDescriptor> subtitles = new ArrayList<PodnapisiSubtitleDescriptor>(); Map<String, List<Map<String, String>>> response = (Map<String, List<Map<String, String>>>) invoke("search", new Object[] {getToken(), filehash}); try { List<Map<String, String>> subtitleData = (response.get("subtitles") == null) ? new ArrayList<Map<String, String>>() : (List<Map<String, String>>) response.get("subtitles"); for (Map<String, String> subtitle : subtitleData) { if (subtitle.get("LanguageCode").equals(PODNAPISI_LANGS.get(sublanguageid))) subtitles.add(parsePodnapisiSubtitle(subtitle)); } } catch (Exception e) { LOGGER.error("API PODNAPISI searchSubtitles", e); } return subtitles; } public List<PodnapisiSubtitleDescriptor> searchSubtitles(String filename, int year, int season, int episode, String sublanguageid) throws IOException { List<PodnapisiSubtitleDescriptor> subtitles = new ArrayList<PodnapisiSubtitleDescriptor>(); String url = "http://www.podnapisi.net/sl/ppodnapisi/search?sK=" + URLEncoder.encode(filename, "UTF-8") + "&sJ=" + PODNAPISI_LANGS.get(sublanguageid); if (year > 0) url = url + "&sY=" + year; if (season > 0) { url = url + "&sTS=" + season + "&sT=1"; // series } else { url = url + "&sT=0"; // movies } if (episode > 0) url = url + "&sTE=" + episode; url = url + "&sXML=1"; Document doc = getXML(url); if (doc != null) { NodeList nList = doc.getElementsByTagName("subtitle"); for (int i = 0; i < nList.getLength(); i++) { if (nList.item(i).getNodeType() == Node.ELEMENT_NODE && nList.item(i).getNodeName().equals("subtitle")) { Element eElement = (Element) nList.item(i); subtitles.add(parsePodnapisiSubtitle(eElement)); } } } return subtitles; } @SuppressWarnings("unchecked") public String download(String subtitleId) throws Exception { checkLoginStatus(); Map<?, ?> response = invoke("download", new Object[] {getToken(), subtitleId}); try { List<Map<String, String>> data = (List<Map<String, String>>) response.get("names"); return "http://www.podnapisi.net/static/podnapisi/" + data.get(0).get("filename"); } catch (Exception e) { LOGGER.error("API PODNAPISI download", e); } return null; } public String downloadUrl(String subtitleId) throws ManagerSetupException, ManagerException { String url = "http://simple.podnapisi.net/en/ondertitels-p" + subtitleId; String xml = manager.getContent(url, getUserAgent(), false); int downloadStartIndex = xml.indexOf("/download"); int startIndex = 0; if (downloadStartIndex > 0) { //get starting point of string for (int i = downloadStartIndex; i > 0; i--){ if (xml.charAt(i) == '='){ startIndex = i; break; } } url = xml.substring(startIndex + 2, downloadStartIndex + 9); return "http://www.podnapisi.net/" + url; } else { LOGGER.error( "Download URL for subtitleID {} can't be found, set to debug for more information!", subtitleId); LOGGER.debug("The URL {}", url); LOGGER.debug("The Page {}", xml); return null; } } private void checkLoginStatus() throws Exception { if (!isLoggedOn()) { login(); } else { keepAlive(); } } private void keepAlive() throws Exception { final Date current = new Date(); if (lastCheck.compareTo(current) < 0) { invoke("keepalive", new Object[] {getToken()}); lastCheck = new Date(System.currentTimeMillis() + maxAge); } } protected Document getXML(String url) { try { return XMLHelper.getDocument(manager.getContent(url, getUserAgent(), false)); } catch (MalformedURLException e) { LOGGER.error("API PODNAPISI getXML", e); } catch (ParserConfigurationException e) { LOGGER.error("API PODNAPISI getXML", e); } catch (IOException e) { LOGGER.error("API PODNAPISI getXML", e); } catch (Exception e) { LOGGER.error("API PODNAPISI getXML", e); } return null; } private PodnapisiSubtitleDescriptor parsePodnapisiSubtitle(Element eElement) { PodnapisiSubtitleDescriptor psd = new PodnapisiSubtitleDescriptor(); psd.setFlagsString(XMLHelper.getStringTagValue("flags", eElement)); psd.setLanguageCode(XMLHelper.getStringTagValue("languageId", eElement)); psd.setMatchRanking(XMLHelper.getStringTagValue("rating", eElement)); psd.setReleaseString(XMLHelper.getStringTagValue("release", eElement)); psd.setSubtitleId(XMLHelper.getStringTagValue("id", eElement)); psd.setSubtitleRating(XMLHelper.getStringTagValue("rating", eElement)); psd.setUploaderName(XMLHelper.getStringTagValue("uploaderName", eElement)); psd.setUploaderUid(XMLHelper.getStringTagValue("uploaderId", eElement)); return psd; } private PodnapisiSubtitleDescriptor parsePodnapisiSubtitle(Map<String, String> subtitle) { PodnapisiSubtitleDescriptor psd = new PodnapisiSubtitleDescriptor(); psd.setFlagsString(subtitle.get("FlagsString")); // psd.setInexact(subtitle.get("Inexact")); psd.setLanguageCode(subtitle.get("LanguageCode")); psd.setMatchRanking(subtitle.get("MatchRanking")); psd.setReleaseString(subtitle.get("ReleaseString")); psd.setSubtitleId(subtitle.get("SubtitleId")); psd.setSubtitleRating(subtitle.get("SubtitleRating")); psd.setUploaderName(subtitle.get("UploaderName")); psd.setUploaderUid(subtitle.get("UploaderUid")); return psd; } private static final Map<String, String> PODNAPISI_LANGS = Collections .unmodifiableMap(new HashMap<String, String>() { /** * */ private static final long serialVersionUID = 2950169212654074275L; { put("sl", "1"); put("en", "2"); put("no", "3"); put("ko", "4"); put("de", "5"); put("is", "6"); put("cs", "7"); put("fr", "8"); put("it", "9"); put("bs", "10"); put("ja", "11"); put("ar", "12"); put("ro", "13"); put("es-ar", "14"); put("hu", "15"); put("el", "16"); put("zh", "17"); put("lt", "19"); put("et", "20"); put("lv", "21"); put("he", "22"); put("nl", "23"); put("da", "24"); put("se", "25"); put("pl", "26"); put("ru", "27"); put("es", "28"); put("sq", "29"); put("tr", "30"); put("fi", "31"); put("pt", "32"); put("bg", "33"); put("mk", "35"); put("sk", "37"); put("hr", "38"); put("zh", "40"); put("hi", "42"); put("th", "44"); put("uk", "46"); put("sr", "47"); put("pt-br", "48"); put("ga", "49"); put("be", "50"); put("vi", "51"); put("fa", "52"); put("ca", "53"); put("id", "54"); } }); }