/* * Copyright (c) Henrik Niehaus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the project (Lazy Bones) nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package lazybones; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.Properties; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import org.hampelratte.svdrp.Response; import org.hampelratte.svdrp.commands.CHAN; import org.hampelratte.svdrp.commands.NEWT; import org.hampelratte.svdrp.responses.highlevel.Channel; import org.hampelratte.svdrp.responses.highlevel.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.XStream; import devplugin.ActionMenu; import devplugin.ButtonAction; import devplugin.Marker; import devplugin.Plugin; import devplugin.PluginCenterPanel; import devplugin.PluginCenterPanelWrapper; import devplugin.PluginInfo; import devplugin.PluginTreeNode; import devplugin.Program; import devplugin.ProgramReceiveTarget; import devplugin.Version; import lazybones.gui.MainDialog; import lazybones.gui.RecordingsCenterPanel; import lazybones.gui.TimelineCenterPanel; import lazybones.gui.TimersCenterPanel; import lazybones.gui.settings.VDRSettingsPanel; import lazybones.logging.DebugConsoleHandler; import lazybones.logging.PopupHandler; import lazybones.logging.SimpleFormatter; import lazybones.programmanager.ProgramDatabase; import lazybones.programmanager.ProgramManager; import tvbrowser.core.Settings; /** * A remote control plugin for VDR * * @author <a href="hampelratte@users.sf.net">hampelratte@users.sf.net</a> * */ public class LazyBones extends Plugin implements Observer { private static transient Logger logger = LoggerFactory.getLogger(LazyBones.class); /** Translator */ private static final util.ui.Localizer mLocalizer = util.ui.Localizer.getLocalizerFor(LazyBones.class); private MainDialog mainDialog; private static Properties props; private final ContextMenuFactory cmf = new ContextMenuFactory(); private static LazyBones instance; public static final String TIMER_MENU_KEY = "TIMER_MENU_KEY"; private PluginCenterPanelWrapper wrapper; private final TimerManager timerManager; private final RecordingManager recordingManager; public LazyBones() { timerManager = new TimerManager(); recordingManager = new RecordingManager(); timerManager.setRecordingManager(recordingManager); recordingManager.setTimerManager(timerManager); } public static LazyBones getInstance() { return instance; } @Override public ActionMenu getContextMenuActions(final Program program) { return cmf.createActionMenu(program); } @Override public ActionMenu getContextMenuActions(final devplugin.Channel channel) { return cmf.createChannelActionMenu(channel); } @Override public PluginCenterPanelWrapper getPluginCenterPanelWrapper() { return wrapper; } private ButtonAction buttonAction; @Override public ActionMenu getButtonAction() { buttonAction = new ButtonAction(); buttonAction.setActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { getMainDialog().setVisible(true); } }); buttonAction.setBigIcon(createImageIcon("lazybones/vdr24.png")); buttonAction.setSmallIcon(createImageIcon("lazybones/vdr16.png")); buttonAction.setShortDescription(LazyBones.getTranslation("lazybones", "Lazy Bones")); buttonAction.setText(LazyBones.getTranslation("lazybones", "Lazy Bones")); return new ActionMenu(buttonAction); } @Override public String getPluginCategory() { return Plugin.REMOTE_CONTROL_SOFTWARE_CATEGORY; } /** * Called by TimerSelectionDialog, if a VDR-Program has been selected * * @param selectedProgram */ public void timerSelectionCallBack(Program selectedProgram, Program originalProgram) { int buffer_before = Integer.parseInt(props.getProperty("timer.before")); int buffer_after = Integer.parseInt(props.getProperty("timer.after")); LazyBonesTimer t = ((TimerProgram) selectedProgram).getTimer(); // start the recording x min before the beggining of the program t.getStartTime().add(Calendar.MINUTE, -buffer_before); // stop the recording x min after the end of the program t.getEndTime().add(Calendar.MINUTE, buffer_after); Response response = VDRConnection.send(new NEWT(t)); if (response.getCode() == 250) { timerManager.assignProgramToTimer(selectedProgram, t); } else { logger.error(LazyBones.getTranslation("couldnt_create", "Couldn\'t create timer:") + " " + response.getMessage()); } } @Override public String getMarkIconName() { return "lazybones/vdr16.png"; } @Override public PluginInfo getInfo() { String name = LazyBones.getTranslation("lazybones", "Lazy Bones"); String description = LazyBones.getTranslation("desc", "This plugin is a remote control for a VDR (by Klaus Schmidinger)."); String author = "Henrik Niehaus, henrik.niehaus@gmx.de"; return new PluginInfo(getClass(), name, description, author, "BSD", "http://hampelratte.org/blog/?page_id=6"); } public static Version getVersion() { // return new Version(0, 0, false, "snapshot-01-05-2013"); return new Version(1, 52, 0, true); } public MainDialog getMainDialog() { if (mainDialog == null) { mainDialog = new MainDialog(getParent(), timerManager, recordingManager); } return mainDialog; } @Override public devplugin.SettingsTab getSettingsTab() { return new VDRSettingsPanel(timerManager); } @Override public void onDeactivation() { String surviveOnExit = props.getProperty("surviveOnExit"); if (Boolean.FALSE.toString().toLowerCase().equals(surviveOnExit)) { try { Player.stop(); } catch (Exception e) { } } } @Override public void loadSettings(Properties props) { LazyBones.props = props; // load data loadData(); String host = props.getProperty("host"); host = host == null ? "localhost" : host; props.setProperty("host", host); String charset = props.getProperty("charset"); charset = charset == null ? "UTF-8" : charset; props.setProperty("charset", charset); String streamurl = props.getProperty("streamurl"); streamurl = streamurl == null ? "http://<host>:3000/<streamtype>/<channel>" : streamurl; props.setProperty("streamurl", streamurl); String streamtype = props.getProperty("streamtype"); streamtype = streamtype == null ? "TS" : streamtype; props.setProperty("streamtype", streamtype); String port = props.getProperty("port"); port = port == null ? "6419" : port; props.setProperty("port", port); String timeout = props.getProperty("timeout"); timeout = timeout == null ? "500" : timeout; props.setProperty("timeout", timeout); String threshold = props.getProperty("percentageThreshold"); threshold = threshold == null ? "45" : threshold; props.setProperty("percentageThreshold", threshold); String timer_before = props.getProperty("timer.before"); timer_before = timer_before == null ? "5" : timer_before; String timer_after = props.getProperty("timer.after"); timer_after = timer_after == null ? "10" : timer_after; String timer_prio = props.getProperty("timer.prio"); timer_prio = timer_prio == null ? "50" : timer_prio; String timer_lifetime = props.getProperty("timer.lifetime"); timer_lifetime = timer_lifetime == null ? "50" : timer_lifetime; props.setProperty("timer.before", timer_before); props.setProperty("timer.after", timer_after); props.setProperty("timer.prio", timer_prio); props.setProperty("timer.lifetime", timer_lifetime); String vpsDefault = props.getProperty("vps.default"); vpsDefault = vpsDefault == null ? "false" : vpsDefault; props.setProperty("vps.default", vpsDefault); String numberOfCards = props.getProperty("numberOfCards"); numberOfCards = numberOfCards == null ? "1" : numberOfCards; props.setProperty("numberOfCards", numberOfCards); String preview_url = props.getProperty("preview.url"); preview_url = preview_url == null ? "http://localhost:8000/preview.jpg" : preview_url; String preview_path = props.getProperty("preview.path"); preview_path = preview_path == null ? "/pub/web/preview.jpg" : preview_path; String preview_method = props.getProperty("preview.method"); preview_method = preview_method == null ? "SVDRP" : preview_method; props.setProperty("preview.url", preview_url); props.setProperty("preview.path", preview_path); props.setProperty("preview.method", preview_method); String switchBefore = props.getProperty("switchBefore"); switchBefore = switchBefore == null ? "false" : switchBefore; props.setProperty("switchBefore", switchBefore); String surviveOnExit = props.getProperty("surviveOnExit"); surviveOnExit = surviveOnExit == null ? "false" : surviveOnExit; props.setProperty("surviveOnExit", surviveOnExit); String recordingURL = props.getProperty("recording.url"); recordingURL = recordingURL == null ? "http://<host>:3000/TS/<recording_number>.rec.ts" : recordingURL; props.setProperty("recording.url", recordingURL); String logConnectionErrors = props.getProperty("logConnectionErrors"); logConnectionErrors = logConnectionErrors == null ? "true" : logConnectionErrors; props.setProperty("logConnectionErrors", logConnectionErrors); String logEPGErrors = props.getProperty("logEPGErrors"); logEPGErrors = logEPGErrors == null ? "true" : logEPGErrors; props.setProperty("logEPGErrors", logEPGErrors); String showTimerOptionsDialog = props.getProperty("showTimerOptionsDialog"); showTimerOptionsDialog = showTimerOptionsDialog == null ? "true" : showTimerOptionsDialog; props.setProperty("showTimerOptionsDialog", showTimerOptionsDialog); String descSourceTvb = props.getProperty("descSourceTvb"); descSourceTvb = descSourceTvb == null ? "0" : descSourceTvb; props.setProperty("descSourceTvb", descSourceTvb); String minChannelNumber = props.getProperty("minChannelNumber"); minChannelNumber = minChannelNumber == null ? "0" : minChannelNumber; props.setProperty("minChannelNumber", minChannelNumber); String maxChannelNumber = props.getProperty("maxChannelNumber"); maxChannelNumber = maxChannelNumber == null ? "0" : maxChannelNumber; props.setProperty("maxChannelNumber", maxChannelNumber); String timelineStartHour = props.getProperty("timelineStartHour"); timelineStartHour = timelineStartHour == null ? "5" : timelineStartHour; props.setProperty("timelineStartHour", timelineStartHour); VDRConnection.host = host; VDRConnection.port = Integer.parseInt(port); VDRConnection.timeout = Integer.parseInt(timeout); VDRConnection.charset = charset; VDRConnection.persistentConnection = true; init(); } @SuppressWarnings("unchecked") private void loadData() { XStream xstream = new XStream(); // load title mapping try { Map<String, String> titleMapping = (HashMap<String, String>) xstream.fromXML(props.getProperty("titleMapping")); timerManager.getTitleMapping().setMappingFromMap(titleMapping); } catch (Exception e) { logger.warn("Couldn't load title mapping", e); } // load channel mapping try { Map<String, Channel> channelMapping = (Hashtable<String, Channel>) xstream.fromXML(props.getProperty("channelMapping")); ChannelManager.setChannelMapping(channelMapping); } catch (Exception e) { logger.warn("Couldn't load channel mapping", e); } // load timers try { List<LazyBonesTimer> timers = (ArrayList<LazyBonesTimer>) xstream.fromXML(props.getProperty("timers")); timerManager.setStoredTimers(timers); } catch (Exception e) { logger.warn("Couldn't load timers", e); } // load channel list try { List<Channel> channelList = (List<Channel>) xstream.fromXML(props.getProperty("channelList")); ChannelManager.getInstance().setChannels(channelList); } catch (Exception e) { logger.warn("Couldn't load channel list", e); } // remove outdated timers Calendar today = GregorianCalendar.getInstance(); for (Iterator<LazyBonesTimer> iter = timerManager.getStoredTimers().iterator(); iter.hasNext();) { LazyBonesTimer timer = iter.next(); if (timer.getEndTime().before(today) & !timer.isRepeating()) { iter.remove(); } } } @Override public void handleTvBrowserStartFinished() { // upload channel list from vdr logger.debug("Updating channel list"); ChannelManager.getInstance().update(); // synchronize timers and recordings synchronize(); } private void init() { instance = this; wrapper = new PluginCenterPanelWrapper() { //@formatter:off PluginCenterPanel[] panels = new PluginCenterPanel[] { new TimelineCenterPanel(timerManager), new TimersCenterPanel(timerManager, recordingManager), new RecordingsCenterPanel(recordingManager)/*, new RemoteControlCenterPanel()*/ }; //@formatter:on @Override public PluginCenterPanel[] getCenterPanels() { return panels; } }; // observe the timer list timerManager.addObserver(this); // initialize logging initLogging(); } private void initLogging() { String logDirectory = Settings.propLogdirectory.getString(); if (logDirectory != null) { if (System.getProperty("java.util.logging.config.file") == null) { // no logging config file is set, so we can adjust the logging level by ourselves java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger(""); rootLogger.setLevel(Level.FINE); logger.info("No logging configuration defined. Setting logging level to Level.FINE"); } } // create our special logging components SimpleFormatter formatter = new SimpleFormatter(); Handler eph = new PopupHandler(); Handler dch = new DebugConsoleHandler(); dch.setLevel(Level.FINEST); // add our special handlers to all lazybones.* messages LoggerFactory.getLogger("lazybones"); java.util.logging.Logger lazyLogger = java.util.logging.Logger.getLogger("lazybones"); lazyLogger.addHandler(eph); lazyLogger.addHandler(dch); eph.setFormatter(formatter); // add our special handlers to all svdrp messages LoggerFactory.getLogger("org.hampelratte.svdrp"); java.util.logging.Logger svdrpLogger = java.util.logging.Logger.getLogger("org.hampelratte.svdrp"); svdrpLogger.setLevel(Level.FINE); svdrpLogger.addHandler(dch); // add our special handlers to the popuplogger LoggerFactory.getLogger(PopupHandler.KEYWORD); java.util.logging.Logger popupLogger = java.util.logging.Logger.getLogger(PopupHandler.KEYWORD); popupLogger.addHandler(eph); popupLogger.addHandler(dch); popupLogger.setLevel(Level.INFO); eph.setLevel(Level.INFO); // create a custom file handler only for lazy bones if (logDirectory != null) { Handler fh = null; try { fh = new FileHandler(new File(logDirectory, "lazybones.log").getAbsolutePath()); fh.setFormatter(formatter); fh.setLevel(Level.FINE); } catch (Exception e) { logger.warn("Couldn't add file handler for Lazy Bones", e); } if (fh != null) { lazyLogger.addHandler(fh); svdrpLogger.addHandler(fh); popupLogger.addHandler(fh); } } } @Override public Properties storeSettings() { storeData(); return props; } private void storeData() { XStream xstream = new XStream(); props.setProperty("channelMapping", xstream.toXML(ChannelManager.getChannelMapping())); props.setProperty("timers", xstream.toXML(timerManager.getTimers())); props.setProperty("titleMapping", xstream.toXML(timerManager.getTitleMapping().getAsMap())); props.setProperty("channelList", xstream.toXML(ChannelManager.getInstance().getChannels())); } public static Properties getProperties() { return props; } public Icon getIcon(String path) { return createImageIcon(path); } @Override public void handleTvDataUpdateFinished() { ProgramManager.getInstance().markPrograms(timerManager); } public Frame getParent() { return getParentFrame(); } /** * Updates the pluginview of TV-Browser */ public void updateTree() { PluginTreeNode node = getRootNode(); node.removeAllActions(); node.removeAllChildren(); for (LazyBonesTimer timer : timerManager.getTimers()) { if (timer.isAssigned()) { for (String progID : timer.getTvBrowserProgIDs()) { Program prog = ProgramDatabase.getProgram(progID); if (prog != null) { node.addProgram(prog); } else { // can be null, if program time is near 00:00, because then // the wrong day is taken to ask tvb for the programm prog = ProgramDatabase.getProgram(progID); if (prog != null) { node.addProgram(prog); } } } } } node.update(); } @Override public boolean canUseProgramTree() { return true; } public static String getTranslation(String key, String altText) { return mLocalizer.msg(key, altText); } public static String getTranslation(String key, String altText, String arg1) { return mLocalizer.msg(key, altText, arg1); } public static String getTranslation(String key, String altText, String arg1, String arg2) { return mLocalizer.msg(key, altText, arg1, arg2); } public static String getTranslation(String key, String altText, String arg1, String arg2, String arg3) { return mLocalizer.msg(key, altText, arg1, arg2, arg3); } public JPopupMenu getSimpleContextMenu(LazyBonesTimer timer) { return cmf.createSimpleActionMenu(timer); } @Override public void update(Observable observable, Object o) { if (observable == timerManager) { // update the plugin tree updateTree(); } } public void synchronize() { timerManager.synchronize(); recordingManager.synchronize(); } private class ContextMenuFactory { public ActionMenu createChannelActionMenu(final devplugin.Channel chan) { final Channel vdrChan = ChannelManager.getChannelMapping().get(chan.getId()); if (vdrChan == null) { // selected channel is not mapped to VDR channel, so it does not make sense to show a menu at all return null; } ActionMenu[] actions = new ActionMenu[2]; AbstractAction watch = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { Player.play(vdrChan.getChannelNumber()); } }; watch.putValue(Action.NAME, LazyBones.getTranslation("watch", "Watch this channel")); watch.putValue(Action.SMALL_ICON, createImageIcon("actions", "media-playback-start", 16)); actions[0] = new ActionMenu(watch); AbstractAction switchToChan = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { // switch to channel logger.info("Switch to channel {}", vdrChan.getName()); CHAN chan = new CHAN(Integer.toString(vdrChan.getChannelNumber())); Response resp = VDRConnection.send(chan); if (resp.getCode() != 250) { logger.error(LazyBones.getTranslation("couldnt_switch", "Couldn't switch to channel", resp.getMessage())); } } }; switchToChan.putValue(Action.NAME, LazyBones.getTranslation("switch_to", "Switch to this channel")); switchToChan.putValue(Action.SMALL_ICON, createImageIcon("lazybones/remote-control.png")); actions[1] = new ActionMenu(switchToChan); String name = LazyBones.getTranslation("lazybones", "Lazy Bones"); ImageIcon icon = createImageIcon("lazybones/vdr16.png"); return new ActionMenu(name, icon, actions); } public ActionMenu createActionMenu(final Program program) { final Channel vdrChan = ChannelManager.getChannelMapping().get(program.getChannel().getId()); if (vdrChan == null) { // selected channel is not mapped to VDR channel, so it does not make sense to show a menu at all return null; } Marker[] markers = program.getMarkerArr(); boolean marked = false; for (int i = 0; i < markers.length; i++) { if (markers[i].getId().equals(getId())) { marked = true; break; } } boolean notAssignedTimersExist = timerManager.getNotAssignedTimers().size() > 0; int size = 4; if (marked || notAssignedTimersExist) { size++; } int index = 1; ActionMenu[] actions = null; if (marked) { actions = new ActionMenu[size]; AbstractAction action1 = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { timerManager.deleteTimer(program); } }; action1.putValue(Action.NAME, LazyBones.getTranslation("dont_capture", "Delete timer")); action1.putValue(Action.SMALL_ICON, createImageIcon("actions", "edit-delete", 16)); actions[index++] = new ActionMenu(action1); AbstractAction action2 = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { logger.info("Looking up timer for {}", program); LazyBonesTimer timer = timerManager.getTimer(program); logger.info("Found timer {}", timer); timerManager.editTimer(timer); } }; action2.putValue(Action.NAME, LazyBones.getTranslation("edit", "Edit Timer")); action2.putValue(Action.SMALL_ICON, createImageIcon("actions", "document-edit", 16)); actions[index++] = new ActionMenu(action2); AbstractAction action3 = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { LazyBones.getInstance().synchronize(); } }; action3.putValue(Action.NAME, LazyBones.getTranslation("resync", "Synchronize with VDR")); action3.putValue(Action.SMALL_ICON, createImageIcon("actions", "view-refresh", 16)); actions[index++] = new ActionMenu(action3); } else { actions = new ActionMenu[size]; AbstractAction action1 = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { timerManager.createTimer(program, false); } }; action1.putValue(Action.NAME, LazyBones.getTranslation("capture", "Capture with VDR")); action1.putValue(Action.SMALL_ICON, createImageIcon("lazybones/capture.png")); actions[index++] = new ActionMenu(action1); AbstractAction action2 = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { LazyBones.getInstance().synchronize(); } }; action2.putValue(Action.NAME, LazyBones.getTranslation("resync", "Synchronize with VDR")); action2.putValue(Action.SMALL_ICON, createImageIcon("actions", "view-refresh", 16)); actions[index++] = new ActionMenu(action2); if (notAssignedTimersExist) { // AbstractAction action3 = new AbstractAction() {public void actionPerformed(ActionEvent evt) {}}; // action3.putValue(Plugin.DISABLED_ON_TASK_MENU, true); Action[] timers = new Action[timerManager.getNotAssignedTimers().size()]; List<LazyBonesTimer> timerList = timerManager.getNotAssignedTimers(); for (int i = 0; i < timers.length; i++) { final LazyBonesTimer timer = timerList.get(i); timers[i] = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { ProgramManager.getInstance().assignTimerToProgram(program, timer); timerManager.assignProgramToTimer(program, timer); updateTree(); } }; timers[i].putValue(Action.NAME, timerList.get(i).getDisplayTitle()); timers[i].putValue(LazyBones.TIMER_MENU_KEY, timerList.get(i)); timers[i].putValue(Action.SMALL_ICON, createImageIcon("lazybones/appointment-new.png")); } String name = LazyBones.getTranslation("assign", "Assign"); ImageIcon icon = createImageIcon("lazybones/appointment-new.png"); actions[index++] = new ActionMenu(name, icon, timers); } } AbstractAction watch = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { Player.play(program); } }; watch.putValue(Action.NAME, LazyBones.getTranslation("watch", "Watch this channel")); watch.putValue(Action.SMALL_ICON, createImageIcon("actions", "media-playback-start", 16)); actions[0] = new ActionMenu(watch); AbstractAction switchToChan = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { // switch to channel devplugin.Channel tvbChan = program.getChannel(); Channel vdrChan = ChannelManager.getChannelMapping().get(tvbChan.getId()); if (vdrChan != null) { logger.info("Swtich to channel {} of program {}", vdrChan.getName(), program.getTitle()); CHAN chan = new CHAN(Integer.toString(vdrChan.getChannelNumber())); Response resp = VDRConnection.send(chan); if (resp.getCode() != 250) { logger.error(LazyBones.getTranslation("couldnt_switch", "Couldn't switch to channel", resp.getMessage())); } } } }; switchToChan.putValue(Action.NAME, LazyBones.getTranslation("switch_to", "Switch to this channel")); switchToChan.putValue(Action.SMALL_ICON, createImageIcon("lazybones/remote-control.png")); actions[index++] = new ActionMenu(switchToChan); String name = LazyBones.getTranslation("lazybones", "Lazy Bones"); ImageIcon icon = createImageIcon("lazybones/vdr16.png"); return new ActionMenu(name, icon, actions); } JPopupMenu simpleMenu; public JPopupMenu createSimpleActionMenu(final LazyBonesTimer timer) { if (simpleMenu == null) { simpleMenu = new JPopupMenu(); JMenuItem delItem = new JMenuItem(LazyBones.getTranslation("dont_capture", "Delete timer"), createImageIcon("actions", "edit-delete", 16)); delItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { timerManager.deleteTimer(timer); } }); JMenuItem editItem = new JMenuItem(LazyBones.getTranslation("edit", "Edit Timer"), createImageIcon("actions", "document-edit", 16)); editItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { timerManager.editTimer(timer); } }); simpleMenu.add(editItem); simpleMenu.add(delItem); } return simpleMenu; } } // ################ to receive Programs from other plugins ###################### private final String TARGET_WATCH = "watch"; private final String TARGET_CAPTURE = "capture"; @Override public boolean canReceiveProgramsWithTarget() { return true; } @Override public ProgramReceiveTarget[] getProgramReceiveTargets() { return new ProgramReceiveTarget[] { new ProgramReceiveTarget(this, LazyBones.getTranslation("capture", "Capture with VDR"), TARGET_CAPTURE), new ProgramReceiveTarget(this, LazyBones.getTranslation("watch", "Watch this channel"), TARGET_WATCH) }; } @Override public boolean receivePrograms(Program[] programArr, ProgramReceiveTarget receiveTarget) { logger.debug("Program received for target [{}]", receiveTarget.getTargetId()); if (TARGET_CAPTURE.equals(receiveTarget.getTargetId())) { // store current property values String threshold = props.getProperty("percentageThreshold"); String showTimerOptionsDialog = props.getProperty("showTimerOptionsDialog"); String logEpgErrors = props.getProperty("logEPGErrors"); String logConnectionErrors = props.getProperty("logConnectionErrors"); // set properties to "automatic mode" props.setProperty("percentageThreshold", "0"); props.setProperty("showTimerOptionsDialog", "false"); props.setProperty("logEPGErrors", "false"); props.setProperty("logConnectionErrors", "false"); // create timers for all programs for (int i = 0; i < programArr.length; i++) { Program prog = programArr[i]; timerManager.createTimer(prog, true); } // restore old properties props.setProperty("percentageThreshold", threshold); props.setProperty("showTimerOptionsDialog", showTimerOptionsDialog); props.setProperty("logEPGErrors", logEpgErrors); props.setProperty("logConnectionErrors", logConnectionErrors); } else if (TARGET_WATCH.equals(receiveTarget.getTargetId())) { Player.play(programArr[0]); } return true; } @Override public int getMarkPriorityForProgram(Program p) { LazyBonesTimer timer = timerManager.getTimer(p); if (timer != null) { if (!timer.isActive()) { return Program.LOWER_MEDIUM_MARK_PRIORITY; } } return Program.HIGHER_MEDIUM_MARK_PRIORITY; } @Override public String getProgramTableIconText() { return LazyBones.getTranslation("vps_activated", "VPS activated"); } @Override public Icon[] getProgramTableIcons(Program program) { LazyBonesTimer timer = timerManager.getTimer(program); if (timer != null) { if (timer.hasState(Timer.VPS)) { Icon[] icons = new ImageIcon[1]; icons[0] = LazyBones.getInstance().getIcon("lazybones/vps16.png"); return icons; } } return super.getProgramTableIcons(program); } }