forked from j62/ctbrec
Fix loading of EventHandlerConfiguration
This commit is contained in:
parent
33b054bc68
commit
b268549ef6
|
@ -7,6 +7,7 @@ import ctbrec.StringUtil;
|
||||||
import ctbrec.event.*;
|
import ctbrec.event.*;
|
||||||
import ctbrec.event.EventHandlerConfiguration.ActionConfiguration;
|
import ctbrec.event.EventHandlerConfiguration.ActionConfiguration;
|
||||||
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
||||||
|
import ctbrec.io.json.mapper.ModelMapper;
|
||||||
import ctbrec.recorder.Recorder;
|
import ctbrec.recorder.Recorder;
|
||||||
import ctbrec.ui.CamrecApplication;
|
import ctbrec.ui.CamrecApplication;
|
||||||
import ctbrec.ui.DesktopIntegration;
|
import ctbrec.ui.DesktopIntegration;
|
||||||
|
@ -33,6 +34,7 @@ import javafx.scene.layout.Priority;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -48,6 +50,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ActionSettingsPanel extends GridPane {
|
public class ActionSettingsPanel extends GridPane {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
|
||||||
|
@ -168,7 +171,7 @@ public class ActionSettingsPanel extends GridPane {
|
||||||
if (!modelSelectionPane.isAllSelected()) {
|
if (!modelSelectionPane.isAllSelected()) {
|
||||||
var pc = new PredicateConfiguration();
|
var pc = new PredicateConfiguration();
|
||||||
pc.setType(ModelPredicate.class.getName());
|
pc.setType(ModelPredicate.class.getName());
|
||||||
pc.setModels(modelSelectionPane.getSelectedItems());
|
pc.setModels(modelSelectionPane.getSelectedItems().stream().map(Mappers.getMapper(ModelMapper.class)::toDto).collect(Collectors.toList())); // NOSONAR
|
||||||
pc.setName("model is one of:" + modelSelectionPane.getSelectedItems());
|
pc.setName("model is one of:" + modelSelectionPane.getSelectedItems());
|
||||||
config.getPredicates().add(pc);
|
config.getPredicates().add(pc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package ctbrec.event;
|
package ctbrec.event;
|
||||||
|
|
||||||
import ctbrec.Model;
|
import ctbrec.io.json.dto.ModelDto;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(of = "id")
|
||||||
public class EventHandlerConfiguration {
|
public class EventHandlerConfiguration {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
|
@ -16,111 +20,25 @@ public class EventHandlerConfiguration {
|
||||||
id = UUID.randomUUID().toString();
|
id = UUID.randomUUID().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
@Data
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(String id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Event.Type getEvent() {
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEvent(Event.Type event) {
|
|
||||||
this.event = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PredicateConfiguration> getPredicates() {
|
|
||||||
return predicates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPredicates(List<PredicateConfiguration> predicates) {
|
|
||||||
this.predicates = predicates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<ActionConfiguration> getActions() {
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setActions(List<ActionConfiguration> actions) {
|
|
||||||
this.actions = actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PredicateConfiguration {
|
public static class PredicateConfiguration {
|
||||||
private String name;
|
private String name;
|
||||||
private String type;
|
private String type;
|
||||||
private List<Model> models;
|
private List<ModelDto> models;
|
||||||
private Map<String, Object> configuration = new HashMap<>();
|
private Map<String, Object> configuration = new HashMap<>();
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getConfiguration() {
|
|
||||||
return configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConfiguration(Map<String, Object> configuration) {
|
|
||||||
this.configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Model> getModels() {
|
|
||||||
return models;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setModels(List<Model> models) {
|
|
||||||
this.models = models;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
public static class ActionConfiguration {
|
public static class ActionConfiguration {
|
||||||
private String name;
|
private String name;
|
||||||
private String type;
|
private String type;
|
||||||
private Map<String, Object> configuration = new HashMap<>();
|
private Map<String, Object> configuration = new HashMap<>();
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(String type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Object> getConfiguration() {
|
|
||||||
return configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setConfiguration(Map<String, Object> configuration) {
|
|
||||||
this.configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name;
|
return name;
|
||||||
|
@ -131,26 +49,4 @@ public class EventHandlerConfiguration {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return name + ", when:" + predicates + " do:" + actions + "]";
|
return name + ", when:" + predicates + " do:" + actions + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ((id == null) ? 0 : id.hashCode());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj)
|
|
||||||
return true;
|
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
if (getClass() != obj.getClass())
|
|
||||||
return false;
|
|
||||||
EventHandlerConfiguration other = (EventHandlerConfiguration) obj;
|
|
||||||
if (id == null) {
|
|
||||||
return other.id == null;
|
|
||||||
} else return id.equals(other.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
package ctbrec.event;
|
package ctbrec.event;
|
||||||
|
|
||||||
|
import ctbrec.Model;
|
||||||
|
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
||||||
|
import ctbrec.io.json.mapper.ModelMapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import ctbrec.Model;
|
|
||||||
import ctbrec.event.EventHandlerConfiguration.PredicateConfiguration;
|
|
||||||
|
|
||||||
public class ModelPredicate extends EventPredicate {
|
public class ModelPredicate extends EventPredicate {
|
||||||
|
|
||||||
private Predicate<Event> internal;
|
private Predicate<Event> internal;
|
||||||
|
|
||||||
public ModelPredicate() {}
|
public ModelPredicate() {
|
||||||
|
}
|
||||||
|
|
||||||
public ModelPredicate(Model model) {
|
public ModelPredicate(Model model) {
|
||||||
internal = createFor(model);
|
internal = createFor(model);
|
||||||
|
@ -22,7 +25,7 @@ public class ModelPredicate extends EventPredicate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configure(List<Model> models) {
|
private void configure(List<Model> models) {
|
||||||
if(models.isEmpty()) {
|
if (models.isEmpty()) {
|
||||||
throw new IllegalArgumentException("List has to contain at least one model");
|
throw new IllegalArgumentException("List has to contain at least one model");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +43,7 @@ public class ModelPredicate extends EventPredicate {
|
||||||
|
|
||||||
private Predicate<Event> createFor(Model model) {
|
private Predicate<Event> createFor(Model model) {
|
||||||
return evt -> {
|
return evt -> {
|
||||||
if(evt instanceof AbstractModelEvent) {
|
if (evt instanceof AbstractModelEvent modelEvent) {
|
||||||
AbstractModelEvent modelEvent = (AbstractModelEvent) evt;
|
|
||||||
Model other = modelEvent.getModel();
|
Model other = modelEvent.getModel();
|
||||||
return Objects.equals(model, other);
|
return Objects.equals(model, other);
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,6 +54,6 @@ public class ModelPredicate extends EventPredicate {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configure(PredicateConfiguration pc) {
|
public void configure(PredicateConfiguration pc) {
|
||||||
configure(pc.getModels());
|
configure(pc.getModels().stream().map(Mappers.getMapper(ModelMapper.class)::toModel).toList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
package ctbrec.recorder.download.hls;
|
|
||||||
|
|
||||||
import com.iheartradio.m3u8.ParseException;
|
|
||||||
import com.iheartradio.m3u8.PlaylistException;
|
|
||||||
import ctbrec.Config;
|
|
||||||
import ctbrec.Model;
|
|
||||||
import ctbrec.OS;
|
|
||||||
import ctbrec.Recording;
|
|
||||||
import ctbrec.io.HttpClient;
|
|
||||||
import ctbrec.io.StreamRedirector;
|
|
||||||
import ctbrec.recorder.download.ProcessExitedUncleanException;
|
|
||||||
import ctbrec.recorder.download.hls.SegmentPlaylist.Segment;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.xml.bind.JAXBException;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the whole HLS download with FFmpeg. Not used at the moment, because FFMpeg can't
|
|
||||||
* handle the HLS encryption of Flirt4Free correctly
|
|
||||||
*/
|
|
||||||
public class FFmpegDownload extends AbstractHlsDownload {
|
|
||||||
private static final transient Logger LOG = LoggerFactory.getLogger(FFmpegDownload.class);
|
|
||||||
|
|
||||||
private transient Process ffmpeg;
|
|
||||||
private File targetFile;
|
|
||||||
|
|
||||||
public FFmpegDownload(HttpClient client) {
|
|
||||||
super(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException {
|
|
||||||
super.init(config, model, startTime, executorService);
|
|
||||||
String suffix = config.getSettings().ffmpegFileSuffix;
|
|
||||||
targetFile = config.getFileForRecording(model, suffix, startTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FFmpegDownload call() throws IOException {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(targetFile.getParentFile().toPath());
|
|
||||||
String chunkPlaylist = getSegmentPlaylistUrl(model);
|
|
||||||
|
|
||||||
String[] args = config.getSettings().ffmpegMergedDownloadArgs.split(" ");
|
|
||||||
String[] argsPlusFile = new String[args.length + 5];
|
|
||||||
int i = 0;
|
|
||||||
argsPlusFile[i++] = "-headers";
|
|
||||||
argsPlusFile[i++] = "User-Agent: " + config.getSettings().httpUserAgent;
|
|
||||||
argsPlusFile[i++] = "-i";
|
|
||||||
argsPlusFile[i++] = chunkPlaylist;
|
|
||||||
System.arraycopy(args, 0, argsPlusFile, i, args.length);
|
|
||||||
argsPlusFile[argsPlusFile.length - 1] = targetFile.getAbsolutePath();
|
|
||||||
String[] cmdline = OS.getFFmpegCommand(argsPlusFile);
|
|
||||||
LOG.debug("Command line: {}", Arrays.toString(cmdline));
|
|
||||||
ffmpeg = Runtime.getRuntime().exec(cmdline, new String[0], targetFile.getParentFile());
|
|
||||||
int exitCode = 1;
|
|
||||||
File ffmpegLog = File.createTempFile(targetFile.getName(), ".log");
|
|
||||||
ffmpegLog.deleteOnExit();
|
|
||||||
try (FileOutputStream mergeLogStream = new FileOutputStream(ffmpegLog)) {
|
|
||||||
Thread stdout = new Thread(new StreamRedirector(ffmpeg.getInputStream(), mergeLogStream));
|
|
||||||
Thread stderr = new Thread(new StreamRedirector(ffmpeg.getErrorStream(), mergeLogStream));
|
|
||||||
stdout.start();
|
|
||||||
stderr.start();
|
|
||||||
exitCode = ffmpeg.waitFor();
|
|
||||||
stdout.join();
|
|
||||||
stderr.join();
|
|
||||||
mergeLogStream.flush();
|
|
||||||
}
|
|
||||||
if (exitCode == 0) {
|
|
||||||
if (ffmpegLog.exists()) {
|
|
||||||
Files.delete(ffmpegLog.toPath());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOG.info("FFmpeg exit code was {}. Logfile: {}", exitCode, ffmpegLog.getAbsolutePath());
|
|
||||||
throw new ProcessExitedUncleanException("FFmpeg exit code was " + exitCode);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LOG.error("Thread interrupted while waiting for FFmpeg to terminate");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
} catch (ExecutionException | ParseException | PlaylistException | JAXBException e) {
|
|
||||||
LOG.error("Couldn't start FFmpeg process for stream download", e);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stop() {
|
|
||||||
if (ffmpeg != null && ffmpeg.isAlive()) {
|
|
||||||
OutputStream ffmpegStdIn = ffmpeg.getOutputStream();
|
|
||||||
try {
|
|
||||||
ffmpegStdIn.write("q".getBytes(StandardCharsets.UTF_8));
|
|
||||||
ffmpegStdIn.flush();
|
|
||||||
ffmpegStdIn.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.error("Couldn't terminate FFmpeg by sending 'q'", e);
|
|
||||||
ffmpeg.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Model getModel() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postProcess(Recording recording) {
|
|
||||||
// nothing to here for now
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public File getTarget() {
|
|
||||||
return targetFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPath(Model model) {
|
|
||||||
String absolutePath = getTarget().getAbsolutePath();
|
|
||||||
String recordingsDir = Config.getInstance().getSettings().recordingsDir;
|
|
||||||
String relativePath = absolutePath.replaceFirst(Pattern.quote(recordingsDir), "");
|
|
||||||
return relativePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void internalStop() {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSingleFile() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getSizeInByte() {
|
|
||||||
return getTarget().length();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void createTargetDirectory() throws IOException {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void execute(SegmentDownload segmentDownload) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected OutputStream getSegmentOutputStream(Segment segment) throws IOException {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finalizeDownload() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue