forked from j62/ctbrec
Add columns "last recorded" and "last seen" to models table
This commit is contained in:
parent
60826323f3
commit
3bb161c055
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue