diff --git a/.gitignore b/.gitignore index 57963509..13810412 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,6 @@ dist/javadoc /nbproject/private/ .externalToolBuilders .idea -version.properties +/src/main/resources/version.properties /.nb-gradle/ RepoZugang.properties diff --git a/build.gradle b/build.gradle index 50302211..cba035ff 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ sourceCompatibility = 1.8 targetCompatibility = 1.8 group = 'de.mediathekview' -version = '2.3.0' +version = '2.4.0' ext { propsFile = file('src/main/resources/version.properties').absoluteFile @@ -69,20 +69,25 @@ dependencies { compile 'com.fasterxml.jackson.core:jackson-core:2.7.0' compile 'com.jidesoft:jide-oss:3.6.16' compile 'org.tukaani:xz:1.5' + compile 'com.squareup.okhttp3:okhttp:3.6.0' + compile 'fm.void.jetm:jetm:1.2.3' + } -task updateVersion << { - Properties props = loadVersionProperties() - def oldVersion = props.getProperty('VERSION') - String buildDate = new Date().format('dd.MM.yyyy HH:mm:ss') - if (!oldVersion.equals(project.version)) { - logger.lifecycle "==msearch======================" - logger.lifecycle "Version: $project.version" - logger.lifecycle "Baudatum: $buildDate" - logger.lifecycle "==msearch======================" - props.setProperty('VERSION', project.version) - props.setProperty('DATE', buildDate) - props.store(propsFile.newWriter(), null) +task updateVersion { + doLast { + Properties props = loadVersionProperties() + def oldVersion = props.getProperty('VERSION') + String buildDate = new Date().format('dd.MM.yyyy HH:mm:ss') + if (!oldVersion.equals(project.version)) { + logger.lifecycle "==msearch======================" + logger.lifecycle "Version: $project.version" + logger.lifecycle "Baudatum: $buildDate" + logger.lifecycle "==msearch======================" + props.setProperty('VERSION', project.version) + props.setProperty('DATE', buildDate) + props.store(propsFile.newWriter(), null) + } } } processResources.dependsOn updateVersion diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 6ffa2378..e36ccf89 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8409be56..187cb261 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Oct 27 18:40:24 CEST 2016 +#Sun Feb 26 14:24:27 CET 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-3.4-bin.zip diff --git a/gradlew b/gradlew index 9aa616c2..4453ccea 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -154,16 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/src/main/java/mSearch/Config.java b/src/main/java/mSearch/Config.java index 07af380b..a947b66f 100644 --- a/src/main/java/mSearch/Config.java +++ b/src/main/java/mSearch/Config.java @@ -19,12 +19,14 @@ */ package mSearch; +import java.util.concurrent.atomic.AtomicBoolean; + public class Config { public static int bandbreite = 0; // maxBandbreite in Byte private static String userAgent = null; public static boolean debug = false; // Debugmodus - private static boolean stop = false; // damit kannn das Laden gestoppt werden + private static final AtomicBoolean stop = new AtomicBoolean(false); // damit kannn das Laden gestoppt werden public static void setUserAgent(String ua) { // Useragent den der Benutzer vorgegeben hat @@ -40,19 +42,13 @@ public static String getUserAgent() { } /** - * Damit kann das Suchen abgebrochen werden - */ - public static synchronized void setStop() { - Config.stop = true; - } - - /** - * Damit kann "stop" gesetzt/rückgesetzt werden + * Damit kann "stop" gesetzt/rückgesetzt werden. + * Bei true wird die Suche abgebrochen. * * @param set */ - public static synchronized void setStop(boolean set) { - Config.stop = set; + public static void setStop(boolean set) { + stop.set(set); } /** @@ -60,7 +56,7 @@ public static synchronized void setStop(boolean set) { * * @return true/false */ - public static synchronized boolean getStop() { - return Config.stop; + public static boolean getStop() { + return stop.get(); } } diff --git a/src/main/java/mSearch/Const.java b/src/main/java/mSearch/Const.java index c36da94c..e8e557ce 100644 --- a/src/main/java/mSearch/Const.java +++ b/src/main/java/mSearch/Const.java @@ -28,33 +28,12 @@ public class Const { public static final String PROGRAMMNAME = "MSearch"; public static final String USER_AGENT_DEFAULT = Const.PROGRAMMNAME + Functions.getProgVersionString(); // MediathekView URLs -// public static final String ADRESSE_FILMLISTEN_SERVER_XML = "http://zdfmediathk.sourceforge.net/update.xml"; -// public static final String ADRESSE_FILMLISTEN_SERVER_JSON = "http://zdfmediathk.sourceforge.net/update-json.xml"; public static final String ADRESSE_FILMLISTEN_SERVER_DIFF = "http://res.mediathekview.de/diff.xml"; public static final String ADRESSE_FILMLISTEN_SERVER_AKT = "http://res.mediathekview.de/akt.xml"; -// public static final String ADRESSE_FILMLISTEN_SERVER_DIFF_RES = "http://92.51.131.172/diff.xml"; -// public static final String ADRESSE_FILMLISTEN_SERVER_AKT_RES = "http://92.51.131.172/akt.xml"; - -// public static final String DATEINAME_LISTE_FILMLISTEN = "filmlisten.xml"; - public static final String ADRESSE_PROGRAMM_VERSION = "https://res.mediathekview.de/version.xml"; - public static final String ADRESSE_DOWNLAD = "https://mediathekview.de/download/"; - public static final String ADRESSE_ANLEITUNG = "https://github.com/mediathekview/MediathekView/wiki"; - public static final String ADRESSE_VORLAGE_PROGRAMMGRUPPEN = "https://res.mediathekview.de/programmgruppen/programmgruppen.xml"; - public static final String ADRESSE_WEBSITE = "https://mediathekview.de"; - public static final String ADRESSE_FORUM = "https://forum.mediathekview.de"; // Dateien/Verzeichnisse - public static final String XML_DATEI = "mediathek.xml"; - public static final String XML_DATEI_FILME = "filme.xml"; - // - public static final int MIN_DATEI_GROESSE_FILM = 256 * 1000; //minimale Größe (256 kB) eines Films um nicht als Fehler zu gelten - public static final String KODIERUNG_UTF = "UTF-8"; - public static final String KODIERUNG_ISO15 = "ISO-8859-15"; - public static final String XML_START = "Mediathek"; - public static final int MAX_SENDER_FILME_LADEN = 2;//es können maximal soviele Filme eines Senders/Servers gleichzeitig geladen werden public static final int STRING_BUFFER_START_BUFFER = 8 * 1024 * 8; // 8 KiB public static final String FORMAT_ZIP = ".zip"; public static final String FORMAT_XZ = ".xz"; - public static final String FORMAT_JSON = ".json"; public static final String RTMP_PRTOKOLL = "rtmp"; public static final String RTMP_FLVSTREAMER = "-r "; public static final int ALTER_FILMLISTE_SEKUNDEN_FUER_AUTOUPDATE = 3 * 60 * 60; // beim Start des Programms wir die Liste geladen wenn sie älter ist als .. diff --git a/src/main/java/mSearch/daten/DatenFilm.java b/src/main/java/mSearch/daten/DatenFilm.java index 15d4d835..66eb2050 100644 --- a/src/main/java/mSearch/daten/DatenFilm.java +++ b/src/main/java/mSearch/daten/DatenFilm.java @@ -21,15 +21,12 @@ import mSearch.Const; import mSearch.tool.*; +import org.apache.commons.lang3.time.FastDateFormat; -import java.text.SimpleDateFormat; import java.util.Date; public class DatenFilm implements Comparable { - private static final GermanStringSorter sorter = GermanStringSorter.getInstance(); - private static final SimpleDateFormat sdf_datum_zeit = new SimpleDateFormat("dd.MM.yyyyHH:mm:ss"); - private static final SimpleDateFormat sdf_datum = new SimpleDateFormat("dd.MM.yyyy"); public static final String AUFLOESUNG_NORMAL = "normal"; public static final String AUFLOESUNG_HD = "hd"; public static final String AUFLOESUNG_KLEIN = "klein"; @@ -49,18 +46,14 @@ public class DatenFilm implements Comparable { public static final int FILM_ZEIT = 7; public static final int FILM_DAUER = 8; public static final int FILM_GROESSE = 9; - public static final int FILM_HD = 10; public static final int FILM_UT = 11; - public static final int FILM_BESCHREIBUNG = 12; public static final int FILM_GEO = 13;// Geoblocking public static final int FILM_URL = 14; public static final int FILM_WEBSEITE = 15; //URL der Website des Films beim Sender public static final int FILM_ABO_NAME = 16;// wird vor dem Speichern gelöscht! - public static final int FILM_URL_SUBTITLE = 17; - public static final int FILM_URL_RTMP = 18; public static final int FILM_URL_AUTH = 19;//frei für andere Sachen public static final int FILM_URL_KLEIN = 20; @@ -68,59 +61,63 @@ public class DatenFilm implements Comparable { public static final int FILM_URL_HD = 22; public static final int FILM_URL_RTMP_HD = 23; public static final int FILM_URL_HISTORY = 24; - public static final int FILM_NEU = 25; - public static final int FILM_DATUM_LONG = 26;// Datum als Long ABER Sekunden!! public static final int FILM_REF = 27;// Referenz auf this - public static final int MAX_ELEM = 28; public static final String TAG = "Filme"; public static final String TAG_JSON_LIST = "X"; - - public final String[] arr = new String[]{ - "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", "", "", "", "", - "", "", "", "", "", ""}; //ist einen Tick schneller, hoffentlich :) - public static final String[] COLUMN_NAMES = {"Nr", "Sender", "Thema", "Titel", - "", "", "Datum", "Zeit", "Dauer", "Größe [MB]", "HD", "UT", - "Beschreibung", "Geo", "Url", "Website", "Abo", - "Url Untertitel", "Url RTMP", "Url Auth", "Url Klein", "Url RTMP Klein", "Url HD", "Url RTMP HD", "Url History", "neu", - "DatumL", "Ref"}; - + "", "", "Datum", "Zeit", "Dauer", "Größe [MB]", "HD", "UT", + "Beschreibung", "Geo", "Url", "Website", "Abo", + "Url Untertitel", "Url RTMP", "Url Auth", "Url Klein", "Url RTMP Klein", "Url HD", "Url RTMP HD", "Url History", "neu", + "DatumL", "Ref"}; // neue Felder werden HINTEN angefügt!!!!! public static final int[] JSON_NAMES = {FILM_SENDER, FILM_THEMA, FILM_TITEL, - FILM_DATUM, FILM_ZEIT, FILM_DAUER, FILM_GROESSE, - FILM_BESCHREIBUNG, FILM_URL, FILM_WEBSEITE, - FILM_URL_SUBTITLE, FILM_URL_RTMP, FILM_URL_KLEIN, FILM_URL_RTMP_KLEIN, FILM_URL_HD, FILM_URL_RTMP_HD, FILM_DATUM_LONG, - FILM_URL_HISTORY, FILM_GEO, FILM_NEU}; - + FILM_DATUM, FILM_ZEIT, FILM_DAUER, FILM_GROESSE, + FILM_BESCHREIBUNG, FILM_URL, FILM_WEBSEITE, + FILM_URL_SUBTITLE, FILM_URL_RTMP, FILM_URL_KLEIN, FILM_URL_RTMP_KLEIN, FILM_URL_HD, FILM_URL_RTMP_HD, FILM_DATUM_LONG, + FILM_URL_HISTORY, FILM_GEO, FILM_NEU}; + private static final GermanStringSorter sorter = GermanStringSorter.getInstance(); + private static final FastDateFormat sdf_datum_zeit = FastDateFormat.getInstance("dd.MM.yyyyHH:mm:ss"); + private static final FastDateFormat sdf_datum = FastDateFormat.getInstance("dd.MM.yyyy"); + private static final String[] GERMAN_ONLY = { + "+++ Aus rechtlichen Gründen ist der Film nur innerhalb von Deutschland abrufbar. +++", + "+++ Aus rechtlichen Gründen ist diese Sendung nur innerhalb von Deutschland abrufbar. +++", + "+++ Aus rechtlichen Gründen ist dieses Video nur innerhalb von Deutschland abrufbar. +++", + "+++ Aus rechtlichen Gründen ist dieses Video nur innerhalb von Deutschland verfügbar. +++", + "+++ Aus rechtlichen Gründen kann das Video nur innerhalb von Deutschland abgerufen werden. +++ Due to legal reasons the video is only available in Germany.+++", + "+++ Aus rechtlichen Gründen kann das Video nur innerhalb von Deutschland abgerufen werden. +++", + "+++ Due to legal reasons the video is only available in Germany.+++", + "+++ Aus rechtlichen Gründen kann das Video nur in Deutschland abgerufen werden. +++", + "[Aus rechtlichen Günden können wir die Partie nicht als Einzelclip anbieten.]", + "+++ Aus rechtlichen Gründen ist das Video nur innerhalb von Deutschland abrufbar. +++", + "+++Aus rechtlichen Gründen kann die Sendung nur innerhalb von Deutschland abgerufen werden. +++", + "+++ Aus rechtlichen Gründen dürfen wir dieses Video nur innerhalb von Deutschland anbieten. +++", + "+++Aus rechtlichen Gründen kann dieses Video nur innerhalb von Deutschland abgerufen werden.+++" + }; + public static boolean[] spaltenAnzeigen = new boolean[MAX_ELEM]; + public final String[] arr = new String[]{ + "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", "", "", "", "", + "", "", "", "", "", ""}; //ist einen Tick schneller, hoffentlich :) public DatumFilm datumFilm = new DatumFilm(0); public long dauerL = 0; // Sekunden public Object abo = null; - public MSLong dateigroesseL; // Dateigröße in MByte - public static boolean[] spaltenAnzeigen = new boolean[MAX_ELEM]; + public MSLong dateigroesseL = new MSLong(0); // Dateigröße in MByte public int nr; private boolean neuerFilm = false; - - public boolean isNew() { - return neuerFilm; - } - - public void setNew(final boolean newFilm) { - neuerFilm = newFilm; - } + private Hash hashValueIndexAddOld = null; + private Hash hashValueUrl = null; public DatenFilm() { dateigroesseL = new MSLong(0); // Dateigröße in MByte } public DatenFilm(String ssender, String tthema, String filmWebsite, String ttitel, String uurl, String uurlRtmp, - String datum, String zeit, - long dauerSekunden, String description) { + String datum, String zeit, + long dauerSekunden, String description) { // da werden die gefundenen Filme beim Absuchen der Senderwebsites erstellt, und nur die!! - dateigroesseL = new MSLong(0); // Dateigröße in MByte arr[FILM_SENDER] = ssender; arr[FILM_THEMA] = tthema.isEmpty() ? ssender : tthema.trim(); arr[FILM_TITEL] = ttitel.isEmpty() ? tthema : ttitel.trim(); @@ -132,6 +129,65 @@ public DatenFilm(String ssender, String tthema, String filmWebsite, String ttite arr[FILM_BESCHREIBUNG] = cleanDescription(description, tthema, ttitel); // Filmlänge + checkFilmDauer(dauerSekunden); + } + + /** + * Determine file size from remote location. + */ + public void setFileSize() { + if (arr[DatenFilm.FILM_GROESSE].isEmpty()) + arr[DatenFilm.FILM_GROESSE] = FileSize.laengeString(arr[DatenFilm.FILM_URL]); + } + + public static String cleanDescription(String s, String thema, String titel) { + // die Beschreibung auf x Zeichen beschränken + + s = Functions.removeHtml(s); // damit die Beschreibung nicht unnötig kurz wird wenn es erst später gemacht wird + + for (String g : GERMAN_ONLY) { + if (s.contains(g)) { + s = s.replace(g, ""); // steht auch mal in der Mitte + } + } + if (s.startsWith(titel)) { + s = s.substring(titel.length()).trim(); + } + if (s.startsWith(thema)) { + s = s.substring(thema.length()).trim(); + } + if (s.startsWith("|")) { + s = s.substring(1).trim(); + } + if (s.startsWith("Video-Clip")) { + s = s.substring("Video-Clip".length()).trim(); + } + if (s.startsWith(titel)) { + s = s.substring(titel.length()).trim(); + } + if (s.startsWith(":") || s.startsWith(",") || s.startsWith("\n")) { + s = s.substring(1).trim(); + } + + if (s.contains("\\\"")) { // wegen " in json-Files + s = s.replace("\\\"", "\""); + } + if (s.length() > Const.MAX_BESCHREIBUNG) { + return s.substring(0, Const.MAX_BESCHREIBUNG) + "\n....."; + } else { + return s; + } + } + + public boolean isNew() { + return neuerFilm; + } + + public void setNew(final boolean newFilm) { + neuerFilm = newFilm; + } + + private void checkFilmDauer(long dauerSekunden) { if (dauerSekunden <= 0 || dauerSekunden > 3600 * 5 /* Werte über 5 Stunden */) { arr[FILM_DAUER] = ""; } else { @@ -143,13 +199,6 @@ public DatenFilm(String ssender, String tthema, String filmWebsite, String ttite } } - public static DatenFilm getDatenFilmLiveStream(String ssender, String addTitle, String urlStream, String urlWebsite) { - return new DatenFilm(ssender, ListeFilme.THEMA_LIVE, urlWebsite/* urlThema */, - ssender + addTitle + ' ' + ListeFilme.THEMA_LIVE, - urlStream, ""/*rtmpURL*/, ""/* datum */, ""/* zeit */, 0, ""); - } - - public String getUrlSubtitle() { return arr[FILM_URL_SUBTITLE]; } @@ -188,7 +237,7 @@ public String getDateigroesse(String url) { } public void setUrlHistory() { - String u = getUrl(this); + String u = getUrl(); if (u.equals(arr[DatenFilm.FILM_URL])) { arr[DatenFilm.FILM_URL_HISTORY] = ""; } else { @@ -196,7 +245,6 @@ public void setUrlHistory() { } } - public String getUrlHistory() { if (arr[DatenFilm.FILM_URL_HISTORY].isEmpty()) { return arr[DatenFilm.FILM_URL]; @@ -208,7 +256,7 @@ public String getUrlHistory() { public String getIndex() { // liefert einen eindeutigen Index für die Filmliste // URL beim KiKa und ORF ändern sich laufend! - return arr[FILM_SENDER].toLowerCase() + arr[FILM_THEMA].toLowerCase() + DatenFilm.getUrl(this); + return (arr[FILM_SENDER] + arr[FILM_THEMA]).toLowerCase() + getUrl(); } public String getIndexAddOld() { @@ -220,21 +268,28 @@ private String repl(String s) { return s.replace("-", "").replace("_", "").replace(".", "").replace(" ", "").replace(",", "").toLowerCase(); } -// public String getIndexAddOld_() { -// // liefert einen eindeutigen Index zum Anhängen einer alten Liste -// return arr[FILM_SENDER] + arr[FILM_THEMA].toLowerCase() + arr[FILM_TITEL].toLowerCase() + arr[FILM_DATUM]; //liefert zu viel Müll -// //return arr[FILM_SENDER] + arr[FILM_THEMA].toLowerCase() + arr[FILM_TITEL].toLowerCase(); -// } - public static String getUrl(DatenFilm film) { - return getUrl(film.arr[DatenFilm.FILM_SENDER], film.arr[DatenFilm.FILM_URL]); + public Hash getHashValueIndexAddOld() { + if (hashValueIndexAddOld == null) + hashValueIndexAddOld = new Hash(getIndexAddOld()); + + return hashValueIndexAddOld; + } + + public Hash getHashValueUrl() { + if (hashValueUrl == null) + hashValueUrl = new Hash(getUrl()); + + return hashValueUrl; } - private static String getUrl(String ssender, String uurl) { + public String getUrl() { // liefert die URL zum VERGLEICHEN!! String url = ""; - if (ssender.equals(Const.ORF)) { + if (arr[DatenFilm.FILM_SENDER].equals(Const.ORF)) { + final String uurl = arr[DatenFilm.FILM_URL]; try { - url = uurl.substring(uurl.indexOf("/online/") + "/online/".length()); + final String online = "/online/"; + url = uurl.substring(uurl.indexOf(online) + online.length()); if (!url.contains("/")) { Log.errorLog(915230478, "Url: " + uurl); return ""; @@ -254,7 +309,7 @@ private static String getUrl(String ssender, String uurl) { } return Const.ORF + "----" + url; } else { - return uurl; + return arr[DatenFilm.FILM_URL]; } } @@ -264,6 +319,12 @@ public boolean isHD() { return !arr[DatenFilm.FILM_URL_HD].isEmpty() || !arr[DatenFilm.FILM_URL_RTMP_HD].isEmpty(); } +// public void clean() { +// // vor dem Speichern nicht benötigte Felder löschen +// arr[FILM_NR] = ""; +// arr[FILM_ABO_NAME] = ""; +// } + public DatenFilm getCopy() { DatenFilm ret = new DatenFilm(); System.arraycopy(this.arr, 0, ret.arr, 0, arr.length); @@ -284,99 +345,100 @@ public int compareTo(DatenFilm arg0) { return ret; } - public void clean() { - // vor dem Speichern nicht benötigte Felder löschen - arr[FILM_NR] = ""; - arr[FILM_ABO_NAME] = ""; + private void preserveMemory() { +//================================ + // Speicher sparen + if (arr[DatenFilm.FILM_GROESSE].length() < 3) { + arr[DatenFilm.FILM_GROESSE] = arr[DatenFilm.FILM_GROESSE].intern(); + } + if (arr[DatenFilm.FILM_URL_KLEIN].length() < 15) { + arr[DatenFilm.FILM_URL_KLEIN] = arr[DatenFilm.FILM_URL_KLEIN].intern(); + } + + arr[DatenFilm.FILM_DATUM] = arr[DatenFilm.FILM_DATUM].intern(); + arr[DatenFilm.FILM_ZEIT] = arr[DatenFilm.FILM_ZEIT].intern(); } - public void init() { + private void setFilmdauer() { try { - //================================ - // Speicher sparen - if (arr[DatenFilm.FILM_GROESSE].length() < 3) { - arr[DatenFilm.FILM_GROESSE] = arr[DatenFilm.FILM_GROESSE].intern(); - } - if (arr[DatenFilm.FILM_URL_KLEIN].length() < 15) { - arr[DatenFilm.FILM_URL_KLEIN] = arr[DatenFilm.FILM_URL_KLEIN].intern(); - } - arr[DatenFilm.FILM_DATUM] = arr[DatenFilm.FILM_DATUM].intern(); - arr[DatenFilm.FILM_ZEIT] = arr[DatenFilm.FILM_ZEIT].intern(); - - //================================ - // Dateigröße - dateigroesseL = new MSLong(this); - - //================================ - // Filmdauer - try { - if (!this.arr[DatenFilm.FILM_DAUER].contains(":") && !this.arr[DatenFilm.FILM_DAUER].isEmpty()) { - // nur als Übergang bis die Liste umgestellt ist - long l = Long.parseLong(this.arr[DatenFilm.FILM_DAUER]); - dauerL = l; - if (l > 0) { - long hours = l / 3600; - l = l - (hours * 3600); - long min = l / 60; - l = l - (min * 60); - long seconds = l; - this.arr[DatenFilm.FILM_DAUER] = fuellen(2, String.valueOf(hours)) + ':' + fuellen(2, String.valueOf(min)) + ':' + fuellen(2, String.valueOf(seconds)); - } else { - this.arr[DatenFilm.FILM_DAUER] = ""; - } + if (!this.arr[DatenFilm.FILM_DAUER].contains(":") && !this.arr[DatenFilm.FILM_DAUER].isEmpty()) { + // nur als Übergang bis die Liste umgestellt ist + long l = Long.parseLong(this.arr[DatenFilm.FILM_DAUER]); + dauerL = l; + if (l > 0) { + long hours = l / 3600; + l = l - (hours * 3600); + long min = l / 60; + l = l - (min * 60); + long seconds = l; + this.arr[DatenFilm.FILM_DAUER] = fuellen(2, String.valueOf(hours)) + ':' + fuellen(2, String.valueOf(min)) + ':' + fuellen(2, String.valueOf(seconds)); } else { - dauerL = 0; - if (!this.arr[DatenFilm.FILM_DAUER].isEmpty()) { - String[] parts = this.arr[DatenFilm.FILM_DAUER].split(":"); - long power = 1; - for (int i = parts.length - 1; i >= 0; i--) { - dauerL += Long.parseLong(parts[i]) * power; - power *= 60; - } - } + this.arr[DatenFilm.FILM_DAUER] = ""; } - } catch (Exception ex) { + } else { dauerL = 0; - Log.errorLog(468912049, "Dauer: " + this.arr[DatenFilm.FILM_DAUER]); + if (!this.arr[DatenFilm.FILM_DAUER].isEmpty()) { + String[] parts = this.arr[DatenFilm.FILM_DAUER].split(":"); + long power = 1; + for (int i = parts.length - 1; i >= 0; i--) { + dauerL += Long.parseLong(parts[i]) * power; + power *= 60; + } + } } + } catch (Exception ex) { + dauerL = 0; + Log.errorLog(468912049, "Dauer: " + this.arr[DatenFilm.FILM_DAUER]); + } + } - //================================ - // Datum - if (!arr[DatenFilm.FILM_DATUM].isEmpty()) { - // nur dann gibts ein Datum - try { - if (arr[DatenFilm.FILM_DATUM_LONG].isEmpty()) { - if (arr[DatenFilm.FILM_ZEIT].isEmpty()) { - datumFilm = new DatumFilm(sdf_datum.parse(arr[DatenFilm.FILM_DATUM]).getTime()); - } else { - datumFilm = new DatumFilm(sdf_datum_zeit.parse(arr[DatenFilm.FILM_DATUM] + arr[DatenFilm.FILM_ZEIT]).getTime()); - } - arr[FILM_DATUM_LONG] = String.valueOf(datumFilm.getTime() / 1000); + private void setDatum() { + if (!arr[DatenFilm.FILM_DATUM].isEmpty()) { + // nur dann gibts ein Datum + try { + if (arr[DatenFilm.FILM_DATUM_LONG].isEmpty()) { + if (arr[DatenFilm.FILM_ZEIT].isEmpty()) { + datumFilm = new DatumFilm(sdf_datum.parse(arr[DatenFilm.FILM_DATUM]).getTime()); } else { - long l = Long.parseLong(arr[DatenFilm.FILM_DATUM_LONG]); - datumFilm = new DatumFilm(l * 1000 /* sind SEKUNDEN!!*/); + datumFilm = new DatumFilm(sdf_datum_zeit.parse(arr[DatenFilm.FILM_DATUM] + arr[DatenFilm.FILM_ZEIT]).getTime()); } - } catch (Exception ex) { - Log.errorLog(915236701, ex, new String[]{"Datum: " + arr[DatenFilm.FILM_DATUM], "Zeit: " + arr[DatenFilm.FILM_ZEIT]}); - datumFilm = new DatumFilm(0); - arr[DatenFilm.FILM_DATUM] = ""; - arr[DatenFilm.FILM_ZEIT] = ""; + arr[FILM_DATUM_LONG] = String.valueOf(datumFilm.getTime() / 1000); + } else { + long l = Long.parseLong(arr[DatenFilm.FILM_DATUM_LONG]); + datumFilm = new DatumFilm(l * 1000 /* sind SEKUNDEN!!*/); } + } catch (Exception ex) { + Log.errorLog(915236701, ex, new String[]{"Datum: " + arr[DatenFilm.FILM_DATUM], "Zeit: " + arr[DatenFilm.FILM_ZEIT]}); + datumFilm = new DatumFilm(0); + arr[DatenFilm.FILM_DATUM] = ""; + arr[DatenFilm.FILM_ZEIT] = ""; } - } catch (Exception ex) { - Log.errorLog(715263987, ex); } } + public void init() { + preserveMemory(); + + //================================ + // Dateigröße + dateigroesseL = new MSLong(this); + + //================================ + // Filmdauer + setFilmdauer(); + + //================================ + // Datum + setDatum(); + } private String getUrlNormalKlein() { // liefert die kleine normale URL - int i; if (!arr[DatenFilm.FILM_URL_KLEIN].isEmpty()) { try { - i = Integer.parseInt(arr[DatenFilm.FILM_URL_KLEIN].substring(0, arr[DatenFilm.FILM_URL_KLEIN].indexOf('|'))); + final int i = Integer.parseInt(arr[DatenFilm.FILM_URL_KLEIN].substring(0, arr[DatenFilm.FILM_URL_KLEIN].indexOf('|'))); return arr[DatenFilm.FILM_URL].substring(0, i) + arr[DatenFilm.FILM_URL_KLEIN].substring(arr[DatenFilm.FILM_URL_KLEIN].indexOf('|') + 1); - } catch (Exception ignored) { + } catch (NumberFormatException ignored) { } } return arr[DatenFilm.FILM_URL]; @@ -384,12 +446,11 @@ private String getUrlNormalKlein() { private String getUrlNormalHd() { // liefert die HD normale URL - int i; if (!arr[DatenFilm.FILM_URL_HD].isEmpty()) { try { - i = Integer.parseInt(arr[DatenFilm.FILM_URL_HD].substring(0, arr[DatenFilm.FILM_URL_HD].indexOf('|'))); + final int i = Integer.parseInt(arr[DatenFilm.FILM_URL_HD].substring(0, arr[DatenFilm.FILM_URL_HD].indexOf('|'))); return arr[DatenFilm.FILM_URL].substring(0, i) + arr[DatenFilm.FILM_URL_HD].substring(arr[DatenFilm.FILM_URL_HD].indexOf('|') + 1); - } catch (Exception ignored) { + } catch (NumberFormatException ignored) { } } return arr[DatenFilm.FILM_URL]; @@ -438,7 +499,7 @@ private String getUrlFlvstreamerHd() { if (!arr[DatenFilm.FILM_URL_RTMP_HD].isEmpty()) { // es gibt eine HD RTMP try { - int i = Integer.parseInt(arr[DatenFilm.FILM_URL_RTMP_HD].substring(0, arr[DatenFilm.FILM_URL_RTMP_HD].indexOf('|'))); + final int i = Integer.parseInt(arr[DatenFilm.FILM_URL_RTMP_HD].substring(0, arr[DatenFilm.FILM_URL_RTMP_HD].indexOf('|'))); return arr[DatenFilm.FILM_URL_RTMP].substring(0, i) + arr[DatenFilm.FILM_URL_RTMP_HD].substring(arr[DatenFilm.FILM_URL_RTMP_HD].indexOf('|') + 1); } catch (Exception ignored) { } @@ -447,69 +508,13 @@ private String getUrlFlvstreamerHd() { return getUrlFlvstreamer(); } - private static final String[] GERMAN_ONLY = { - "+++ Aus rechtlichen Gründen ist der Film nur innerhalb von Deutschland abrufbar. +++", - "+++ Aus rechtlichen Gründen ist diese Sendung nur innerhalb von Deutschland abrufbar. +++", - "+++ Aus rechtlichen Gründen ist dieses Video nur innerhalb von Deutschland abrufbar. +++", - "+++ Aus rechtlichen Gründen ist dieses Video nur innerhalb von Deutschland verfügbar. +++", - "+++ Aus rechtlichen Gründen kann das Video nur innerhalb von Deutschland abgerufen werden. +++ Due to legal reasons the video is only available in Germany.+++", - "+++ Aus rechtlichen Gründen kann das Video nur innerhalb von Deutschland abgerufen werden. +++", - "+++ Due to legal reasons the video is only available in Germany.+++", - "+++ Aus rechtlichen Gründen kann das Video nur in Deutschland abgerufen werden. +++", - "[Aus rechtlichen Günden können wir die Partie nicht als Einzelclip anbieten.]", - "+++ Aus rechtlichen Gründen ist das Video nur innerhalb von Deutschland abrufbar. +++", - "+++Aus rechtlichen Gründen kann die Sendung nur innerhalb von Deutschland abgerufen werden. +++", - "+++ Aus rechtlichen Gründen dürfen wir dieses Video nur innerhalb von Deutschland anbieten. +++", - "+++Aus rechtlichen Gründen kann dieses Video nur innerhalb von Deutschland abgerufen werden.+++" - }; - - public static String cleanDescription(String s, String thema, String titel) { - // die Beschreibung auf x Zeichen beschränken - - s = Functions.removeHtml(s); // damit die Beschreibung nicht unnötig kurz wird wenn es erst später gemacht wird - - for (String g : GERMAN_ONLY) { - if (s.contains(g)) { - s = s.replace(g, ""); // steht auch mal in der Mitte - } - } - if (s.startsWith(titel)) { - s = s.substring(titel.length()).trim(); - } - if (s.startsWith(thema)) { - s = s.substring(thema.length()).trim(); - } - if (s.startsWith("|")) { - s = s.substring(1).trim(); - } - if (s.startsWith("Video-Clip")) { - s = s.substring("Video-Clip".length()).trim(); - } - if (s.startsWith(titel)) { - s = s.substring(titel.length()).trim(); - } - if (s.startsWith(":") || s.startsWith(",") || s.startsWith("\n")) { - s = s.substring(1).trim(); - } - - if (s.contains("\\\"")) { // wegen " in json-Files - s = s.replace("\\\"", "\""); - } - if (s.length() > Const.MAX_BESCHREIBUNG) { - return s.substring(0, Const.MAX_BESCHREIBUNG) + "\n....."; - } else { - return s; - } - } - private void checkDatum(String datum, String fehlermeldung) { //Datum max. 100 Tage in der Zukunft final long MAX = 1000L * 60L * 60L * 24L * 100L; datum = datum.trim(); if (datum.contains(".") && datum.length() == 10) { try { - SimpleDateFormat sdfIn = new SimpleDateFormat("dd.MM.yyyy"); - Date filmDate = sdfIn.parse(datum); + Date filmDate = FastDateFormat.getInstance("dd.MM.yyyy").parse(datum); if (filmDate.getTime() < 0) { //Datum vor 1970 Log.errorLog(923012125, "Unsinniger Wert: [" + datum + "] " + fehlermeldung); diff --git a/src/main/java/mSearch/daten/ListeFilme.java b/src/main/java/mSearch/daten/ListeFilme.java index 2d10f607..54fd2174 100644 --- a/src/main/java/mSearch/daten/ListeFilme.java +++ b/src/main/java/mSearch/daten/ListeFilme.java @@ -24,12 +24,13 @@ import mSearch.tool.FileSize; import mSearch.tool.Functions; import mSearch.tool.Log; +import org.apache.commons.lang3.time.FastDateFormat; import java.security.MessageDigest; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; -import java.util.stream.Collectors; +import java.util.concurrent.ConcurrentSkipListSet; @SuppressWarnings("serial") public class ListeFilme extends ArrayList { @@ -50,18 +51,17 @@ public class ListeFilme extends ArrayList { public int nr = 1; public String[] metaDaten = new String[]{"", "", "", "", ""}; private final static String DATUM_ZEIT_FORMAT = "dd.MM.yyyy, HH:mm"; - private final static String DATUM_ZEIT_FORMAT_REV = "yyyy.MM.dd__HH:mm"; - SimpleDateFormat sdf = new SimpleDateFormat(DATUM_ZEIT_FORMAT); + private final SimpleDateFormat sdf = new SimpleDateFormat(DATUM_ZEIT_FORMAT); public String[] sender = {""}; public String[][] themenPerSender = {{""}}; public boolean neueFilme = false; - private Collection indexes; + private final Collection indexes; public ListeFilme() { super(); - indexes = new ArrayList<>(); + indexes = new ConcurrentSkipListSet<>(); } @@ -81,6 +81,17 @@ public synchronized boolean addFilmVomSender(DatenFilm film) { return !indexes.contains(film.getIndex()) && addInit(film); } + private void addHash(DatenFilm f, HashSet hash, boolean index) { + if (f.arr[DatenFilm.FILM_SENDER].equals(Const.KIKA)) { + // beim KIKA ändern sich die URLs laufend + hash.add(f.arr[DatenFilm.FILM_THEMA] + f.arr[DatenFilm.FILM_TITEL]); + } else if (index) { + hash.add(f.getIndex()); + } else { + hash.add(f.getUrl()); + } + } + public synchronized void updateListe(ListeFilme listeEinsortieren, boolean index /* Vergleich über Index, sonst nur URL */, boolean ersetzen) { // in eine vorhandene Liste soll eine andere Filmliste einsortiert werden // es werden nur Filme die noch nicht vorhanden sind, einsortiert @@ -88,17 +99,7 @@ public synchronized void updateListe(ListeFilme listeEinsortieren, boolean index final HashSet hash = new HashSet<>(listeEinsortieren.size() + 1, 1); if (ersetzen) { - // ========================================== - for (DatenFilm f : listeEinsortieren) { - if (f.arr[DatenFilm.FILM_SENDER].equals(Const.KIKA)) { - // beim KIKA ändern sich die URLs laufend - hash.add(f.arr[DatenFilm.FILM_THEMA] + f.arr[DatenFilm.FILM_TITEL]); - } else if (index) { - hash.add(f.getIndex()); - } else { - hash.add(DatenFilm.getUrl(f)); - } - } + listeEinsortieren.forEach((DatenFilm f) -> addHash(f, hash, index)); Iterator it = this.iterator(); while (it.hasNext()) { @@ -112,7 +113,7 @@ public synchronized void updateListe(ListeFilme listeEinsortieren, boolean index if (hash.contains(f.getIndex())) { it.remove(); } - } else if (hash.contains(DatenFilm.getUrl(f))) { + } else if (hash.contains(f.getUrl())) { it.remove(); } } @@ -120,16 +121,7 @@ public synchronized void updateListe(ListeFilme listeEinsortieren, boolean index listeEinsortieren.forEach(this::addInit); } else { // ============================================== - for (DatenFilm f : this) { - if (f.arr[DatenFilm.FILM_SENDER].equals(Const.KIKA)) { - // beim KIKA ändern sich die URLs laufend - hash.add(f.arr[DatenFilm.FILM_THEMA] + f.arr[DatenFilm.FILM_TITEL]); - } else if (index) { - hash.add(f.getIndex()); - } else { - hash.add(DatenFilm.getUrl(f)); - } - } + this.forEach(f -> addHash(f, hash, index)); for (DatenFilm f : listeEinsortieren) { if (f.arr[DatenFilm.FILM_SENDER].equals(Const.KIKA)) { @@ -140,7 +132,7 @@ public synchronized void updateListe(ListeFilme listeEinsortieren, boolean index if (!hash.contains(f.getIndex())) { addInit(f); } - } else if (!hash.contains(DatenFilm.getUrl(f))) { + } else if (!hash.contains(f.getUrl())) { addInit(f); } } @@ -164,44 +156,13 @@ public boolean add(DatenFilm aFilm) public synchronized void clear() { nr = 1; neueFilme = false; + indexes.clear(); super.clear(); } - public void cleanList() { - // für den BR: alle Filme mit Thema "BR" die es auch in einem anderen Thema gibt löschen - // wird vorerst nicht verwendet: findet nur ~200 Filme von über 3000 - int count = 0; - final SimpleDateFormat sdfClean = new SimpleDateFormat(DATUM_ZEIT_FORMAT); - Log.sysLog("cleanList start: " + sdfClean.format(System.currentTimeMillis())); - ListeFilme tmp = this.stream().filter(datenFilm -> datenFilm.arr[DatenFilm.FILM_SENDER].equals(Const.BR)) - .filter(datenFilm -> datenFilm.arr[DatenFilm.FILM_THEMA].equals(Const.BR)) - .collect(Collectors.toCollection(ListeFilme::new)); - - for (DatenFilm tFilm : tmp) { - for (DatenFilm datenFilm : this) { - if (datenFilm.arr[DatenFilm.FILM_SENDER].equals(Const.BR)) { - if (!datenFilm.arr[DatenFilm.FILM_THEMA].equals(Const.BR)) { - if (datenFilm.arr[DatenFilm.FILM_URL].equals(tFilm.arr[DatenFilm.FILM_URL])) { - this.remove(tFilm); - ++count; - break; - } - } - } - } - } - - tmp.clear(); - - Log.sysLog("cleanList stop: " + sdfClean.format(System.currentTimeMillis())); - Log.sysLog("cleanList count: " + count); - } - public synchronized void check() { // zum Debuggen for (DatenFilm film : this) { -// film.arr[DatenFilm.FILM_THEMA_NR] = FilenameUtils.cleanUnicode(film.arr[DatenFilm.FILM_THEMA_NR], "!!!!!!!!!!!!!"); -// film.arr[DatenFilm.FILM_TITEL_NR] = FilenameUtils.cleanUnicode(film.arr[DatenFilm.FILM_TITEL_NR], "!!!!!!!!!!!!!"); String s = film.arr[DatenFilm.FILM_BESCHREIBUNG]; film.arr[DatenFilm.FILM_BESCHREIBUNG] = Functions.removeHtml(film.arr[DatenFilm.FILM_BESCHREIBUNG]); if (!s.equals(film.arr[DatenFilm.FILM_BESCHREIBUNG])) { @@ -229,39 +190,6 @@ public synchronized void check() { } } - public synchronized void nurDoppelteAnzeigen(boolean index) { - // zum Debuggen: URLs die doppelt sind, in die History eintragen - // damit sie markiert werden - DatenFilm film; - HashSet hashDoppelt = new HashSet<>(); - HashSet hash = new HashSet<>(); - Iterator it = this.iterator(); - while (it.hasNext()) { - film = it.next(); - if (index) { - if (!hash.contains(film.getIndex())) { - hash.add(film.getIndex()); - } else { - // dann ist er mind. doppelt in der Liste - hashDoppelt.add(film.arr[DatenFilm.FILM_URL]); - } - } else if (!hash.contains(film.arr[DatenFilm.FILM_URL])) { - hash.add(film.arr[DatenFilm.FILM_URL]); - } else { - // dann ist er mind. doppelt in der Liste - hashDoppelt.add(film.arr[DatenFilm.FILM_URL]); - } - } - it = this.iterator(); - while (it.hasNext()) { - if (!hashDoppelt.contains(it.next().arr[DatenFilm.FILM_URL])) { - it.remove(); - } - } - hash.clear(); - hashDoppelt.clear(); - } - public synchronized void sort() { Collections.sort(this); // und jetzt noch die Nummerierung in Ordnung bringen @@ -275,19 +203,6 @@ public synchronized void setMeta(ListeFilme listeFilme) { System.arraycopy(listeFilme.metaDaten, 0, metaDaten, 0, MAX_ELEM); } - public synchronized DatenFilm istInFilmListe(String sender, String thema, String titel) { - // prüfen ob es den Film schon gibt - // und sich evtl. nur die URL geändert hat - for (DatenFilm film : this) { - if (film.arr[DatenFilm.FILM_SENDER].equals(sender) - && film.arr[DatenFilm.FILM_THEMA].equalsIgnoreCase(thema) - && film.arr[DatenFilm.FILM_TITEL].equalsIgnoreCase(titel)) { - return film; - } - } - return null; - } - public synchronized ListeFilme neueFilme(ListeFilme orgListe) { // Funktion liefert eine Liste mit Filmen // die im Vergleich zur Liste "orgListe" @@ -313,19 +228,26 @@ public synchronized ListeFilme neueFilme(ListeFilme orgListe) { return ret; } - public String getFileSizeUrl(String url, String sender) { + /** + * @param url the URL as String. + * @return the determined size or -1. + * @deprecated Move this someday to DatenFilm. + */ + @Deprecated + public String getFileSizeUrl(String url) { + //FIXME bring to DatenFilm and reduce calculation String res; Optional opt = this.parallelStream() - .filter(f -> f.arr[DatenFilm.FILM_URL].equals(url)).findFirst(); + .filter(f -> f.arr[DatenFilm.FILM_URL].equals(url)).findAny(); if (opt.isPresent()) { DatenFilm film = opt.get(); if (!film.arr[DatenFilm.FILM_GROESSE].isEmpty()) res = film.arr[DatenFilm.FILM_GROESSE]; else - res = FileSize.laengeString(url, sender); + res = FileSize.laengeString(url); } else - res = FileSize.laengeString(url, sender); + res = FileSize.laengeString(url); return res; } @@ -336,24 +258,13 @@ public String getFileSizeUrl(String url, String sender) { * @param sender Sender which films are to be deleted. */ public synchronized void deleteAllFilms(String sender) { - DatenFilm film; - Iterator it = this.iterator(); - while (it.hasNext()) { - film = it.next(); - if (film.arr[DatenFilm.FILM_SENDER].equalsIgnoreCase(sender)) { - it.remove(); - } - } + removeIf(film -> film.arr[DatenFilm.FILM_SENDER].equalsIgnoreCase(sender)); } public synchronized DatenFilm getFilmByUrl(final String url) { - for (DatenFilm film : this) { - if (film.arr[DatenFilm.FILM_URL].equalsIgnoreCase(url)) { - return film; - } - } - return null; + Optional opt = this.parallelStream().filter(f -> f.arr[DatenFilm.FILM_URL].equalsIgnoreCase(url)).findAny(); + return opt.orElse(null); } public synchronized void checkThema(String sender, LinkedList liste, String thema) { @@ -393,13 +304,13 @@ public synchronized String genDate() { // Tag, Zeit in lokaler Zeit wann die Filmliste erstellt wurde // in der Form "dd.MM.yyyy, HH:mm" String ret; - SimpleDateFormat sdf_ = new SimpleDateFormat(DATUM_ZEIT_FORMAT); String date; if (metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR].isEmpty()) { // noch eine alte Filmliste ret = metaDaten[ListeFilme.FILMLISTE_DATUM_NR]; } else { date = metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR]; + SimpleDateFormat sdf_ = new SimpleDateFormat(DATUM_ZEIT_FORMAT); sdf_.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC")); Date filmDate = null; try { @@ -409,7 +320,7 @@ public synchronized String genDate() { if (filmDate == null) { ret = metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR]; } else { - SimpleDateFormat formatter = new SimpleDateFormat(DATUM_ZEIT_FORMAT); + FastDateFormat formatter = FastDateFormat.getInstance(DATUM_ZEIT_FORMAT); ret = formatter.format(filmDate); } } @@ -421,39 +332,12 @@ public synchronized String getId() { return metaDaten[ListeFilme.FILMLISTE_ID_NR]; } - public synchronized String genDateRev() { - // Tag, Zeit in lokaler Zeit wann die Filmliste erstellt wurde - // in der Form "yyyy.MM.dd__HH:mm" - String ret; - SimpleDateFormat sdf_ = new SimpleDateFormat(DATUM_ZEIT_FORMAT); - String date; - if (metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR].isEmpty()) { - // noch eine alte Filmliste - ret = metaDaten[ListeFilme.FILMLISTE_DATUM_NR]; - } else { - date = metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR]; - sdf_.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC")); - Date filmDate = null; - try { - filmDate = sdf_.parse(date); - } catch (ParseException ignored) { - } - if (filmDate == null) { - ret = metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR]; - } else { - SimpleDateFormat formatter = new SimpleDateFormat(DATUM_ZEIT_FORMAT_REV); - ret = formatter.format(filmDate); - } - } - return ret; - } - /** * Get the age of the film list. * * @return Age in seconds. */ - public synchronized int getAge() { + public int getAge() { int ret = 0; Date now = new Date(System.currentTimeMillis()); Date filmDate = getAgeAsDate(); @@ -471,7 +355,7 @@ public synchronized int getAge() { * * @return Age as a {@link java.util.Date} object. */ - public synchronized Date getAgeAsDate() { + public Date getAgeAsDate() { String date; if (!metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR].isEmpty()) { date = metaDaten[ListeFilme.FILMLISTE_DATUM_GMT_NR]; @@ -524,7 +408,7 @@ public synchronized boolean isTooOldForDiff() { * @param sekunden The age in seconds. * @return true if older. */ - public synchronized boolean isOlderThan(int sekunden) { + public boolean isOlderThan(int sekunden) { int ret = getAge(); if (ret != 0) { Log.sysLog("Die Filmliste ist " + ret / 60 + " Minuten alt"); @@ -563,8 +447,9 @@ private String createChecksum(String input) { return sb.toString(); } + private String getJetzt_ddMMyyyy_HHmm() { - SimpleDateFormat formatter = new SimpleDateFormat(DATUM_ZEIT_FORMAT); + FastDateFormat formatter = FastDateFormat.getInstance(DATUM_ZEIT_FORMAT); return formatter.format(new Date()); } diff --git a/src/main/java/mSearch/filmlisten/FilmlisteLesen.java b/src/main/java/mSearch/filmlisten/FilmlisteLesen.java index db4bcec0..2d5172ec 100644 --- a/src/main/java/mSearch/filmlisten/FilmlisteLesen.java +++ b/src/main/java/mSearch/filmlisten/FilmlisteLesen.java @@ -30,7 +30,12 @@ import mSearch.filmeSuchen.ListenerFilmeLadenEvent; import mSearch.tool.InputStreamProgressMonitor; import mSearch.tool.Log; +import mSearch.tool.MVHttpClient; import mSearch.tool.ProgressMonitorInputStream; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.apache.commons.lang3.time.FastDateFormat; import org.tukaani.xz.XZInputStream; import javax.swing.event.EventListenerList; @@ -38,30 +43,19 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URISyntaxException; -import java.text.SimpleDateFormat; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Date; +import java.util.concurrent.TimeUnit; import java.util.zip.ZipInputStream; public class FilmlisteLesen { - - public enum WorkMode { - - NORMAL, FASTAUTO - } + private static final int PROGRESS_MAX = 100; private static WorkMode workMode = WorkMode.NORMAL; // die Klasse wird an verschiedenen Stellen benutzt, klappt sonst nicht immer, zB. FilmListe zu alt und neu laden private final EventListenerList listeners = new EventListenerList(); private int max = 0; private int progress = 0; - private static final int TIMEOUT = 10_000; //10 Sekunden - private static final int PROGRESS_MAX = 100; - private long seconds = 0; - - public void addAdListener(ListenerFilmeLaden listener) { - listeners.add(ListenerFilmeLaden.class, listener); - } + private long milliseconds = 0; /** * Set the specific work mode for reading film list. @@ -73,39 +67,8 @@ public static void setWorkMode(WorkMode mode) { workMode = mode; } - private InputStream getInputStreamForLocation(String source) throws IOException, URISyntaxException { - InputStream in; - long size = 0; - final URI uri; - if (source.startsWith("http")) { - uri = new URI(source); - //remote address for internet download - HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); - conn.setConnectTimeout(TIMEOUT); - conn.setReadTimeout(TIMEOUT); - conn.setRequestProperty("User-Agent", Config.getUserAgent()); - if (conn.getResponseCode() < 400) { - size = conn.getContentLengthLong(); - } - in = new ProgressMonitorInputStream(conn.getInputStream(), size, new InputStreamProgressMonitor() { - private int oldProgress = 0; - - @Override - public void progress(long bytesRead, long size) { - final int iProgress = (int) (bytesRead * 100 / size); - if (iProgress != oldProgress) { - oldProgress = iProgress; - notifyProgress(uri.toASCIIString(), "Download", iProgress); - } - } - }); - } else { - //local file - notifyProgress(source, "Download", PROGRESS_MAX); - in = new FileInputStream(source); - } - - return in; + public void addAdListener(ListenerFilmeLaden listener) { + listeners.add(ListenerFilmeLaden.class, listener); } private InputStream selectDecompressor(String source, InputStream in) throws Exception { @@ -119,105 +82,100 @@ private InputStream selectDecompressor(String source, InputStream in) throws Exc return in; } - public void readFilmListe(String source, final ListeFilme listeFilme, int days) { - Log.sysLog("Liste Filme lesen von: " + source); + private void readData(JsonParser jp, ListeFilme listeFilme) throws IOException { JsonToken jsonToken; String sender = "", thema = ""; - listeFilme.clear(); - this.notifyStart(source, PROGRESS_MAX); // für die Progressanzeige - if (days > 0) { - final long maxDays = 1000L * 60L * 60L * 24L * days; - seconds = new Date().getTime() - maxDays; - } else { - seconds = 0; + if (jp.nextToken() != JsonToken.START_OBJECT) { + throw new IllegalStateException("Expected data to start with an Object"); } - try (InputStream in = selectDecompressor(source, getInputStreamForLocation(source)); - JsonParser jp = new JsonFactory().createParser(in)) { - - if (jp.nextToken() != JsonToken.START_OBJECT) { - throw new IllegalStateException("Expected data to start with an Object"); + while ((jsonToken = jp.nextToken()) != null) { + if (jsonToken == JsonToken.END_OBJECT) { + break; } - - while ((jsonToken = jp.nextToken()) != null) { - if (jsonToken == JsonToken.END_OBJECT) { - break; - } - if (jp.isExpectedStartArrayToken()) { - for (int k = 0; k < ListeFilme.MAX_ELEM; ++k) { - listeFilme.metaDaten[k] = jp.nextTextValue(); - } - break; + if (jp.isExpectedStartArrayToken()) { + for (int k = 0; k < ListeFilme.MAX_ELEM; ++k) { + listeFilme.metaDaten[k] = jp.nextTextValue(); } + break; } - while ((jsonToken = jp.nextToken()) != null) { - if (jsonToken == JsonToken.END_OBJECT) { - break; - } - if (jp.isExpectedStartArrayToken()) { - // sind nur die Feldbeschreibungen, brauch mer nicht - jp.nextToken(); - break; - } + } + while ((jsonToken = jp.nextToken()) != null) { + if (jsonToken == JsonToken.END_OBJECT) { + break; } - while (!Config.getStop() && (jsonToken = jp.nextToken()) != null) { - if (jsonToken == JsonToken.END_OBJECT) { - break; - } - if (jp.isExpectedStartArrayToken()) { - DatenFilm datenFilm = new DatenFilm(); - for (int i = 0; i < DatenFilm.JSON_NAMES.length; ++i) { - //if we are in FASTAUTO mode, we don´t need film descriptions. - //this should speed up loading on low end devices... - if (workMode == WorkMode.FASTAUTO) { - if (DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_BESCHREIBUNG - || DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_WEBSEITE - || DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_GEO) { - jp.nextToken(); - continue; - } - } - -// if (DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_TITEL) { -// //convert UNICODE et al to java strings. -// datenFilm.arr[DatenFilm.JSON_NAMES[i]] = StringEscapeUtils.unescapeJava(jp.nextTextValue()); -// } else - if (DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_NEU) { - final String value = jp.nextTextValue(); - //This value is unused... - //datenFilm.arr[DatenFilm.FILM_NEU_NR] = value; - datenFilm.setNew(Boolean.parseBoolean(value)); - } else { - datenFilm.arr[DatenFilm.JSON_NAMES[i]] = jp.nextTextValue(); - } - - /// für die Entwicklungszeit - if (datenFilm.arr[DatenFilm.JSON_NAMES[i]] == null) { - datenFilm.arr[DatenFilm.JSON_NAMES[i]] = ""; + if (jp.isExpectedStartArrayToken()) { + // sind nur die Feldbeschreibungen, brauch mer nicht + jp.nextToken(); + break; + } + } + while (!Config.getStop() && (jsonToken = jp.nextToken()) != null) { + if (jsonToken == JsonToken.END_OBJECT) { + break; + } + if (jp.isExpectedStartArrayToken()) { + DatenFilm datenFilm = new DatenFilm(); + for (int i = 0; i < DatenFilm.JSON_NAMES.length; ++i) { + //if we are in FASTAUTO mode, we don´t need film descriptions. + //this should speed up loading on low end devices... + if (workMode == WorkMode.FASTAUTO) { + if (DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_BESCHREIBUNG + || DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_WEBSEITE + || DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_GEO) { + jp.nextToken(); + continue; } } - if (datenFilm.arr[DatenFilm.FILM_SENDER].isEmpty()) { - datenFilm.arr[DatenFilm.FILM_SENDER] = sender; + if (DatenFilm.JSON_NAMES[i] == DatenFilm.FILM_NEU) { + final String value = jp.nextTextValue(); + //This value is unused... + //datenFilm.arr[DatenFilm.FILM_NEU_NR] = value; + datenFilm.setNew(Boolean.parseBoolean(value)); } else { - sender = datenFilm.arr[DatenFilm.FILM_SENDER]; + datenFilm.arr[DatenFilm.JSON_NAMES[i]] = jp.nextTextValue(); } - if (datenFilm.arr[DatenFilm.FILM_THEMA].isEmpty()) { - datenFilm.arr[DatenFilm.FILM_THEMA] = thema; - } else { - thema = datenFilm.arr[DatenFilm.FILM_THEMA]; + + /// für die Entwicklungszeit + if (datenFilm.arr[DatenFilm.JSON_NAMES[i]] == null) { + datenFilm.arr[DatenFilm.JSON_NAMES[i]] = ""; } + } + if (datenFilm.arr[DatenFilm.FILM_SENDER].isEmpty()) { + datenFilm.arr[DatenFilm.FILM_SENDER] = sender; + } else { + sender = datenFilm.arr[DatenFilm.FILM_SENDER]; + } + if (datenFilm.arr[DatenFilm.FILM_THEMA].isEmpty()) { + datenFilm.arr[DatenFilm.FILM_THEMA] = thema; + } else { + thema = datenFilm.arr[DatenFilm.FILM_THEMA]; + } - listeFilme.importFilmliste(datenFilm); - if (seconds > 0) { - // muss "rückwärts" laufen, da das Datum sonst 2x gebaut werden muss - // wenns drin bleibt, kann mans noch ändern - if (!checkDate(datenFilm)) { - listeFilme.remove(datenFilm); - } + listeFilme.importFilmliste(datenFilm); + if (milliseconds > 0) { + // muss "rückwärts" laufen, da das Datum sonst 2x gebaut werden muss + // wenns drin bleibt, kann mans noch ändern + if (!checkDate(datenFilm)) { + listeFilme.remove(datenFilm); } } } + } + } + + /** + * Read a locally available filmlist. + * + * @param source file path as string + * @param listeFilme the list to read to + */ + private void processFromFile(String source, ListeFilme listeFilme) { + notifyProgress(source, PROGRESS_MAX); + try (InputStream in = selectDecompressor(source, new FileInputStream(source)); + JsonParser jp = new JsonFactory().createParser(in)) { + readData(jp, listeFilme); } catch (FileNotFoundException ex) { Log.errorLog(894512369, "FilmListe existiert nicht: " + source); listeFilme.clear(); @@ -225,19 +183,86 @@ public void readFilmListe(String source, final ListeFilme listeFilme, int days) Log.errorLog(945123641, ex, "FilmListe: " + source); listeFilme.clear(); } + } + + private void checkDays(long days) { + if (days > 0) { + milliseconds = System.currentTimeMillis() - TimeUnit.MILLISECONDS.convert(days, TimeUnit.DAYS); + } else { + milliseconds = 0; + } + } - if (Config.getStop()) { - Log.sysLog("--> Abbruch"); + public void readFilmListe(String source, final ListeFilme listeFilme, int days) { + try { + Log.sysLog("Liste Filme lesen von: " + source); listeFilme.clear(); + this.notifyStart(source, PROGRESS_MAX); // für die Progressanzeige + + checkDays(days); + + if (!source.startsWith("http")) { + processFromFile(source, listeFilme); + } else { + processFromWeb(new URL(source), listeFilme); + } + + if (Config.getStop()) { + Log.sysLog("--> Abbruch"); + listeFilme.clear(); + } + } catch (MalformedURLException ex) { + ex.printStackTrace(); } + notifyFertig(source, listeFilme); } + /** + * Download a process a filmliste from the web. + * + * @param source source url as string + * @param listeFilme the list to read to + */ + private void processFromWeb(URL source, ListeFilme listeFilme) { + Request.Builder builder = new Request.Builder().url(source); + builder.addHeader("User-Agent", Config.getUserAgent()); + + //our progress monitor callback + InputStreamProgressMonitor monitor = new InputStreamProgressMonitor() { + private int oldProgress = 0; + + @Override + public void progress(long bytesRead, long size) { + final int iProgress = (int) (bytesRead * 100 / size); + if (iProgress != oldProgress) { + oldProgress = iProgress; + notifyProgress(source.toString(), iProgress); + } + } + }; + + try (Response response = MVHttpClient.getInstance().getHttpClient().newCall(builder.build()).execute(); + ResponseBody body = response.body()) { + if (response.isSuccessful()) { + try (InputStream input = new ProgressMonitorInputStream(body.byteStream(), body.contentLength(), monitor)) { + try (InputStream is = selectDecompressor(source.toString(), input); + JsonParser jp = new JsonFactory().createParser(is)) { + readData(jp, listeFilme); + } + } + } + } catch (Exception ex) { + Log.errorLog(945123641, ex, "FilmListe: " + source); + listeFilme.clear(); + } + } + private boolean checkDate(DatenFilm film) { // true wenn der Film angezeigt werden kann! try { if (film.datumFilm.getTime() != 0) { - if (film.datumFilm.getTime() < seconds) { + if (film.datumFilm.getTime() < milliseconds) { return false; } } @@ -255,22 +280,27 @@ private void notifyStart(String url, int mmax) { } } - private void notifyProgress(String url, String text, int p) { - progress = p; + private void notifyProgress(String url, int iProgress) { + progress = iProgress; if (progress > max) { progress = max; } for (ListenerFilmeLaden l : listeners.getListeners(ListenerFilmeLaden.class)) { - l.progress(new ListenerFilmeLadenEvent(url, text, max, progress, 0, false)); + l.progress(new ListenerFilmeLadenEvent(url, "Download", max, progress, 0, false)); } } private void notifyFertig(String url, ListeFilme liste) { - Log.sysLog("Liste Filme gelesen am: " + new SimpleDateFormat("dd.MM.yyyy, HH:mm").format(new Date())); + Log.sysLog("Liste Filme gelesen am: " + FastDateFormat.getInstance("dd.MM.yyyy, HH:mm").format(new Date())); Log.sysLog(" erstellt am: " + liste.genDate()); Log.sysLog(" Anzahl Filme: " + liste.size()); for (ListenerFilmeLaden l : listeners.getListeners(ListenerFilmeLaden.class)) { l.fertig(new ListenerFilmeLadenEvent(url, "", max, progress, 0, false)); } } + + public enum WorkMode { + + NORMAL, FASTAUTO + } } diff --git a/src/main/java/mSearch/filmlisten/FilmlistenSuchen.java b/src/main/java/mSearch/filmlisten/FilmlistenSuchen.java index 4f72d59d..8f7a9611 100644 --- a/src/main/java/mSearch/filmlisten/FilmlistenSuchen.java +++ b/src/main/java/mSearch/filmlisten/FilmlistenSuchen.java @@ -33,6 +33,7 @@ import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Random; @@ -166,14 +167,14 @@ public void getDownloadUrlsFilmlisten(String dateiUrl, ListeFilmlistenUrls liste conn.setRequestProperty("User-Agent", userAgent); conn.setReadTimeout(timeout); conn.setConnectTimeout(timeout); - inReader = new InputStreamReader(conn.getInputStream(), Const.KODIERUNG_UTF); + inReader = new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8); } else { // eine Datei verarbeiten File f = new File(dateiUrl); if (!f.exists()) { return; } - inReader = new InputStreamReader(new FileInputStream(f), Const.KODIERUNG_UTF); + inReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8); } parser = inFactory.createXMLStreamReader(inReader); while (parser.hasNext()) { diff --git a/src/main/java/mSearch/filmlisten/WriteFilmlistJson.java b/src/main/java/mSearch/filmlisten/WriteFilmlistJson.java index 450bd568..53de1eaf 100644 --- a/src/main/java/mSearch/filmlisten/WriteFilmlistJson.java +++ b/src/main/java/mSearch/filmlisten/WriteFilmlistJson.java @@ -22,112 +22,160 @@ import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; -import java.io.File; -import java.io.FileOutputStream; -import java.util.Iterator; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; +import mSearch.Const; import mSearch.daten.DatenFilm; import mSearch.daten.ListeFilme; -import mSearch.Const; import mSearch.tool.Log; import org.tukaani.xz.LZMA2Options; import org.tukaani.xz.XZOutputStream; +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; + public class WriteFilmlistJson { + private void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException { + final ByteBuffer buffer = ByteBuffer.allocateDirect(64 * 1024); + while (src.read(buffer) != -1) { + buffer.flip(); + dest.write(buffer); + buffer.compact(); + } + + buffer.flip(); + + while (buffer.hasRemaining()) { + dest.write(buffer); + } + } + + protected JsonGenerator getJsonGenerator(OutputStream os) throws IOException { + JsonFactory jsonF = new JsonFactory(); + JsonGenerator jg = jsonF.createGenerator(os, JsonEncoding.UTF8); + //jg.useDefaultPrettyPrinter(); // enable indentation just to make debug/testing easier + + return jg; + } + + /** + * Write film data and compress with LZMA2. + * + * @param datei file path + * @param listeFilme film data + */ + public void filmlisteSchreibenJsonCompressed(String datei, ListeFilme listeFilme) { + final String tempFile = datei + "_temp"; + filmlisteSchreibenJson(tempFile, listeFilme); + + try { + Log.sysLog("Komprimiere Datei: " + datei); + if (datei.endsWith(Const.FORMAT_XZ)) { + final Path xz = testNativeXz(); + if (xz != null) { + Process p = new ProcessBuilder(xz.toString(), "-9", tempFile).start(); + final int exitCode = p.waitFor(); + if (exitCode == 0) { + Files.move(Paths.get(tempFile + ".xz"), Paths.get(datei), StandardCopyOption.REPLACE_EXISTING); + } + } else + compressFile(tempFile, datei); + } + + Files.deleteIfExists(Paths.get(tempFile)); + } catch (IOException | InterruptedException ex) { + Log.sysLog("Komprimieren fehlgeschlagen"); + } + } + public void filmlisteSchreibenJson(String datei, ListeFilme listeFilme) { - ZipOutputStream zipOutputStream = null; - XZOutputStream xZOutputStream = null; - JsonGenerator jg = null; try { Log.sysLog("Filme schreiben (" + listeFilme.size() + " Filme) :"); - File file = new File(datei); - File dir = new File(file.getParent()); - if (!dir.exists()) { - if (!dir.mkdirs()) { - Log.errorLog(915236478, "Kann den Pfad nicht anlegen: " + dir.toString()); - } - } + Log.sysLog(" --> Start Schreiben nach: " + datei); String sender = "", thema = ""; - JsonFactory jsonF = new JsonFactory(); - if (datei.endsWith(Const.FORMAT_XZ)) { - LZMA2Options options = new LZMA2Options(); - xZOutputStream = new XZOutputStream(new FileOutputStream(file), options); - jg = jsonF.createGenerator(xZOutputStream); - } else if (datei.endsWith(Const.FORMAT_ZIP)) { - zipOutputStream = new ZipOutputStream(new FileOutputStream(file)); - ZipEntry entry = new ZipEntry(Const.XML_DATEI_FILME); - zipOutputStream.putNextEntry(entry); - jg = jsonF.createGenerator(zipOutputStream, JsonEncoding.UTF8); - } else { - jg = jsonF.createGenerator(new File(datei), JsonEncoding.UTF8); - } - jg.useDefaultPrettyPrinter(); // enable indentation just to make debug/testing easier - jg.writeStartObject(); - // Infos zur Filmliste - jg.writeArrayFieldStart(ListeFilme.FILMLISTE); - for (int i = 0; i < ListeFilme.MAX_ELEM; ++i) { - jg.writeString(listeFilme.metaDaten[i]); - } - jg.writeEndArray(); - // Infos der Felder in der Filmliste - jg.writeArrayFieldStart(ListeFilme.FILMLISTE); - for (int i = 0; i < DatenFilm.JSON_NAMES.length; ++i) { - jg.writeString(DatenFilm.COLUMN_NAMES[DatenFilm.JSON_NAMES[i]]); - } - jg.writeEndArray(); - //Filme schreiben - DatenFilm datenFilm; - Iterator iterator = listeFilme.iterator(); - while (iterator.hasNext()) { - datenFilm = iterator.next(); - datenFilm.arr[DatenFilm.FILM_NEU] = Boolean.toString(datenFilm.isNew()); // damit wirs beim nächsten Programmstart noch wissen - - jg.writeArrayFieldStart(DatenFilm.TAG_JSON_LIST); + + try (FileOutputStream fos = new FileOutputStream(datei); + JsonGenerator jg = getJsonGenerator(fos)) { + + jg.writeStartObject(); + // Infos zur Filmliste + jg.writeArrayFieldStart(ListeFilme.FILMLISTE); + for (int i = 0; i < ListeFilme.MAX_ELEM; ++i) { + jg.writeString(listeFilme.metaDaten[i]); + } + jg.writeEndArray(); + // Infos der Felder in der Filmliste + jg.writeArrayFieldStart(ListeFilme.FILMLISTE); for (int i = 0; i < DatenFilm.JSON_NAMES.length; ++i) { - int m = DatenFilm.JSON_NAMES[i]; - if (m == DatenFilm.FILM_SENDER) { - if (datenFilm.arr[m].equals(sender)) { - jg.writeString(""); - } else { - sender = datenFilm.arr[m]; - jg.writeString(datenFilm.arr[m]); - } - } else if (m == DatenFilm.FILM_THEMA) { - if (datenFilm.arr[m].equals(thema)) { - jg.writeString(""); + jg.writeString(DatenFilm.COLUMN_NAMES[DatenFilm.JSON_NAMES[i]]); + } + jg.writeEndArray(); + //Filme schreiben + for (DatenFilm datenFilm : listeFilme) { + datenFilm.arr[DatenFilm.FILM_NEU] = Boolean.toString(datenFilm.isNew()); // damit wirs beim nächsten Programmstart noch wissen + + jg.writeArrayFieldStart(DatenFilm.TAG_JSON_LIST); + for (int i = 0; i < DatenFilm.JSON_NAMES.length; ++i) { + int m = DatenFilm.JSON_NAMES[i]; + if (m == DatenFilm.FILM_SENDER) { + if (datenFilm.arr[m].equals(sender)) { + jg.writeString(""); + } else { + sender = datenFilm.arr[m]; + jg.writeString(datenFilm.arr[m]); + } + } else if (m == DatenFilm.FILM_THEMA) { + if (datenFilm.arr[m].equals(thema)) { + jg.writeString(""); + } else { + thema = datenFilm.arr[m]; + jg.writeString(datenFilm.arr[m]); + } } else { - thema = datenFilm.arr[m]; jg.writeString(datenFilm.arr[m]); } - } else { - jg.writeString(datenFilm.arr[m]); } + jg.writeEndArray(); } - jg.writeEndArray(); + jg.writeEndObject(); + Log.sysLog(" --> geschrieben!"); } - jg.writeEndObject(); - Log.sysLog(" --> geschrieben!"); } catch (Exception ex) { Log.errorLog(846930145, ex, "nach: " + datei); - } finally { - try { - if (jg != null) { - jg.close(); - } - // die werden nicht immer korrekt geschlossen !??! - if (zipOutputStream != null) { - zipOutputStream.close(); - } - if (xZOutputStream != null) { - xZOutputStream.close(); - } - } catch (Exception e) { - Log.errorLog(732101201, e, "close stream: " + datei); + } + } + + private Path testNativeXz() { + final String[] paths = {"/usr/bin/xz", "/opt/local/bin/xz", "/usr/local/bin/xz"}; + + Path xz = null; + + for (String path : paths) { + xz = Paths.get(path); + if (Files.isExecutable(xz)) { + break; } } + + return xz; } + private void compressFile(String inputName, String outputName) throws IOException { + try (InputStream input = new FileInputStream(inputName); + FileOutputStream fos = new FileOutputStream(outputName); + final OutputStream output = new XZOutputStream(fos, new LZMA2Options()); + final ReadableByteChannel inputChannel = Channels.newChannel(input); + final WritableByteChannel outputChannel = Channels.newChannel(output)) { + + fastChannelCopy(inputChannel, outputChannel); + } catch (IOException ignored) { + } + } } diff --git a/src/main/java/mSearch/tool/FileSize.java b/src/main/java/mSearch/tool/FileSize.java index b24b62e9..245ceeab 100644 --- a/src/main/java/mSearch/tool/FileSize.java +++ b/src/main/java/mSearch/tool/FileSize.java @@ -1,106 +1,54 @@ package mSearch.tool; -import java.net.HttpURLConnection; -import java.net.URL; -import mSearch.Config; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; -public class FileSize { - - final static int TIMEOUT = 3000; // ms //ToDo evtl. wieder kürzen!! +import java.io.IOException; +public class FileSize { public static String laengeString(String url) { - // liefert die Dateigröße einer URL in MB!! - // Anzeige der Größe in MB und deshalb: Faktor 1000 - return laengeString(url, ""); - } - - public static String laengeString(String url, String ssender) { // liefert die Dateigröße einer URL in MB!! // Anzeige der Größe in MiB und deshalb: Faktor 1000 String groesseStr = ""; - long l = laenge(url, ssender); - if (l > 1000 * 1000) { + long l = getFileSizeFromUrl(url); + if (l > 1_000_000) { // größer als 1MiB sonst kann ich mirs sparen - groesseStr = String.valueOf(l / (1000 * 1000)); + groesseStr = String.valueOf(l / 1_000_000); } else if (l > 0) { groesseStr = "1"; } return groesseStr; } - public static long laengeLong(String url) { - // liefert die Dateigröße einer URL in MB!! - // Anzeige der Größe in MiB und deshalb: Faktor 1000 - long groesse = 0; - - long l = laenge(url, ""); - if (l > 1000 * 1000) { - // größer als 1MiB sonst kann ich mirs sparen - groesse = l / (1000 * 1000); - } else if (l > 0) { - groesse = 1; - } - return groesse; - } - - private static long laenge(String url, String ssender) { - // liefert die Dateigröße einer URL in BYTE! - // oder -1 - long ret = -1; - int retCode; + /** + * Return the size of a URL in bytes. + * + * @param url URL as String to query. + * @return size in bytes or -1. + */ + private static long getFileSizeFromUrl(String url) { if (!url.toLowerCase().startsWith("http")) { - return ret; + return -1; } -//// FilmeSuchen.listeSenderLaufen.inc(ssender, RunSender.Count.GET_SIZE_SUM); - try { - HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); - conn.setRequestProperty("User-Agent", Config.getUserAgent()); - conn.setReadTimeout(TIMEOUT); - conn.setConnectTimeout(TIMEOUT); - retCode = conn.getResponseCode(); - if (retCode < 400) { - ret = conn.getContentLengthLong(); //gibts erst seit jdk 7 - } - conn.disconnect(); - // dann über eine Proxy - if (retCode == 403) { -//// FilmeSuchen.listeSenderLaufen.inc(ssender, RunSender.Count.GET_SIZE_SUM403); -// if (!Config.proxyUrl.isEmpty() && Config.proxyPort > 0) { -// // nur dann verwenden, wenn ein Proxy angegeben -// try { -// SocketAddress saddr = new InetSocketAddress(Config.proxyUrl, Config.proxyPort); -// Proxy proxy = new Proxy(Proxy.Type.SOCKS, saddr); -// conn = (HttpURLConnection) new URL(url).openConnection(proxy); -// conn.setRequestProperty("User-Agent", Config.getUserAgent()); -// conn.setReadTimeout(TIMEOUT); -// conn.setConnectTimeout(TIMEOUT); -// ret = conn.getContentLengthLong(); //gibts erst seit jdk 7 -// conn.disconnect(); -// if (ret > 0) { -////// FilmeSuchen.listeSenderLaufen.inc(ssender, RunSender.Count.GET_SIZE_PROXY); -// } -// } catch (Exception ex) { -// ret = -1; -// Log.errorLog(963215478, ex); -// } -// } - } - } catch (Exception ex) { - ret = -1; - if (ex.getMessage().equals("Read timed out")) { - Log.errorLog(825141452, "Read timed out: " + ssender + " url: " + url); - } else { - Log.errorLog(643298301, ex, "url: " + url); - } + final Request request = new Request.Builder().url(url).head().build(); + long respLength = -1; + try (Response response = MVHttpClient.getInstance().getReducedTimeOutClient().newCall(request).execute(); + ResponseBody body = response.body()) { + if (response.isSuccessful()) + respLength = body.contentLength(); + } catch (IOException ignored) { + respLength = -1; } - if (ret < 1000 * 1000) { + + if (respLength < 1_000_000) { // alles unter 1MB sind Playlisten, ORF: Trailer bei im Ausland gesperrten Filmen, ... // dann wars nix - ret = -1; + respLength = -1; } - return ret; + return respLength; } } diff --git a/src/main/java/mSearch/tool/Functions.java b/src/main/java/mSearch/tool/Functions.java index 1f06dc24..9c25fbd3 100644 --- a/src/main/java/mSearch/tool/Functions.java +++ b/src/main/java/mSearch/tool/Functions.java @@ -20,13 +20,14 @@ package mSearch.tool; import com.jidesoft.utils.SystemInfo; -import java.io.File; -import java.security.CodeSource; -import java.util.ResourceBundle; import mSearch.Const; import mSearch.daten.DatenFilm; import org.apache.commons.lang3.StringEscapeUtils; +import java.io.File; +import java.security.CodeSource; +import java.util.ResourceBundle; + public class Functions { private static final String RBVERSION = "version"; @@ -41,9 +42,9 @@ public static String textLaenge(int max, String text, boolean mitte, boolean add } while (text.length() < max) { if (addVorne) { - text = " " + text; + text = ' ' + text; } else { - text = text + " "; + text = text + ' '; } } return text; @@ -51,7 +52,7 @@ public static String textLaenge(int max, String text, boolean mitte, boolean add public static String minTextLaenge(int max, String text) { while (text.length() < max) { - text = text + " "; + text = text + ' '; } return text; } @@ -130,7 +131,7 @@ public static String getPathJar() { } public static String getProgVersionString() { - return " [Vers.: " + getProgVersion().toString() + "]"; + return " [Vers.: " + getProgVersion().toString() + ']'; } public static String[] getJavaVersion() { @@ -187,36 +188,40 @@ public static String getBuildNr() { return new Version("").toString(); } - public static void unescape(DatenFilm film) { - + private static void unescapeThema(DatenFilm film) { // Thema film.arr[DatenFilm.FILM_THEMA] = StringEscapeUtils.unescapeXml(film.arr[DatenFilm.FILM_THEMA]); film.arr[DatenFilm.FILM_THEMA] = StringEscapeUtils.unescapeHtml4(film.arr[DatenFilm.FILM_THEMA]); film.arr[DatenFilm.FILM_THEMA] = StringEscapeUtils.unescapeJava(film.arr[DatenFilm.FILM_THEMA]); + } + private static void unescapeTitel(DatenFilm film) { // Titel film.arr[DatenFilm.FILM_TITEL] = StringEscapeUtils.unescapeXml(film.arr[DatenFilm.FILM_TITEL]); film.arr[DatenFilm.FILM_TITEL] = StringEscapeUtils.unescapeHtml4(film.arr[DatenFilm.FILM_TITEL]); film.arr[DatenFilm.FILM_TITEL] = StringEscapeUtils.unescapeJava(film.arr[DatenFilm.FILM_TITEL]); + } + private static void unescapeDescription(DatenFilm film) { // Beschreibung film.arr[DatenFilm.FILM_BESCHREIBUNG] = StringEscapeUtils.unescapeXml(film.arr[DatenFilm.FILM_BESCHREIBUNG]); film.arr[DatenFilm.FILM_BESCHREIBUNG] = StringEscapeUtils.unescapeHtml4(film.arr[DatenFilm.FILM_BESCHREIBUNG]); film.arr[DatenFilm.FILM_BESCHREIBUNG] = StringEscapeUtils.unescapeJava(film.arr[DatenFilm.FILM_BESCHREIBUNG]); film.arr[DatenFilm.FILM_BESCHREIBUNG] = removeHtml(film.arr[DatenFilm.FILM_BESCHREIBUNG]); + } - // aus "(2\3)" wird durch escapen: (2\u0003) - // deswegen "\" tauschen in "/" -// if (film.arr[DatenFilm.FILM_THEMA].contains("\\") || film.arr[DatenFilm.FILM_TITEL].contains("\\") -// || film.arr[DatenFilm.FILM_BESCHREIBUNG].contains("\\")) { -// System.out.print(film.arr[DatenFilm.FILM_THEMA]); -// System.out.print(film.arr[DatenFilm.FILM_TITEL]); -// System.out.print(film.arr[DatenFilm.FILM_BESCHREIBUNG]); -// } + private static void replaceText(DatenFilm film) { film.arr[DatenFilm.FILM_THEMA] = film.arr[DatenFilm.FILM_THEMA].replace("\\", "/").trim(); film.arr[DatenFilm.FILM_TITEL] = film.arr[DatenFilm.FILM_TITEL].replace("\\", "/").trim(); film.arr[DatenFilm.FILM_BESCHREIBUNG] = film.arr[DatenFilm.FILM_BESCHREIBUNG].replace("\\", "/").trim(); + } + + public static void unescape(DatenFilm film) { + unescapeThema(film); + unescapeTitel(film); + unescapeDescription(film); + replaceText(film); } // public static String utf8(String ret) { @@ -255,7 +260,7 @@ public static String addsPfad(String pfad1, String pfad2) { ret = pfad2; } else if (pfad2.isEmpty()) { ret = pfad1; - } else if (!pfad1.equals("") && !pfad2.equals("")) { + } else if (!pfad1.isEmpty() && !pfad2.isEmpty()) { if (pfad1.endsWith(File.separator)) { ret = pfad1.substring(0, pfad1.length() - 1); } else { @@ -268,7 +273,7 @@ public static String addsPfad(String pfad1, String pfad2) { } } } - if (ret.equals("")) { + if (ret.isEmpty()) { Log.errorLog(283946015, pfad1 + " - " + pfad2); } return ret; @@ -278,7 +283,7 @@ public static String addUrl(String u1, String u2) { if (u1.endsWith("/")) { return u1 + u2; } else { - return u1 + "/" + u2; + return u1 + '/' + u2; } } @@ -291,17 +296,17 @@ public static String getDateiName(String pfad) { //Dateinamen einer URL extrahieren String ret = ""; if (pfad != null) { - if (!pfad.equals("")) { - ret = pfad.substring(pfad.lastIndexOf("/") + 1); + if (!pfad.isEmpty()) { + ret = pfad.substring(pfad.lastIndexOf('/') + 1); } } if (ret.contains("?")) { - ret = ret.substring(0, ret.indexOf("?")); + ret = ret.substring(0, ret.indexOf('?')); } if (ret.contains("&")) { - ret = ret.substring(0, ret.indexOf("&")); + ret = ret.substring(0, ret.indexOf('&')); } - if (ret.equals("")) { + if (ret.isEmpty()) { Log.errorLog(395019631, pfad); } return ret; diff --git a/src/main/java/mSearch/tool/Hash.java b/src/main/java/mSearch/tool/Hash.java new file mode 100644 index 00000000..6bb423df --- /dev/null +++ b/src/main/java/mSearch/tool/Hash.java @@ -0,0 +1,181 @@ +package mSearch.tool; + +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public final class Hash { + private final MessageDigest md; + private byte[] bytes; + + public Hash() { + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("MD5 algorithm not found", e); + } + } + + public Hash(byte[] b) { + this(); + update(b); + } + + public Hash(byte[] b, int off, int len) { + this(); + update(b, off, len); + } + + public Hash(ByteBuffer bb) { + this(); + update(bb); + } + + public Hash(String s) { + this(); + update(s); + } + + public Hash(char c) { + this(); + update(c); + } + + public Hash(short s) { + this(); + update(s); + } + + public Hash(int i) { + this(); + update(i); + } + + public Hash(long l) { + this(); + update(l); + } + + public Hash(float f) { + this(); + update(f); + } + + public Hash(double d) { + this(); + update(d); + } + + public Hash update(byte b) { + checkNotFinished(); + md.update(b); + return this; + } + + public Hash update(byte[] b) { + return update(b, 0, b.length); + } + + public Hash update(byte[] b, int off, int len) { + checkNotFinished(); + md.update(b, off, len); + return this; + } + + public Hash update(ByteBuffer bb) { + checkNotFinished(); + md.update(bb); + return this; + } + + public Hash update(String s) { + if (s != null) { + for (int i = 0; i < s.length(); i++) { + update(s.charAt(i)); + } + } + return this; + } + + public Hash update(short s) { + return update((byte) (s >> 8)).update((byte) s); + } + + public Hash update(char c) { + return update((short) c); + } + + public Hash update(int i) { + return update((short) (i >> 16)).update((short) i); + } + + public Hash update(long l) { + return update((int) (l >> 32)).update((int) l); + } + + public Hash update(float f) { + return update(Float.floatToRawIntBits(f)); + } + + public Hash update(double d) { + return update(Double.doubleToRawLongBits(d)); + } + + public Hash finish() { + bytes = md.digest(); + return this; + } + + public void reset() { + bytes = null; + } + + public byte[] getBytes() { + ensureFinished(); + return bytes; + } + + public String toHexString() { + ensureFinished(); + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(Character.forDigit((b >> 8) & 0xf, 16)); + sb.append(Character.forDigit(b & 0xf, 16)); + } + return sb.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null || !(obj instanceof Hash)) + return false; + + Hash hash = (Hash) obj; + return Arrays.equals(getBytes(), hash.getBytes()); + } + + @Override + public int hashCode() { + byte[] b = getBytes(); + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; + } + + @Override + public String toString() { + return toHexString(); + } + + private void checkNotFinished() { + if (bytes != null) { + throw new IllegalStateException("Hash must be reset before resuse"); + } + } + + private void ensureFinished() { + if (bytes == null) + finish(); + } +} diff --git a/src/main/java/mSearch/tool/Log.java b/src/main/java/mSearch/tool/Log.java index c20d6126..184a0d97 100644 --- a/src/main/java/mSearch/tool/Log.java +++ b/src/main/java/mSearch/tool/Log.java @@ -24,6 +24,7 @@ import mSearch.Const; import java.io.*; +import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -329,7 +330,7 @@ private static void printLog() { logList.forEach(System.out::println); if (logFile != null) { - try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(logFile, true), Const.KODIERUNG_UTF)) { + try (OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(logFile, true), StandardCharsets.UTF_8)) { for (String s : logList) { out.write(s); out.write("\n"); diff --git a/src/main/java/mSearch/tool/MSStringBuilder.java b/src/main/java/mSearch/tool/MSStringBuilder.java index 391f2a6f..1f7b7be2 100644 --- a/src/main/java/mSearch/tool/MSStringBuilder.java +++ b/src/main/java/mSearch/tool/MSStringBuilder.java @@ -167,13 +167,16 @@ public void extractList(String abMuster, String bisMuster, String musterStart1, } str = addUrl + str; - if (!result.contains(str)) { - result.add(str); - if (result.size() > 1000) { - DbgMsg.print("Achtung"); - } - } + addStr(str, result); + } + } + private void addStr(String str, ArrayList result) { + if (!result.contains(str)) { + result.add(str); + if (result.size() > 1000) { + DbgMsg.print("Achtung"); + } } } diff --git a/src/main/java/mSearch/tool/MVHttpClient.java b/src/main/java/mSearch/tool/MVHttpClient.java new file mode 100644 index 00000000..98745663 --- /dev/null +++ b/src/main/java/mSearch/tool/MVHttpClient.java @@ -0,0 +1,39 @@ +package mSearch.tool; + +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; + +import java.util.concurrent.TimeUnit; + +public class MVHttpClient { + private final static MVHttpClient ourInstance = new MVHttpClient(); + private final OkHttpClient httpClient; + private final OkHttpClient copyClient; + + private MVHttpClient() { + httpClient = new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .writeTimeout(30, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .connectionPool(new ConnectionPool(100, 1, TimeUnit.SECONDS)) + .build(); + httpClient.dispatcher().setMaxRequests(100); + + copyClient = httpClient.newBuilder() + .connectTimeout(5, TimeUnit.SECONDS) + .readTimeout(5, TimeUnit.SECONDS) + .writeTimeout(2, TimeUnit.SECONDS).build(); + } + + public static MVHttpClient getInstance() { + return ourInstance; + } + + public OkHttpClient getHttpClient() { + return httpClient; + } + + public OkHttpClient getReducedTimeOutClient() { + return copyClient; + } +}