]> www.vanbest.org Git - tv_grab_nl_java/commitdiff
Implemented generic Programme cache using hsqldbl; fixed reading enabled/disabled...
authorJan-Pascal van Best <janpascal@vanbest.org>
Sat, 31 Mar 2012 10:49:33 +0000 (12:49 +0200)
committerJan-Pascal van Best <janpascal@vanbest.org>
Sat, 31 Mar 2012 10:49:33 +0000 (12:49 +0200)
.gitignore
src/main/java/org/vanbest/xmltv/AbstractEPGSource.java
src/main/java/org/vanbest/xmltv/Channel.java
src/main/java/org/vanbest/xmltv/Config.java
src/main/java/org/vanbest/xmltv/Main.java
src/main/java/org/vanbest/xmltv/Programme.java
src/main/java/org/vanbest/xmltv/ProgrammeCache.java
src/main/java/org/vanbest/xmltv/RTL.java
src/main/java/org/vanbest/xmltv/TvGidsProgrammeCache.java [new file with mode: 0644]
src/main/java/org/vanbest/xmltv/XmlTvWriter.java

index 9f114ba114ea932df0201c7e0f094c5400e0a55f..3f450ce2fa4224b405b2b2dd1175c85de57cc64b 100644 (file)
@@ -2,3 +2,4 @@
 /rtl.xml
 /target
 /testdb.*
