/* 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.nms.system; import java.io.File; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import org.jflicks.autoart.AutoArt; import org.jflicks.configure.BaseConfiguration; import org.jflicks.configure.Configuration; import org.jflicks.configure.J4ccConfiguration; import org.jflicks.configure.J4ccRecorder; import org.jflicks.configure.NameValue; import org.jflicks.nms.BaseNMS; import org.jflicks.nms.NMSConstants; import org.jflicks.photomanager.PhotoManager; import org.jflicks.trailer.Trailer; import org.jflicks.tv.Listing; import org.jflicks.tv.live.Live; import org.jflicks.tv.ondemand.OnDemand; import org.jflicks.tv.postproc.PostProc; import org.jflicks.tv.programdata.ProgramData; import org.jflicks.tv.recorder.Recorder; import org.jflicks.tv.scheduler.Scheduler; import org.jflicks.util.ExtensionsFilter; import org.jflicks.util.LogUtil; import org.jflicks.util.Util; import org.jflicks.videomanager.VideoManager; import fi.iki.elonen.SimpleWebServer; /** * This is our implementation of an NMS. * * @author Doug Barnum * @version 1.0 */ public class SystemNMS extends BaseNMS { private SimpleWebServer simpleWebServer; /** * Default empty constructor. */ public SystemNMS() { setTitle("SystemNMS"); saveDefaultConfigurations(); } public void startWebServer() { if (simpleWebServer == null) { int port = getConfiguredStreamPort(); ArrayList<File> rootDirs= new ArrayList<File>(); String[] paths = getConfiguredStreamPaths(); if ((paths != null) && (paths.length > 0)) { for (int i = 0; i < paths.length; i++) { rootDirs.add(new File(paths[i])); } } simpleWebServer = new SimpleWebServer(null, port, rootDirs, true); try { simpleWebServer.start(); } catch (Exception ex) { LogUtil.log(LogUtil.INFO, "simpleWebServer problem: " + ex.getMessage()); simpleWebServer = null; } } } public void stopWebServer() { if (simpleWebServer != null) { simpleWebServer.stop(); simpleWebServer = null; } } private File createConfigurationFile(Configuration c) { File result = null; if (c != null) { File db = new File("db"); if ((db.exists()) && (db.isDirectory())) { String s = c.getName() + "-" + c.getSource() + ".properties"; // We have to do something with path / chars. Crap. We // will just have to turn into something odd... s = s.replaceAll("/", "SLASH"); result = new File(db, s); } } return (result); } /** * {@inheritDoc} */ public Configuration[] getConfigurations() { Configuration[] result = null; File db = new File("db"); if ((db.exists()) && (db.isDirectory())) { String[] props = { "properties" }; ExtensionsFilter exts = new ExtensionsFilter(props); File[] fprops = db.listFiles(exts); if ((fprops != null) && (fprops.length > 0)) { ArrayList<Configuration> l = new ArrayList<Configuration>(); for (int i = 0; i < fprops.length; i++) { Configuration c = fromProperties(Util.findProperties(fprops[i])); if (c != null) { l.add(c); } } if (l.size() > 0) { result = l.toArray(new Configuration[l.size()]); examineOnDemand(result); examineScheduler(result); Arrays.sort(result, new ConfigurationSortByName()); } } } return (result); } /** * {@inheritDoc} */ public J4ccConfiguration getJ4ccConfiguration() { J4ccConfiguration result = new J4ccConfiguration(); result.setHost(getHost()); result.setPort(getPort()); Scheduler s = getScheduler(); if (s != null) { String[] paths = s.getConfiguredRecordingDirectories(); String[] listings = s.getConfiguredListingNames(); result.setListings(listings); result.setPaths(paths); Configuration c = getConfigurationBySource("Schedules Direct"); if (c != null) { NameValue nv = c.findNameValueByName(NMSConstants.USER_NAME); if (nv != null) { result.setUserName(nv.getValue()); } nv = c.findNameValueByName(NMSConstants.PASSWORD); if (nv != null) { result.setPassword(nv.getValue()); } nv = c.findNameValueByName(NMSConstants.ZIP_CODE); if (nv != null) { result.setZipCode(nv.getValue()); } } Configuration[] hdhrs = findHdHrConfigurations(getConfigurations()); if ((hdhrs != null) && (hdhrs.length > 0)) { J4ccRecorder[] rarray = new J4ccRecorder[hdhrs.length]; for (int i = 0; i < rarray.length; i++) { rarray[i] = new J4ccRecorder(); String source = hdhrs[i].getSource(); rarray[i].setName(source); String device = source.substring(10); Recorder r = getRecorderByDevice(device); String listing = s.getListingNameByRecorder(r); rarray[i].setListing(listing); NameValue nv = hdhrs[i].findNameValueByName(NMSConstants.HLS_MODE); if (nv != null) { rarray[i].setHDTC(Util.str2boolean(nv.getValue(), false)); if (rarray[i].isHDTC()) { rarray[i].setTranscode(false); } else { nv = hdhrs[i].findNameValueByName(NMSConstants.RECORDING_INDEXER_NAME); if (nv != null) { String indexer = nv.getValue(); if (indexer != null) { if (indexer.equals("ToMp4EncodeWorker")) { rarray[i].setTranscode(true); } } } } } } result.setJ4ccRecorders(rarray); } } return (result); } /** * {@inheritDoc} */ public void setJ4ccConfiguration(J4ccConfiguration c) { if (c != null) { } } private Configuration[] findHdHrConfigurations(Configuration[] array) { Configuration[] result = null; if (array != null) { ArrayList<Configuration> l = new ArrayList<Configuration>(); for (int i = 0; i < array.length; i++) { if (NMSConstants.RECORDER_NAME.equals(array[i].getName())) { String source = array[i].getSource(); if ((source != null) && (source.startsWith("HDHomerun"))) { l.add(array[i]); } } } if (l.size() > 0) { result = l.toArray(new Configuration[l.size()]); } } return (result); } private Configuration findConfigurationBySource(String s, Configuration[] array) { Configuration result = null; if ((s != null) && (array != null)) { for (int i = 0; i < array.length; i++) { if (array[i].isSource(s)) { result = array[i]; break; } } } return (result); } private void saveDefaultConfigurations() { Configuration def = getDefaultConfiguration(); LogUtil.log(LogUtil.DEBUG, "SystemNMS: def: " + def); save(def, false); setConfiguration(getConfigurationBySource(def.getSource())); LogUtil.log(LogUtil.DEBUG, "SystemNMS: conf: " + getConfiguration()); Scheduler s = getScheduler(); if (s != null) { save(s.getDefaultConfiguration(), false); } Live l = getLive(); if (l != null) { save(l.getDefaultConfiguration(), false); } PostProc pp = getPostProc(); if (pp != null) { save(pp.getDefaultConfiguration(), false); } Recorder[] recorders = getRecorders(); if (recorders != null) { for (int i = 0; i < recorders.length; i++) { save(recorders[i].getDefaultConfiguration(), false); } } ProgramData[] pds = getProgramData(); if (pds != null) { for (int i = 0; i < pds.length; i++) { save(pds[i].getDefaultConfiguration(), false); } } OnDemand[] ons = getOnDemands(); if (ons != null) { for (int i = 0; i < ons.length; i++) { save(ons[i].getDefaultConfiguration(), false); } } } /** * {@inheritDoc} */ public void removeConfiguration(Configuration c) { File f = createConfigurationFile(c); if ((f != null) && (f.exists()) && (f.isFile())) { boolean result = f.delete(); if (!result) { LogUtil.log(LogUtil.WARNING, "Failed to delete " + f.getPath()); } } } /** * {@inheritDoc} */ public void save(Configuration c, boolean force) { LogUtil.log(LogUtil.DEBUG, "save: c: " + c + " force: " + force); if (c != null) { File f = createConfigurationFile(c); LogUtil.log(LogUtil.DEBUG, "save: f: " + f); if ((f != null) && (f.exists()) && (f.isFile())) { // The Configuration does exist. We over write only if // force is true. if (force) { Util.writeProperties(f, toProperties(c)); updateConfiguration(c); } } else if (f != null) { // Ok we have a good path. We will save it anyway. Util.writeProperties(f, toProperties(c)); updateConfiguration(c); } } } private void updateConfiguration(Configuration c) { LogUtil.log(LogUtil.DEBUG, "updateConfiguration c: " + c); if (c != null) { String name = c.getName(); if (name != null) { if (name.equals(NMSConstants.NMS_NAME)) { setConfiguration(c); stopWebServer(); startWebServer(); } else if (name.equals(NMSConstants.SCHEDULER_NAME)) { Scheduler s = getScheduler(); if (s != null) { s.setConfiguration(c); } } else if (name.equals(NMSConstants.PHOTO_MANAGER_NAME)) { PhotoManager pm = getPhotoManager(); if (pm != null) { pm.setConfiguration(c); } } else if (name.equals(NMSConstants.VIDEO_MANAGER_NAME)) { VideoManager vm = getVideoManager(); if (vm != null) { vm.setConfiguration(c); } } else if (name.equals(NMSConstants.AUTO_ART_NAME)) { AutoArt aa = getAutoArt(); if (aa != null) { aa.setConfiguration(c); } } else if (name.equals(NMSConstants.LIVE_NAME)) { Live l = getLive(); if (l != null) { l.setConfiguration(c); } } else if (name.equals(NMSConstants.POST_PROC_NAME)) { PostProc pp = getPostProc(); if (pp != null) { pp.setConfiguration(c); } } else if (name.equals(NMSConstants.RECORDER_NAME)) { String source = c.getSource(); if (source != null) { Recorder[] array = getRecorders(); if (array != null) { Recorder r = null; for (int i = 0; i < array.length; i++) { String tmp = array[i].getTitle() + " " + array[i].getDevice(); if (source.equals(tmp)) { r = array[i]; break; } } if (r != null) { r.setConfiguration(c); } } } } else if (name.equals(NMSConstants.PROGRAM_DATA_NAME)) { String source = c.getSource(); if (source != null) { ProgramData[] array = getProgramData(); if (array != null) { ProgramData pd = null; for (int i = 0; i < array.length; i++) { if (source.equals(array[i].getTitle())) { pd = array[i]; break; } } if (pd != null) { pd.setConfiguration(c); } } } } else if (name.equals(NMSConstants.ON_DEMAND_NAME)) { String source = c.getSource(); if (source != null) { OnDemand[] array = getOnDemands(); if (array != null) { OnDemand od = null; for (int i = 0; i < array.length; i++) { if (source.equals(array[i].getTitle())) { od = array[i]; break; } } if (od != null) { od.setConfiguration(c); } } } } else if (name.equals(NMSConstants.TRAILER_NAME)) { String source = c.getSource(); if (source != null) { Trailer[] array = getTrailers(); if (array != null) { Trailer t = null; for (int i = 0; i < array.length; i++) { if (source.equals(array[i].getTitle())) { t = array[i]; break; } } if (t != null) { t.setConfiguration(c); } } } } else { LogUtil.log(LogUtil.WARNING, "Not handling update of " + c.getName() + "-" + c.getSource()); } } } } /** * Close up all resources. */ public void close() { } private String[] getListingNames() { String[] result = null; ArrayList<String> l = new ArrayList<String>(); l.add(NMSConstants.NOT_CONNECTED); ProgramData[] array = getProgramData(); if (array != null) { for (int i = 0; i < array.length; i++) { Listing[] lists = array[i].getListings(); if (lists != null) { for (int j = 0; j < lists.length; j++) { l.add(lists[j].getName()); } } } result = l.toArray(new String[l.size()]); } return (result); } private void examineOnDemand(Configuration[] array) { if (array != null) { // We have to examine any OnDemand services that we // have deployed. Since an OnDemand service takes // a fulltime Recorder we need give them a property // to link to one. OnDemand[] ods = getOnDemands(); Recorder[] recs = getRecorders(); if ((ods != null) && (recs != null)) { boolean changed = false; ArrayList<String> rlist = new ArrayList<String>(); rlist.add(NMSConstants.NOT_CONNECTED); for (int i = 0; i < recs.length; i++) { rlist.add(recs[i].getTitle() + " " + recs[i].getDevice()); } for (int i = 0; i < ods.length; i++) { Configuration c = ods[i].getConfiguration(); BaseConfiguration work = null; for (int j = 0; j < array.length; j++) { if (array[j].equals(c)) { work = (BaseConfiguration) array[j]; break; } } if (work != null) { // We have the Configuration that we have to // examine. See if a Recorder property exists // right now. NameValue old = work.findNameValueByName( NMSConstants.RECORDING_DEVICE); if (old == null) { // We need to make one. NameValue nv = new NameValue(); nv.setName(NMSConstants.RECORDING_DEVICE); nv.setDescription( NMSConstants.RECORDING_DEVICE); nv.setValue(NMSConstants.NOT_CONNECTED); nv.setDefaultValue(NMSConstants.NOT_CONNECTED); nv.setType(nv.toType("STRING_FROM_CHOICE_TYPE")); nv.setChoices( rlist.toArray(new String[rlist.size()])); work.addNameValue(nv); changed = true; } else { // We have an old one but we may need to change // the Recorder selection. String value = old.getValue(); } if (changed) { save(work, true); } } } } } } private void examineScheduler(Configuration[] array) { if (array != null) { // We have to make sure that the Scheduler has the proper // configuration data for the ProgramData and Recorder // instances. The user has to connect these in some way // and there is no way the default configuration can preset // them. So we do a little work here to try to maintain // the Scheduler Configuration matches the current state. Configuration c = findConfigurationBySource(NMSConstants.SCHEDULER_SOURCE, array); if (c != null) { BaseConfiguration bc = (BaseConfiguration) c; // This will be at least one item called "Not Connected" String[] listings = getListingNames(); boolean changed = false; Recorder[] records = getRecorders(); if ((records != null) && (listings != null)) { for (int i = 0; i < records.length; i++) { Configuration rc = findConfigurationBySource(records[i].getTitle() + " " + records[i].getDevice(), array); if (rc != null) { NameValue old = c.findNameValueByName(rc.getSource()); if (old == null) { // We need to create one. NameValue nv = new NameValue(); nv.setName(rc.getSource()); nv.setDescription( NMSConstants.RECORDING_DEVICE); nv.setValue(listings[0]); nv.setDefaultValue(listings[0]); nv.setType( nv.toType("STRING_FROM_CHOICE_TYPE")); nv.setChoices(listings); bc.addNameValue(nv); changed = true; } else { if (!Arrays.equals(old.getChoices(), listings)) { old.setChoices(listings); changed = true; } } } } } if (changed) { save(bc, true); } } } } // This method is trying to delete old "connections" between a recorder // and program data. If the user removes a recorder or changes the // program service (less likely) then these properties hang out in the // configuration. As the system boots they are added if need be but // eventually they need to be deleted if no longer valid. It would be // nice to do this automatically but we cannot do it until all services // have started. So we have to figure out the best way to go about // calling this.... private void schedulerConnectCheck(Configuration c) { if (c != null) { BaseConfiguration bc = (BaseConfiguration) c; // Now lets remove any old connections if they no longer // are valid. This will clean up some stuff when recorders // are removed. NameValue[] all = bc.getNameValues(); if (all != null) { boolean connectchanged = false; ArrayList<NameValue> good = new ArrayList<NameValue>(); for (int i = 0; i < all.length; i++) { String name = all[i].getName(); if (name.equals(NMSConstants.RECORDING_DIRECTORIES)) { good.add(all[i]); } else { name = name.substring(name.indexOf(" ")); name = name.trim(); // OK name should be just the device name. if ((!name.endsWith("null")) && (getRecorderByDevice(name) != null)) { good.add(all[i]); } else { connectchanged = true; } } } if (connectchanged) { if (good.size() > 0) { bc.setNameValues(good.toArray( new NameValue[good.size()])); } else { bc.setNameValues(null); } save(bc, true); } } } } static class ConfigurationSortByName implements Comparator<Configuration>, Serializable { public int compare(Configuration c0, Configuration c1) { return (c0.getName().compareTo(c1.getName())); } } }