Java tutorial
/* This file is part of JFLICKS. JFLICKS 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 3 of the License, or (at your option) any later version. JFLICKS 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. You should have received a copy of the GNU General Public License along with JFLICKS. If not, see <http://www.gnu.org/licenses/>. */ package org.jflicks.tv.programdata.sd; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; import java.util.Set; import net.sf.xtvdclient.xtvd.datatypes.Xtvd; import net.sf.xtvdclient.xtvd.parser.Parser; import net.sf.xtvdclient.xtvd.parser.ParserFactory; import org.apache.commons.codec.binary.Hex; import org.jflicks.tv.ChannelLogo; import org.jflicks.tv.programdata.sd.json.Client; import org.jflicks.tv.programdata.sd.json.GuideRequest; import org.jflicks.tv.programdata.sd.json.Logo; import org.jflicks.tv.programdata.sd.json.Mapping; import org.jflicks.tv.programdata.sd.json.Program; import org.jflicks.tv.programdata.sd.json.Station; import org.jflicks.tv.programdata.sd.json.StationID; import org.jflicks.tv.programdata.sd.json.StationSchedule; import org.jflicks.tv.programdata.sd.json.UserLineup; import org.jflicks.util.LogUtil; import org.jflicks.util.Util; /** * This class gets data from Schedules Direct. * * @author Doug Barnum * @version 1.0 */ public class SchedulesDirect { private File workingFile; private ArrayList<ChannelLogo> channelLogoList; private static HashMap<String, Program> programMap; private static SchedulesDirect instance; static { System.setProperty("propertiesDirectory", "conf"); instance = new SchedulesDirect(); instance.setChannelLogoList(new ArrayList<ChannelLogo>()); if (!instance.readCache()) { instance.programMap = new HashMap<String, Program>(); } } private SchedulesDirect() { } public static SchedulesDirect getInstance() { return (instance); } private ArrayList<ChannelLogo> getChannelLogoList() { return (channelLogoList); } private void setChannelLogoList(ArrayList<ChannelLogo> l) { channelLogoList = l; } private void clearChannelLogoList() { if (channelLogoList != null) { channelLogoList.clear(); } } private void addChannelLogo(ChannelLogo cl) { if ((channelLogoList != null) && (cl != null)) { if (!channelLogoList.contains(cl)) { channelLogoList.add(cl); } } } public ChannelLogo[] getChannelLogos() { ChannelLogo[] result = null; if ((channelLogoList != null) && (channelLogoList.size() > 0)) { result = channelLogoList.toArray(new ChannelLogo[channelLogoList.size()]); } return (result); } private boolean readCache() { boolean result = false; ObjectInputStream ois = null; try { File f = new File("programMap.ser"); if (f.exists()) { FileInputStream fis = new FileInputStream(f); ois = new ObjectInputStream(fis); programMap = (HashMap<String, Program>) ois.readObject(); result = true; } } catch (IOException | ClassNotFoundException ex) { ex.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException ex) { } } } return (result); } private void writeCache() { ObjectOutputStream oos = null; try { File f = new File("programMap.ser"); FileOutputStream fos = new FileOutputStream(f); oos = new ObjectOutputStream(fos); oos.writeObject(programMap); } catch (IOException ex) { LogUtil.log(LogUtil.WARNING, "writeCache: " + ex.getMessage()); } finally { if (oos != null) { try { oos.close(); } catch (IOException ex) { } } } } /** * The XML data is first downloaded and then parsed for information. * * @return A File instance. */ public File getWorkingFile() { return (workingFile); } private void setWorkingFile(File f) { workingFile = f; } private HashMap<String, Program> getProgramMap() { return (programMap); } private void unmark() { Set<Map.Entry<String, Program>> set = programMap.entrySet(); Iterator<Map.Entry<String, Program>> iter = set.iterator(); while (iter.hasNext()) { Map.Entry<String, Program> me = iter.next(); Program p = me.getValue(); p.setMarked(false); } } private void mark(String pid) { if (pid != null) { Set<Map.Entry<String, Program>> set = programMap.entrySet(); Iterator<Map.Entry<String, Program>> iter = set.iterator(); while (iter.hasNext()) { Map.Entry<String, Program> me = iter.next(); String key = me.getKey(); if (pid.equals(key)) { Program p = me.getValue(); p.setMarked(true); } } } } private void mark(String[] array) { if ((array != null) && (array.length > 0)) { for (int i = 0; i < array.length; i++) { mark(array[i]); } } } private void purge() { ArrayList<String> purgeList = new ArrayList<String>(); Set<Map.Entry<String, Program>> set = programMap.entrySet(); Iterator<Map.Entry<String, Program>> iter = set.iterator(); while (iter.hasNext()) { Map.Entry<String, Program> me = iter.next(); String pid = me.getKey(); Program p = me.getValue(); if (!p.isMarked()) { purgeList.add(pid); } } if (purgeList.size() > 0) { for (int i = 0; i < purgeList.size(); i++) { String pid = purgeList.get(i); programMap.remove(pid); } } } private boolean haveInCache(String pid, String md5) { boolean result = false; if ((pid != null) && (md5 != null)) { Set<Map.Entry<String, Program>> set = programMap.entrySet(); Iterator<Map.Entry<String, Program>> iter = set.iterator(); while (iter.hasNext()) { Map.Entry<String, Program> me = iter.next(); String key = me.getKey(); if (pid.equals(key)) { Program p = me.getValue(); if (md5.equals(p.getMd5())) { result = true; } break; } } } return (result); } private void putInCache(Program p) { if (p != null) { programMap.put(p.getProgramID(), p); } } private StationID[] getStationIDsByLineupName(String name) { StationID[] result = null; if (name != null) { name = name + ".properties"; LogUtil.log(LogUtil.DEBUG, "looking to conf file <" + name + ">"); File conf = new File("conf"); File pfile = new File(conf, name); Properties p = Util.findProperties(pfile); if (p != null) { Set<String> set = p.stringPropertyNames(); if (set != null) { String[] tags = set.toArray(new String[set.size()]); if ((tags != null) && (tags.length > 0)) { result = new StationID[tags.length]; for (int i = 0; i < result.length; i++) { String val = p.getProperty(tags[i]); if (val != null) { int index = val.indexOf("|"); if (index >= 0) { StationID tmp = new StationID(); tmp.setStationID(tags[i]); tmp.setChannel(val.substring(0, index)); result[i] = tmp; } } } } } } } return (result); } private boolean isWanted(StationID[] array, String sid, String channel) { boolean result = false; if ((array != null) && (array.length > 0) && (sid != null) && (channel != null)) { for (int i = 0; i < array.length; i++) { if ((sid.equals(array[i].getStationID())) && (channel.equals(array[i].getChannel()))) { result = true; break; } } } return (result); } private String[] getStationsByLineupName(String name) { String[] result = null; if (name != null) { name = name + ".properties"; LogUtil.log(LogUtil.DEBUG, "looking to conf file <" + name + ">"); File conf = new File("conf"); File pfile = new File(conf, name); Properties p = Util.findProperties(pfile); if (p != null) { Set<String> set = p.stringPropertyNames(); if (set != null) { result = set.toArray(new String[set.size()]); } } } return (result); } private ArrayList<String[]> computeList(String[] array, int max) { ArrayList<String[]> result = null; if ((array != null) && (array.length > 0)) { result = new ArrayList<String[]>(); int count = array.length / max; if ((array.length % max) > 0) { count++; } int broken = 0; int start = 0; int end = max; for (int i = 0; i < count; i++) { if (end > array.length) { end = array.length; } String[] sub = Arrays.copyOfRange(array, start, end); broken += sub.length; result.add(sub); end += max; start += max; } } return (result); } private Client getClient(String user, String password, String country, String zip) { Client result = null; if ((user != null) && (password != null) && (country != null) && (zip != null)) { try { MessageDigest md = MessageDigest.getInstance("SHA-1"); md.reset(); md.update(password.getBytes()); String sha = new String(Hex.encodeHex(md.digest())); Client c = new Client(); if (c.doToken(user, sha)) { if (c.doStatus()) { LogUtil.log(LogUtil.DEBUG, "SD json server status ok\n"); if (c.doHeadend(country, zip)) { result = c; } } } } catch (Exception ex) { LogUtil.log(LogUtil.WARNING, "getClient: " + ex.getMessage()); } } return (result); } private String secondsToDuration(int seconds) { String result = null; StringBuilder sb = new StringBuilder("PT"); int lsec = seconds; int hours = lsec / 3600; lsec = lsec % 3600; int minutes = lsec / 60; if (hours < 10) { sb.append("0"); } sb.append(hours); sb.append("H"); if (minutes < 10) { sb.append("0"); } sb.append(minutes); sb.append("M"); result = sb.toString(); return (result); } /** * The major work method here will trigger a call to the web service * and parse the resulting data into the Xtvd object model. * * @return A Xtvd instance. */ public Xtvd getXtvd(String user, String password, String country, String zipcode) { LogUtil.log(LogUtil.DEBUG, "getXtvd user " + user); LogUtil.log(LogUtil.DEBUG, "getXtvd password " + password); LogUtil.log(LogUtil.DEBUG, "getXtvd country " + country); LogUtil.log(LogUtil.DEBUG, "getXtvd zipcode " + zipcode); Xtvd result = null; // We now use the JSON service and make our own Xtvd // instance. Then very little other code needs to change. result = new Xtvd(); // Clear our ChannelLogo list. clearChannelLogoList(); Client c = getClient(user, password, country, zipcode); if (c != null) { UserLineup ul = c.getUserLineup(); if (ul != null) { String[] lineupNames = handleLineup(result, c, ul.getLineups()); if ((lineupNames != null) && (lineupNames.length > 0)) { ArrayList<net.sf.xtvdclient.xtvd.datatypes.Schedule> schedlist = new ArrayList<net.sf.xtvdclient.xtvd.datatypes.Schedule>(); ArrayList<String> md5list = new ArrayList<String>(); for (int i = 0; i < lineupNames.length; i++) { LogUtil.log(LogUtil.DEBUG, lineupNames[i]); String[] sids = getStationsByLineupName(lineupNames[i]); if ((sids != null) && (sids.length > 0)) { GuideRequest[] grs = new GuideRequest[sids.length]; for (int j = 0; j < grs.length; j++) { grs[j] = new GuideRequest(sids[j]); } StationSchedule[] sched = c.getGuide(grs); LogUtil.log(LogUtil.DEBUG, sched.toString()); if ((sched != null) && (sched.length > 0)) { // Make an xtvd Schedule. for (int j = 0; j < sched.length; j++) { Program[] progs = sched[j].getPrograms(); if ((progs != null) && (progs.length > 0)) { for (int k = 0; k < progs.length; k++) { net.sf.xtvdclient.xtvd.datatypes.Schedule xsched = new net.sf.xtvdclient.xtvd.datatypes.Schedule(); xsched.setStation(Util.str2int(sched[j].getStationID(), 0)); xsched.setProgram(progs[k].getProgramID()); try { net.sf.xtvdclient.xtvd.datatypes.DateTime dt = new net.sf.xtvdclient.xtvd.datatypes.DateTime( progs[k].getAirDateTime()); xsched.setTime(dt); } catch (Exception ex) { LogUtil.log(LogUtil.WARNING, ex.getMessage()); } String durformat = secondsToDuration(progs[k].getDuration()); net.sf.xtvdclient.xtvd.datatypes.Duration dur = new net.sf.xtvdclient.xtvd.datatypes.Duration( durformat); xsched.setDuration(dur); schedlist.add(xsched); md5list.add(progs[k].getMd5()); } } } } else { LogUtil.log(LogUtil.INFO, "NOT found sched"); } } else { LogUtil.log(LogUtil.INFO, "NOT found conf file"); } } result.setSchedules(schedlist); // First unmark all Programs in our map. unmark(); // Now we have to get the programs ArrayList<String> allPidlist = new ArrayList<String>(); ArrayList<String> pidlist = new ArrayList<String>(); for (int i = 0; i < schedlist.size(); i++) { String pid = schedlist.get(i).getProgram(); String md5 = md5list.get(i); allPidlist.add(pid); if (!haveInCache(pid, md5)) { pidlist.add(pid); } } // The pidlist has all the Programs we have to fetch. LogUtil.log(LogUtil.DEBUG, "we have to fetch " + pidlist.size() + " programs."); if (pidlist.size() > 0) { String[] parray = pidlist.toArray(new String[pidlist.size()]); ArrayList<String[]> alist = computeList(parray, 1000); if ((alist != null) && (alist.size() > 0)) { for (int i = 0; i < alist.size(); i++) { String[] sub = alist.get(i); Program[] progs = c.getPrograms(sub); if ((progs != null) && (progs.length > 0)) { for (int j = 0; j < progs.length; j++) { putInCache(progs[j]); } } } } } // Next mark all Programs in our map. String[] parray = allPidlist.toArray(new String[allPidlist.size()]); mark(parray); // Now purge old programs. purge(); // We have to convert our cache programs to xtvd programs. result.setPrograms(handlePrograms()); // Finally lets write the cache. writeCache(); } } } return (result); } private String[] handleLineup(Xtvd xtvd, Client c, org.jflicks.tv.programdata.sd.json.Lineup[] array) { String[] result = null; if ((c != null) && (array != null) && (array.length > 0)) { ArrayList<String> lineuplist = new ArrayList<String>(); HashMap<String, net.sf.xtvdclient.xtvd.datatypes.Lineup> hm = new HashMap<String, net.sf.xtvdclient.xtvd.datatypes.Lineup>(); HashMap<Integer, net.sf.xtvdclient.xtvd.datatypes.Station> smap = new HashMap<Integer, net.sf.xtvdclient.xtvd.datatypes.Station>(); for (int i = 0; i < array.length; i++) { net.sf.xtvdclient.xtvd.datatypes.Lineup l = new net.sf.xtvdclient.xtvd.datatypes.Lineup(); LogUtil.log(LogUtil.DEBUG, array[i].getName()); String mapName = array[i].getName(); LogUtil.log(LogUtil.DEBUG, mapName); Mapping jmap = c.getMapping(mapName); String lineupValidName = null; lineuplist.add(array[i].toString()); l.setId(array[i].getId()); l.setName(array[i].toString()); l.setLocation(array[i].getLocation()); String type = array[i].getTransport(); if (type != null) { if (type.equalsIgnoreCase("cable")) { l.setType(net.sf.xtvdclient.xtvd.datatypes.LineupTypes.CABLE); } else if (type.equalsIgnoreCase("cable digital")) { l.setType(net.sf.xtvdclient.xtvd.datatypes.LineupTypes.CABLE_DIGITAL); } else if (type.equalsIgnoreCase("satellite")) { l.setType(net.sf.xtvdclient.xtvd.datatypes.LineupTypes.SATELLITE); } else if (type.equalsIgnoreCase("local broadcast")) { l.setType(net.sf.xtvdclient.xtvd.datatypes.LineupTypes.LOCAL_BROADCAST); } else { l.setType(net.sf.xtvdclient.xtvd.datatypes.LineupTypes.LOCAL_BROADCAST); } } if (jmap != null) { ArrayList<net.sf.xtvdclient.xtvd.datatypes.Map> mlist = handleStationMap(jmap, array[i]); l.setMaps(mlist); // We build ChannelLogo instances from a jmap. channelLogosFromMapping(jmap); } hm.put(array[i].toString(), l); // Now set all the stations. handleStations(smap, jmap, l.getName()); } xtvd.setLineups(hm); xtvd.setStations(smap); if (lineuplist.size() > 0) { result = lineuplist.toArray(new String[lineuplist.size()]); } } return (result); } private ArrayList<net.sf.xtvdclient.xtvd.datatypes.Map> handleStationMap(Mapping m, org.jflicks.tv.programdata.sd.json.Lineup l) { ArrayList<net.sf.xtvdclient.xtvd.datatypes.Map> result = null; if (m != null) { StationID[] sids = m.getMap(); if ((sids != null) && (sids.length > 0)) { StationID[] wanted = getStationIDsByLineupName(l.toString()); if ((wanted != null) && (wanted.length > 0)) { result = new ArrayList<net.sf.xtvdclient.xtvd.datatypes.Map>(); for (int i = 0; i < sids.length; i++) { String currentsid = sids[i].getStationID(); String currentch = sids[i].getChannel(); if (currentch == null) { currentch = sids[i].getAtscMajor() + "." + sids[i].getAtscMinor(); } if (isWanted(wanted, currentsid, currentch)) { net.sf.xtvdclient.xtvd.datatypes.Map map = new net.sf.xtvdclient.xtvd.datatypes.Map(); map.setStation(Util.str2int(currentsid, 0)); String ch = sids[i].getChannel(); if (ch == null) { map.setChannel("" + sids[i].getAtscMajor()); map.setChannelMinor(sids[i].getAtscMinor()); } else { map.setChannel(ch); } result.add(map); } } } } } return (result); } private void handleStations(HashMap<Integer, net.sf.xtvdclient.xtvd.datatypes.Station> hm, Mapping m, String lineupName) { LogUtil.log(LogUtil.DEBUG, lineupName); if (m != null) { org.jflicks.tv.programdata.sd.json.Station[] array = getStations(m, lineupName); if ((array != null) && (array.length > 0)) { for (int i = 0; i < array.length; i++) { int sid = Util.str2int(array[i].getStationID(), 0); net.sf.xtvdclient.xtvd.datatypes.Station station = new net.sf.xtvdclient.xtvd.datatypes.Station(); station.setId(sid); station.setCallSign(array[i].getCallsign()); station.setName(array[i].getName()); org.jflicks.tv.programdata.sd.json.StationID sidobj = getStationID(m, array[i].getStationID()); if (sidobj != null) { station.setFccChannelNumber(sidobj.getUhfVhf()); } hm.put(Integer.valueOf(sid), station); } } } } private org.jflicks.tv.programdata.sd.json.Station[] getStations(Mapping m, String lineupName) { org.jflicks.tv.programdata.sd.json.Station[] result = null; if ((m != null) && (lineupName != null)) { String[] sids = getStationsByLineupName(lineupName); if ((sids != null) && (sids.length > 0)) { ArrayList<org.jflicks.tv.programdata.sd.json.Station> list = new ArrayList<org.jflicks.tv.programdata.sd.json.Station>(); for (int i = 0; i < sids.length; i++) { org.jflicks.tv.programdata.sd.json.Station station = getStationById(m.getStations(), sids[i]); if (station != null) { list.add(station); } } if (list.size() > 0) { result = list.toArray(new org.jflicks.tv.programdata.sd.json.Station[list.size()]); } } } return (result); } private org.jflicks.tv.programdata.sd.json.Station getStationById( org.jflicks.tv.programdata.sd.json.Station[] array, String sid) { org.jflicks.tv.programdata.sd.json.Station result = null; if ((array != null) && (array.length > 0) && (sid != null)) { for (int i = 0; i < array.length; i++) { if (sid.equals(array[i].getStationID())) { result = array[i]; break; } } } return (result); } private org.jflicks.tv.programdata.sd.json.StationID getStationID(Mapping m, String sid) { org.jflicks.tv.programdata.sd.json.StationID result = null; if ((m != null) && (sid != null)) { org.jflicks.tv.programdata.sd.json.StationID[] all = m.getMap(); if ((all != null) && (all.length > 0)) { for (int i = 0; i < all.length; i++) { if (sid.equals(all[i].getStationID())) { result = all[i]; break; } } } } return (result); } private HashMap<String, net.sf.xtvdclient.xtvd.datatypes.Program> handlePrograms() { HashMap<String, net.sf.xtvdclient.xtvd.datatypes.Program> result = new HashMap<String, net.sf.xtvdclient.xtvd.datatypes.Program>(); Set<Map.Entry<String, Program>> set = programMap.entrySet(); Iterator<Map.Entry<String, Program>> iter = set.iterator(); while (iter.hasNext()) { Map.Entry<String, Program> me = iter.next(); String pid = me.getKey(); Program p = me.getValue(); net.sf.xtvdclient.xtvd.datatypes.XtvdDate xdate = null; String orig = p.getOriginalDateTime(); if (orig != null) { try { xdate = new net.sf.xtvdclient.xtvd.datatypes.XtvdDate(orig); } catch (Exception ex) { LogUtil.log(LogUtil.WARNING, ex.getMessage()); } } net.sf.xtvdclient.xtvd.datatypes.Program xp = new net.sf.xtvdclient.xtvd.datatypes.Program(); xp.setId(p.getProgramID()); xp.setTitle(p.getBestTitle()); xp.setSubtitle(p.getEpisodeTitle150()); xp.setDescription(p.getBestDescription()); xp.setShowType(p.getShowType()); //xp.setSyndicatedEpisodeNumber(p.getEpisode()); xp.setSeries(p.getSeriesId()); xp.setOriginalAirDate(xdate); result.put(pid, xp); } return (result); } private void channelLogosFromMapping(Mapping m) { if (m != null) { Station[] array = m.getStations(); if ((array != null) && (array.length > 0)) { for (int i = 0; i < array.length; i++) { String stationID = array[i].getStationID(); Logo l = array[i].getLogo(); if ((l != null) && (stationID != null)) { // Turn id string to an int. int cid = Util.str2int(stationID, -1); if (cid != -1) { ChannelLogo cl = new ChannelLogo(); cl.setChannelId(cid); cl.setUrl(l.getUrl()); cl.setWidth(l.getWidth()); cl.setHeight(l.getHeight()); cl.setMd5(l.getMd5()); addChannelLogo(cl); LogUtil.log(LogUtil.DEBUG, "channelId: " + cl.getChannelId()); LogUtil.log(LogUtil.DEBUG, "url: " + cl.getUrl()); LogUtil.log(LogUtil.DEBUG, "width: " + cl.getWidth()); LogUtil.log(LogUtil.DEBUG, "height: " + cl.getHeight()); LogUtil.log(LogUtil.DEBUG, "md5: " + cl.getMd5()); } } } } } } }