+/tv_grab_nl_java.db.properties
index 09d243740cac42694f78561bda9ef88f372bc0a0..ef40f5ba0ae536526c44efd042273b0751e5b5a4 100644 (file)
@@ -13,14 +13,14 @@ import org.vanbest.xmltv.EPGSource.Stats;
 public abstract class AbstractEPGSource implements EPGSource {
 
        protected Config config;
-       protected ProgrammeCache cache;
+       protected TvGidsProgrammeCache cache;
        protected Stats stats = new Stats();
        
        public static final int MAX_FETCH_TRIES=5;
 
        public AbstractEPGSource(Config config) {
                this.config = config;
-               cache = new ProgrammeCache(config.cacheFile);
+               cache = new TvGidsProgrammeCache(config.cacheFile);
        }
 
        public Set<TvGidsProgramme> getProgrammes(Channel channel, int day, boolean fetchDetails)
index b26ba9d5a24a0a3bacca042167779ceb5286e985..650ae5ebfe3b088526489063be34ad50a863391f 100644 (file)
@@ -1,8 +1,10 @@
 package org.vanbest.xmltv;\r
 \r
 import java.util.ArrayList;\r
+import java.util.HashMap;\r
 import java.util.HashSet;\r
 import java.util.List;\r
+import java.util.Map;\r
 import java.util.Set;\r
 \r
 import javax.xml.stream.XMLStreamException;\r
@@ -18,6 +20,9 @@ public class Channel {
        \r
        public final static int CHANNEL_SOURCE_TVGIDS=1;\r
        public final static int CHANNEL_SOURCE_RTL=2;\r
+       private final static String[] CHANNEL_SOURCE_NAMES={"tvgids.nl", "rtl.nl"};\r
+       private static Map<String,Integer> channelSourceNameMap = new HashMap<String,Integer>();\r
+\r
        \r
        protected Channel(int source, String id) {\r
                this.id = id;\r
@@ -47,12 +52,30 @@ public class Channel {
        public String getXmltvChannelId() {\r
                switch (source) {\r
                case CHANNEL_SOURCE_TVGIDS:\r
-                       return id+".tvgids.nl";\r
                case CHANNEL_SOURCE_RTL:\r
-                       return id;\r
                default:\r
-                       return id;\r
+                       return id+"."+getSourceName();\r
+               }\r
+       }\r
+       \r
+       public static String getChannelSourceName(int id) {\r
+               return CHANNEL_SOURCE_NAMES[id-1];\r
+       }\r
+\r
+       public String getSourceName() {\r
+               return getChannelSourceName(source);\r
+       }\r
+       \r
+       public static int getChannelSourceId(String name) {\r
+               if (channelSourceNameMap.isEmpty()) {\r
+                       int i=1;\r
+                       for (String s: CHANNEL_SOURCE_NAMES) {\r
+                               channelSourceNameMap.put(s,  i);\r
+                               i++;\r
+                       }\r
                }\r
+               return channelSourceNameMap.get(name);\r
+               \r
        }\r
         \r
        public void serialize(XMLStreamWriter writer) throws XMLStreamException {\r
index 86c88b265e6b9dae1b14cca8036cf4a3e56586e9..3a9544d39d62b57154822e4d60bfd0e912538151 100644 (file)
@@ -40,6 +40,9 @@ public class Config {
        public List<Channel> channels;
        public Map<String, String> cattrans;
        protected File cacheFile;
+       public String cacheDbHandle;
+       public String cacheDbUser;
+       public String cacheDbPassword;
        boolean quiet = false;
        public int logLevel = LOG_DEFAULT;
        
@@ -49,7 +52,7 @@ public class Config {
        
        public static int LOG_DEFAULT = LOG_INFO;
        
-       private final static int CURRENT_FILE_FORMAT=2;
+       private final static int CURRENT_FILE_FORMAT=3;
 
        String project_version;
        
@@ -70,6 +73,9 @@ public class Config {
                result.cattrans = getDefaultCattrans();
                result.cacheFile = defaultCacheFile();
                result.niceMilliseconds = 500;
+               result.cacheDbHandle = "jdbc:hsqldb:file:cachedb"; // FIXME in userdir
+               result.cacheDbUser = "SA";
+               result.cacheDbPassword = "";
                return result;
        }
                
@@ -125,8 +131,8 @@ public class Config {
                out.println("nice-time-milliseconds: " + niceMilliseconds);
                for(Channel c: channels) {
                        // FIXME: handle multiple channels names, icons and urls
-                       out.print("channel: " + c.source + 
-                                       ":" + c.id +
+                       out.print("channel: " + c.getSourceName() + 
+                                       ": " + c.id +
                                        ": " + (c.enabled?"enabled":"disabled") +
                                        ": " + escape(c.defaultName()));
                        if (!c.icons.isEmpty()) {
@@ -219,7 +225,14 @@ public class Config {
                                                }
                                                break;
                                        case 2:
-                                               c = Channel.getChannel(Integer.parseInt(parts.get(1)), parts.get(2), parts.get(4));
+                                       case 3:
+                                               int source;
+                                               if (fileformat==2) {
+                                                       source = Integer.parseInt(parts.get(1));
+                                               } else {
+                                                       source = Channel.getChannelSourceId(parts.get(1));
+                                               }
+                                               c = Channel.getChannel(source, parts.get(2), parts.get(4));
                                                if (parts.size()>5) {
                                                        c.addIcon(parts.get(5));
                                                }
index 76e291b3cda9b587aa610d06fac9513b1c511596..d05f132850af33c191cf8b9bd370822b3f6e7383 100644 (file)
@@ -112,7 +112,9 @@ public class Main {
                
                Set<String> oldChannels = new HashSet<String>();
                for (Channel c: config.channels) {
-                       oldChannels.add(c.id);
+                       if (c.enabled) { 
+                               oldChannels.add(c.source+"::"+c.id); 
+                       }
                }
                List<Channel> channels = gids.getChannels();
                
@@ -121,7 +123,7 @@ public class Main {
                boolean none = false;
                boolean keep = false;
                for (Channel c: channels) {
-                       boolean selected = oldChannels.contains(c.id);
+                       boolean selected = oldChannels.contains(c.source+"::"+c.id);
                        System.out.print("add channel " + c.id + " (" + c.defaultName() + ") [[y]es,[n]o,[a]ll,[none],[k]eep selection (default=" + (selected?"yes":"no") + ")] ");
                        if (keep) {
                                c.enabled = selected;
index 08fbc4992535c1c82258c1429933bd1a26e63bc8..0bcb6c5ea294952d17e2b89aa66d5555166701e1 100644 (file)
@@ -1,5 +1,6 @@
 package org.vanbest.xmltv;
 
+import java.io.Serializable;
 import java.net.URL;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
@@ -11,8 +12,8 @@ import java.util.concurrent.TimeUnit;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 
-public class Programme {
-       class Title {
+public class Programme implements Serializable {
+       class Title implements Serializable {
                String title;
                String lang;
            public Title(String title, String lang) {
@@ -20,11 +21,11 @@ public class Programme {
                this.lang = lang;
            }
        }
-       class Actor {
+       class Actor implements Serializable {
                String name;
                String role;
        }
-       class Credits {
+       class Credits implements Serializable {
                List<String> directors;
                List<Actor> actors;
                List<String> writers;
@@ -36,30 +37,30 @@ public class Programme {
                List<String> commentators;
                List<String> guests;
        }
-       class Length {
+       class Length implements Serializable {
                TimeUnit unit; 
                int count;
        }
-       class Icon {
+       class Icon implements Serializable {
                URL url;
                int width;
                int height;
        }
-       class Episode {
+       class Episode implements Serializable {
            String episode;
            String system; // onscreen or xmltv_ns
        }
-       class Video {
+       class Video implements Serializable {
                boolean present;
                boolean colour;
                String aspect; // eg. 16:9, 4:3
                String quality; // eg. 'HDTV', '800x600'.
        }
-       class Audio {
+       class Audio implements Serializable {
                boolean present;
                String stereo; // 'mono','stereo','dolby','dolby digital','bilingual' or 'surround'. 
        }
-       class Subtitle {
+       class Subtitle implements Serializable {
                String type; // teletext | onscreen | deaf-signed
                Title language;
        }
@@ -69,7 +70,7 @@ public class Programme {
     public Date vpsStart;
     public String showview;
     public String videoplus;
-       public Channel channel; // required
+       public String channel; // required xmltvid of the associated channel
     public String clumpidx;    
     
     public List<Title> titles; // at least one
@@ -170,7 +171,7 @@ public class Programme {
                writer.writeStartElement("programme");
                if(startTime != null) writer.writeAttribute("start", df.format(startTime));
                if(endTime != null) writer.writeAttribute("stop", df.format(endTime));
-               if(channel != null) writer.writeAttribute("channel", ""+channel.id);
+               if(channel != null) writer.writeAttribute("channel", ""+channel);
                writeTitleList(titles,"title",writer);
                writeTitleList(secondaryTitles,"sub-title", writer); 
                if(credits != null) {
index 926b0c247681463f4b1d97e17d20dee98fb5a74d..63687071caf92ab003a3e6acf6784a96aa1e5491 100644 (file)
@@ -25,50 +25,101 @@ import java.io.InputStream;
 import java.io.InvalidClassException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Properties;
 
 import org.apache.commons.io.FileUtils;
 
 public class ProgrammeCache {
-       private File cacheFile;
-       private Map<String,TvGidsProgrammeDetails> cache;
+       private Connection db;
+       private Config config;
+       private PreparedStatement getStatement;
+       private PreparedStatement putStatement;
        
-       public ProgrammeCache(File cacheFile) {
-               this.cacheFile = cacheFile;
-               if (cacheFile.canRead()) {
-                       try {
-                               cache = (Map<String,TvGidsProgrammeDetails>) new ObjectInputStream( new FileInputStream( cacheFile ) ).readObject();
-                       } catch (InvalidClassException e) {
-                               // TODO Auto-generated catch block
-                               cache = new HashMap<String,TvGidsProgrammeDetails>();
-                       } catch (ClassNotFoundException e) {
-                               // TODO Auto-generated catch block
-                               cache = new HashMap<String,TvGidsProgrammeDetails>();
-                       } catch (FileNotFoundException e) {
-                               // TODO Auto-generated catch block
-                               e.printStackTrace();
-                               cache = new HashMap<String,TvGidsProgrammeDetails>();
-                       } catch (IOException e) {
-                               // TODO Auto-generated catch block
+       public ProgrammeCache(Config config) {
+               this.config = config;
+        try {
+                       db = DriverManager.getConnection(config.cacheDbHandle, config.cacheDbUser, config.cacheDbPassword);
+                       Statement stat = db.createStatement();
+                       stat.execute("CREATE TABLE IF NOT EXISTS cache (id VARCHAR(64) PRIMARY KEY, date DATE, programme OTHER)");
+                       stat.close();
+                       
+                       getStatement = db.prepareStatement("SELECT programme FROM cache WHERE id=?");
+                       putStatement = db.prepareStatement("INSERT INTO cache VALUES (?,?,?)");
+               } catch (SQLException e) {
+                       db = null;
+                       if (!config.quiet) {
+                               System.out.println("Unable to open cache database, proceeding without cache");
                                e.printStackTrace();
-                               cache = new HashMap<String,TvGidsProgrammeDetails>();
                        }
-               } else {
-                       cache = new HashMap<String,TvGidsProgrammeDetails>();
                }
-               // FileUtils.forceMkdir(root);
        }
        
-       public TvGidsProgrammeDetails getDetails(String id) {
-               return cache.get(id);
+       public Programme get(String id) {
+               if (db==null) return null;
+               try {
+                       getStatement.setString(1, id);
+                       ResultSet r = getStatement.executeQuery();
+                       if (!r.next()) return null; // not found
+                       return (Programme) r.getObject("programme");
+               } catch (SQLException e) {
+                       if (!config.quiet) {
+                               e.printStackTrace();
+                       }
+                       return null;
+               }
        }
        
-       public void add(String id, TvGidsProgrammeDetails d) {
-               cache.put(id, d);
+       public void put(String id, Programme prog) {
+               if (db == null) return;
+               try {
+                       putStatement.setString(1, id);
+                       putStatement.setDate(2, new java.sql.Date(prog.startTime.getTime()));
+                       putStatement.setObject(3, prog);
+                       int count = putStatement.executeUpdate();
+                       if (count!=1 && !config.quiet) {
+                               System.out.println("Weird, cache database update statement affected " + count + " rows");
+                       }
+               } catch (SQLException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
        }
-       
+
+       public void cleanup() {
+               Statement stat;
+               try {
+                       stat = db.createStatement();
+                       int count = stat.executeUpdate("DELETE FROM cache WHERE date<CURRENT_DATE - 3 DAY");
+                       if (!config.quiet) {
+                               System.out.println("Purged " + count + " old entries from cache");
+                       }
+                       stat.close();
+               } catch (SQLException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+       }
+
        public void close() throws FileNotFoundException, IOException {
-               new ObjectOutputStream( new FileOutputStream(cacheFile)).writeObject(cache);
+               cleanup();
+               if (db != null) {
+                       try {
+                               getStatement.close();
+                               putStatement.close();
+                               db.close();
+                       } catch (SQLException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                       }
+               }
        }
 }
index 69fff39a2f55dd4dcb0012ddd3e050c9829daa5b..86d8495bc0323d301c3eb8870db01c3f225cc512 100644 (file)
@@ -58,7 +58,7 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
        private static final String detail_url="http://www.rtl.nl/active/epg_data/uitzending_data/";\r
        private static final String icon_url="http://www.rtl.nl/service/gids/components/vaste_componenten/";\r
        private static final String xmltv_channel_suffix = ".rtl.nl";\r
-       private static final int MAX_PROGRAMMES_PER_DAY = 200000;\r
+       private static final int MAX_PROGRAMMES_PER_DAY = 20;\r
        \r
        private Connection db;\r
        \r
@@ -66,6 +66,7 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
                        "site_path", "wwwadres", "presentatie", "omroep", "eindtijd", "inhoud", "tt_inhoud", "alginhoud", "afl_titel", "kijkwijzer" };\r
                \r
        Map<String,Integer> xmlKeyMap = new HashMap<String,Integer>();\r
+       private ProgrammeCache cache;\r
        \r
        class RTLException extends Exception {\r
                public RTLException(String s) {\r
@@ -75,6 +76,7 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
        \r
        public RTL(Config config, boolean useDB) {\r
                super(config);\r
+               cache = new ProgrammeCache(config);\r
                try {\r
                        if (useDB) {\r
                                Properties dbProp = new Properties();\r
@@ -261,9 +263,7 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
                } else if (tag.equals("omroep")) {\r
                } else if (tag.equals("kijkwijzer")) {\r
                } else if (tag.equals("presentatie")) {\r
-                       // A\r
-                       // A en B\r
-                       // A, B, C en D\r
+                       // A; A en B; A, B, C en D\r
                        String[] presentatoren = e.getTextContent().split(", | en ");\r
                        for(String pres:presentatoren) {\r
                                prog.addPresenter(pres);\r
@@ -299,7 +299,7 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
                List<Programme> result = new LinkedList<Programme>();\r
                Map<String,Channel> channelMap = new HashMap<String,Channel>();\r
                for(Channel c: channels) {\r
-                       if (c.enabled) channelMap.put(c.id, c);\r
+                       if (c.enabled && c.source==Channel.CHANNEL_SOURCE_RTL) channelMap.put(c.id, c);\r
                }\r
                URL url = programmeUrl(day);\r
                //String xmltext = fetchURL(url);\r
@@ -328,18 +328,30 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
                                String programme_id = p.getString(2);\r
                                String quark1 = p.getString(3);\r
                                String quark2 = p.getString(4);\r
-                               Programme prog = new Programme();\r
-                               prog.addTitle(title);\r
-                               Date start = parseTime(date, starttime);\r
-                               prog.startTime = start;\r
-                               prog.channel = channelMap.get(id);\r
-                               fetchDetail(prog, date, programme_id);\r
+                               Programme prog = cache.get(programme_id);\r
+                               if (prog == null) {\r
+                                       stats.cacheMisses++;\r
+                                       prog = new Programme();\r
+                                       prog.addTitle(title);\r
+                                       prog.startTime = parseTime(date, starttime);\r
+                                       prog.channel = channelMap.get(id).getXmltvChannelId();\r
+                                       if (fetchDetails) {\r
+                                               fetchDetail(prog, date, programme_id);\r
+                                       }\r
+                                       cache.put(programme_id, prog);\r
+                               } else {\r
+                                       stats.cacheHits++;\r
+                               }\r
                                result.add(prog);\r
                        }\r
                }\r
                return result;\r
        }\r
 \r
+       public void close() throws FileNotFoundException, IOException {\r
+               super.close();\r
+               cache.close();\r
+       }\r
        private Date parseTime(Date date, String time) {\r
                Calendar result = Calendar.getInstance();\r
                result.setTime(date);\r
@@ -381,12 +393,18 @@ public class RTL extends AbstractEPGSource implements EPGSource  {
                        writer.writeStartElement("tv");\r
                        for(Channel c: channels) {c.serialize(writer);}\r
                        writer.flush();\r
-                       //List<Programme> programmes = rtl.getProgrammes1(channels.subList(6, 9), 0, true);\r
-                       List<Programme> programmes = rtl.getProgrammes1(channels, 0, true);\r
+                       List<Programme> programmes = rtl.getProgrammes1(channels.subList(6, 9), 0, true);\r
+                       //List<Programme> programmes = rtl.getProgrammes1(channels, 0, true);\r
                        for(Programme p: programmes) {p.serialize(writer);}\r
                        writer.writeEndElement();\r
                        writer.writeEndDocument();\r
                        writer.flush();\r
+                       if (!config.quiet) {\r
+                               EPGSource.Stats stats = rtl.getStats();\r
+                               System.out.println("Number of programmes from cache: " + stats.cacheHits);\r
+                               System.out.println("Number of programmes fetched: " + stats.cacheMisses);\r
+                               System.out.println("Number of fetch errors: " + stats.fetchErrors);\r
+                       }\r
                        rtl.close();\r
                } catch (Exception e) {\r
                        // TODO Auto-generated catch block\r
diff --git a/src/main/java/org/vanbest/xmltv/TvGidsProgrammeCache.java b/src/main/java/org/vanbest/xmltv/TvGidsProgrammeCache.java
new file mode 100644 (file)
index 0000000..90afc63
--- /dev/null
@@ -0,0 +1,74 @@
+package org.vanbest.xmltv;
+
+/*
+  Copyright (c) 2012 Jan-Pascal van Best <janpascal@vanbest.org>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  The full license text can be found in the LICENSE file.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidClassException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+
+public class TvGidsProgrammeCache {
+       private File cacheFile;
+       private Map<String,TvGidsProgrammeDetails> cache;
+       
+       public TvGidsProgrammeCache(File cacheFile) {
+               this.cacheFile = cacheFile;
+               if (cacheFile.canRead()) {
+                       try {
+                               cache = (Map<String,TvGidsProgrammeDetails>) new ObjectInputStream( new FileInputStream( cacheFile ) ).readObject();
+                       } catch (InvalidClassException e) {
+                               // TODO Auto-generated catch block
+                               cache = new HashMap<String,TvGidsProgrammeDetails>();
+                       } catch (ClassNotFoundException e) {
+                               // TODO Auto-generated catch block
+                               cache = new HashMap<String,TvGidsProgrammeDetails>();
+                       } catch (FileNotFoundException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                               cache = new HashMap<String,TvGidsProgrammeDetails>();
+                       } catch (IOException e) {
+                               // TODO Auto-generated catch block
+                               e.printStackTrace();
+                               cache = new HashMap<String,TvGidsProgrammeDetails>();
+                       }
+               } else {
+                       cache = new HashMap<String,TvGidsProgrammeDetails>();
+               }
+               // FileUtils.forceMkdir(root);
+       }
+       
+       public TvGidsProgrammeDetails getDetails(String id) {
+               return cache.get(id);
+       }
+       
+       public void add(String id, TvGidsProgrammeDetails d) {
+               cache.put(id, d);
+       }
+       
+       public void close() throws FileNotFoundException, IOException {
+               new ObjectOutputStream( new FileOutputStream(cacheFile)).writeObject(cache);
+       }
+}
index b2cb8933a4d0f131220603d1dd236285b35c5f56..e8ea472ad59ee38f741629d5c0665578a9ccaa6c 100644 (file)
@@ -59,7 +59,6 @@ public class XmlTvWriter {
                for(Channel c: channels) {
                        if (!c.enabled) continue;
                        c.serialize(writer);
-                       writeln();              
                }
        }