forked from j62/ctbrec
1
0
Fork 0

Add columns "last recorded" and "last seen" to models table

This commit is contained in:
0xboobface 2020-01-05 16:02:22 +01:00
parent 60826323f3
commit 3bb161c055
11 changed files with 149 additions and 23 deletions

View File

@ -0,0 +1,18 @@
package ctbrec.ui;
import java.time.Instant;
import javafx.beans.property.ObjectPropertyBase;
public class InstantProperty extends ObjectPropertyBase<Instant> {
@Override
public Object getBean() {
return get();
}
@Override
public String getName() {
return "Instant";
}
}

View File

@ -1,6 +1,7 @@
package ctbrec.ui;
import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -18,6 +19,7 @@ import ctbrec.sites.Site;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
/**
* Just a wrapper for Model, which augments it with JavaFX value binding properties, so that UI widgets get updated proeprly
@ -27,11 +29,16 @@ public class JavaFxModel implements Model {
private transient BooleanProperty recordingProperty = new SimpleBooleanProperty();
private transient BooleanProperty pausedProperty = new SimpleBooleanProperty();
private transient SimpleIntegerProperty priorityProperty = new SimpleIntegerProperty();
private transient SimpleObjectProperty<Instant> lastSeenProperty = new SimpleObjectProperty<>();
private transient SimpleObjectProperty<Instant> lastRecordedProperty = new SimpleObjectProperty<>();
private Model delegate;
public JavaFxModel(Model delegate) {
this.delegate = delegate;
setPriority(delegate.getPriority());
setLastSeen(delegate.getLastSeen());
setLastRecorded(delegate.getLastRecorded());
}
@Override
@ -234,6 +241,36 @@ public class JavaFxModel implements Model {
return delegate.getPriority();
}
public SimpleObjectProperty<Instant> lastSeenProperty() {
return lastSeenProperty;
}
@Override
public void setLastSeen(Instant timestamp) {
delegate.setLastSeen(timestamp);
lastSeenProperty.set(timestamp);
}
@Override
public Instant getLastSeen() {
return delegate.getLastSeen();
}
public SimpleObjectProperty<Instant> lastRecordedProperty() {
return lastRecordedProperty;
}
@Override
public void setLastRecorded(Instant timestamp) {
delegate.setLastRecorded(timestamp);
lastRecordedProperty.set(timestamp);
}
@Override
public Instant getLastRecorded() {
return delegate.getLastRecorded();
}
@Override
public int compareTo(Model o) {
return delegate.compareTo(o);

View File

@ -3,6 +3,7 @@ package ctbrec.ui;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@ -34,6 +35,7 @@ import ctbrec.ui.action.PlayAction;
import ctbrec.ui.action.ResumeAction;
import ctbrec.ui.action.StopRecordingAction;
import ctbrec.ui.controls.AutoFillTextField;
import ctbrec.ui.controls.DateTimeCellFactory;
import ctbrec.ui.controls.Dialogs;
import ctbrec.ui.controls.SearchBox;
import javafx.application.Platform;
@ -176,6 +178,16 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
priority.setOnEditStart(e -> cellEditing = true);
priority.setOnEditCommit(this::onUpdatePriority);
priority.setOnEditCancel(e -> cellEditing = false);
TableColumn<JavaFxModel, Instant> lastSeen = new TableColumn<>("last seen");
lastSeen.setCellValueFactory(cdf -> cdf.getValue().lastSeenProperty());
lastSeen.setCellFactory(new DateTimeCellFactory<JavaFxModel>());
lastSeen.setPrefWidth(150);
lastSeen.setEditable(false);
TableColumn<JavaFxModel, Instant> lastRecorded = new TableColumn<>("last recorded");
lastRecorded.setCellValueFactory(cdf -> cdf.getValue().lastRecordedProperty());
lastRecorded.setCellFactory(new DateTimeCellFactory<JavaFxModel>());
lastRecorded.setPrefWidth(150);
lastRecorded.setEditable(false);
TableColumn<JavaFxModel, String> notes = new TableColumn<>("Notes");
notes.setCellValueFactory(cdf -> {
JavaFxModel m = cdf.getValue();
@ -199,7 +211,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
});
notes.setPrefWidth(400);
notes.setEditable(false);
table.getColumns().addAll(preview, name, url, online, recording, paused, priority, notes);
table.getColumns().addAll(preview, name, url, online, recording, paused, priority, lastSeen, lastRecorded, notes);
table.setItems(observableModels);
table.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> {
popup = createContextMenu();
@ -417,6 +429,8 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
oldModel.setSuspended(updatedModel.isSuspended());
oldModel.getOnlineProperty().set(updatedModel.getOnlineProperty().get());
oldModel.getRecordingProperty().set(updatedModel.getRecordingProperty().get());
oldModel.lastRecordedProperty().set(updatedModel.lastRecordedProperty().get());
oldModel.lastSeenProperty().set(updatedModel.lastSeenProperty().get());
}
}
}

View File

@ -12,10 +12,6 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@ -37,6 +33,7 @@ import ctbrec.recorder.ProgressListener;
import ctbrec.recorder.Recorder;
import ctbrec.recorder.download.hls.MergedHlsDownload;
import ctbrec.sites.Site;
import ctbrec.ui.controls.DateTimeCellFactory;
import ctbrec.ui.controls.Toast;
import javafx.application.Platform;
import javafx.beans.property.SimpleObjectProperty;
@ -127,7 +124,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
Instant instant = cdf.getValue().getStartDate();
return new SimpleObjectProperty<Instant>(instant);
});
date.setCellFactory(param -> createDateCell());
date.setCellFactory(new DateTimeCellFactory<JavaFxRecording>());
date.setPrefWidth(200);
TableColumn<JavaFxRecording, String> status = new TableColumn<>("Status");
status.setCellValueFactory(cdf -> cdf.getValue().getStatusProperty());
@ -193,23 +190,6 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
return cell;
}
private TableCell<JavaFxRecording, Instant> createDateCell() {
TableCell<JavaFxRecording, Instant> cell = new TableCell<JavaFxRecording, Instant>() {
@Override
protected void updateItem(Instant instant, boolean empty) {
if(empty || instant == null) {
setText(null);
} else {
ZonedDateTime time = instant.atZone(ZoneId.systemDefault());
DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM);
setText(dtf.format(time));
}
}
};
return cell;
}
private void onContextMenuRequested(ContextMenuEvent event) {
List<JavaFxRecording> recordings = table.getSelectionModel().getSelectedItems();
if (recordings != null && !recordings.isEmpty()) {

View File

@ -0,0 +1,30 @@
package ctbrec.ui.controls;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
public class DateTimeCellFactory<T> implements Callback<TableColumn<T, Instant>, TableCell<T, Instant>> {
@Override
public TableCell<T, Instant> call(TableColumn<T, Instant> param) {
return new TableCell<T, Instant>() {
@Override
protected void updateItem(Instant item, boolean empty) {
if (empty || item == null) {
setText("");
} else {
LocalDateTime dateTime = LocalDateTime.ofInstant(item, ZoneId.systemDefault());
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
String formattedDateTime = formatter.format(dateTime);
setText(item.equals(Instant.EPOCH) ? "" : formattedDateTime);
}
}
};
}
}

View File

@ -1,6 +1,7 @@
package ctbrec;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -27,6 +28,8 @@ public abstract class AbstractModel implements Model {
private boolean suspended = false;
protected transient Site site;
protected State onlineState = State.UNKNOWN;
private Instant lastSeen;
private Instant lastRecorded;
@Override
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
@ -205,6 +208,26 @@ public abstract class AbstractModel implements Model {
this.priority = priority;
}
@Override
public Instant getLastSeen() {
return Optional.ofNullable(lastSeen).orElse(Instant.EPOCH);
}
@Override
public void setLastSeen(Instant lastSeen) {
this.lastSeen = lastSeen;
}
@Override
public Instant getLastRecorded() {
return Optional.ofNullable(lastRecorded).orElse(Instant.EPOCH);
}
@Override
public void setLastRecorded(Instant lastRecorded) {
this.lastRecorded = lastRecorded;
}
@Override
public Download createDownload() {
if(Config.isServerMode()) {

View File

@ -2,6 +2,7 @@ package ctbrec;
import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.ExecutionException;
@ -82,6 +83,14 @@ public interface Model extends Comparable<Model>, Serializable {
public void receiveTip(Double tokens) throws IOException;
public void setLastSeen(Instant timestamp);
public Instant getLastSeen();
public void setLastRecorded(Instant timestamp);
public Instant getLastRecorded();
/**
* Determines the stream resolution for this model
*

View File

@ -2,6 +2,7 @@ package ctbrec.io;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@ -69,6 +70,10 @@ public class ModelJsonAdapter extends JsonAdapter<Model> {
} else if(key.equals("suspended")) {
suspended = reader.nextBoolean();
model.setSuspended(suspended);
} else if(key.equals("lastSeen")) {
model.setLastSeen(Instant.ofEpochMilli(reader.nextLong()));
} else if(key.equals("lastRecorded")) {
model.setLastRecorded(Instant.ofEpochMilli(reader.nextLong()));
} else if(key.equals("siteSpecific")) {
reader.beginObject();
try {
@ -108,6 +113,8 @@ public class ModelJsonAdapter extends JsonAdapter<Model> {
writer.name("priority").value(model.getPriority());
writer.name("streamUrlIndex").value(model.getStreamUrlIndex());
writer.name("suspended").value(model.isSuspended());
writer.name("lastSeen").value(model.getLastSeen().toEpochMilli());
writer.name("lastRecorded").value(model.getLastRecorded().toEpochMilli());
writer.name("siteSpecific");
writer.beginObject();
model.writeSiteSpecificData(writer);

View File

@ -273,6 +273,7 @@ public class NextGenLocalRecorder implements Recorder {
completionService.submit(() -> {
try {
setRecordingStatus(rec, State.RECORDING);
model.setLastRecorded(rec.getStartDate());
recordingManager.saveRecording(rec);
download.start();
} catch (IOException e) {

View File

@ -108,6 +108,7 @@ public class OnlineMonitor extends Thread {
try {
if (model.isOnline(IGNORE_CACHE)) {
EventBusHolder.BUS.post(new ModelIsOnlineEvent(model));
model.setLastSeen(Instant.now());
}
Model.State state = model.getOnlineState(false);
LOG.trace("Model online state: {} {}", model.getName(), state);

View File

@ -177,6 +177,12 @@ public class HlsDownload extends AbstractHlsDownload {
} catch (Exception e) {
throw new IOException("Couldn't download segment", e);
} finally {
try {
Thread.sleep(10_000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
downloadThreadPool.shutdown();
try {
LOG.debug("Waiting for last segments for {}", model);