forked from j62/ctbrec
1
0
Fork 0
ctbrec/common/src/main/java/ctbrec/Recording.java

346 lines
9.2 KiB
Java

package ctbrec;
import ctbrec.event.EventBusHolder;
import ctbrec.event.RecordingStateChangedEvent;
import ctbrec.io.IoUtils;
import ctbrec.recorder.download.RecordingProcess;
import ctbrec.recorder.download.StreamSource;
import ctbrec.recorder.download.VideoLengthDetector;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Future;
import static ctbrec.Recording.State.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
@Slf4j
@NoArgsConstructor
public class Recording implements Serializable {
private String id;
private Model model;
private transient RecordingProcess recordingProcess;
private transient Future<RecordingProcess> currentIteration;
private Instant startDate;
private String path;
private State status = State.UNKNOWN;
private int progress = -1;
private long sizeInByte = -1;
private String metaDataFile;
private boolean singleFile = false;
private boolean pinned = false;
private String note;
private Set<String> associatedFiles = new HashSet<>();
private File absoluteFile = null;
private File postProcessedFile = null;
private int selectedResolution = -1;
private long lastSizeUpdate = 0;
private String recordingsDir;
public Recording(String recordingsDir) {
this.recordingsDir = recordingsDir;
}
public enum State {
RECORDING("recording"),
GENERATING_PLAYLIST("generating playlist"),
POST_PROCESSING("post-processing"),
FINISHED("finished"),
DOWNLOADING("downloading"),
DELETING("deleting"),
DELETED("deleted"),
UNKNOWN("unknown"),
WAITING("waiting"),
FAILED("failed");
private final String desc;
State(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return desc;
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Instant getStartDate() {
return startDate;
}
public void setStartDate(Instant startDate) {
this.startDate = startDate;
}
public State getStatus() {
return status;
}
public void setStatus(State status) {
this.status = status;
}
public void setStatusWithEvent(State status) {
setStatus(status);
fireStatusEvent(status);
}
public int getProgress() {
return this.progress;
}
public void setProgress(int progress) {
this.progress = progress;
}
public void setPath(String path) {
this.path = path;
}
public File getAbsoluteFile() {
if (absoluteFile == null) {
File recordingsFile = new File(recordingsDir, path);
absoluteFile = recordingsFile;
}
return absoluteFile;
}
public void setAbsoluteFile(File absoluteFile) {
this.absoluteFile = absoluteFile;
}
public File getPostProcessedFile() {
if (postProcessedFile == null) {
setPostProcessedFile(getAbsoluteFile());
}
return postProcessedFile;
}
public void setPostProcessedFile(File postProcessedFile) {
this.postProcessedFile = postProcessedFile;
}
public long getSizeInByte() {
if (sizeInByte == -1) {
refresh();
}
return sizeInByte;
}
public void setSizeInByte(long sizeInByte) {
this.sizeInByte = sizeInByte;
}
public void postprocess() {
getRecordingProcess().postProcess(this);
}
private void fireStatusEvent(State status) {
RecordingStateChangedEvent evt = new RecordingStateChangedEvent(getRecordingProcess().getTarget(), status, getModel(), getStartDate());
EventBusHolder.BUS.post(evt);
}
public Model getModel() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
public RecordingProcess getRecordingProcess() {
return recordingProcess;
}
public void setRecordingProcess(RecordingProcess recordingProcess) {
this.recordingProcess = recordingProcess;
}
public boolean isSingleFile() {
return singleFile;
}
public void setSingleFile(boolean singleFile) {
this.singleFile = singleFile;
}
public String getMetaDataFile() {
return metaDataFile;
}
public void setMetaDataFile(String metaDataFile) {
this.metaDataFile = metaDataFile;
}
public boolean isPinned() {
return pinned;
}
public void setPinned(boolean pinned) {
this.pinned = pinned;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public int getSelectedResolution() {
if ((selectedResolution == -1 || selectedResolution == StreamSource.UNKNOWN) && recordingProcess != null) {
selectedResolution = recordingProcess.getSelectedResolution();
}
return selectedResolution;
}
public void setSelectedResolution(int selectedResolution) {
this.selectedResolution = selectedResolution;
}
public Duration getLength() {
File ppFile = getPostProcessedFile();
if (ppFile.isDirectory()) {
File playlist = new File(ppFile, "playlist.m3u8");
return VideoLengthDetector.getLength(playlist);
} else {
return VideoLengthDetector.getLength(ppFile);
}
}
@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 (!(obj instanceof Recording other))
return false;
if (getId() == null) {
return other.getId() == null;
} else return getId().equals(other.getId());
}
@Override
public String toString() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Config.RECORDING_DATE_FORMAT);
LocalDateTime localStartDate = LocalDateTime.ofInstant(getStartDate(), ZoneId.systemDefault());
return getModel().getSanitizedNamed() + '_' + formatter.format(localStartDate);
}
private long getSize() {
try {
Set<File> files = getAllRecordingFiles();
long sum = 0;
for (File file : files) {
if (file.isDirectory()) {
sum += IoUtils.getDirectorySize(file);
} else {
if (!file.exists()) {
if (file.getName().endsWith(".m3u8")) {
sum += IoUtils.getDirectorySize(file.getParentFile());
}
} else {
sum += file.length();
}
}
}
return sum;
} catch (IOException e) {
log.error("Couldn't determine recording size", e);
return -1;
}
}
public Set<File> getAllRecordingFiles() throws IOException {
Set<File> files = new HashSet<>();
if (absoluteFile != null) {
files.add(absoluteFile.getCanonicalFile());
}
if (postProcessedFile != null) {
files.add(postProcessedFile.getCanonicalFile());
}
for (String associatedFile : associatedFiles) {
files.add(new File(associatedFile).getCanonicalFile());
}
return files;
}
public void refresh() {
long now = System.currentTimeMillis();
if (now - lastSizeUpdate > 2500) {
sizeInByte = getSize();
lastSizeUpdate = now;
}
}
public void refresh(Path dir, WatchEvent<Path> event) throws IOException {
Path child = dir.resolve(event.context());
if (event.kind() == ENTRY_CREATE) {
sizeInByte += Files.size(child);
} else {
refresh();
}
}
public boolean canBePostProcessed() {
return getStatus() == FAILED || getStatus() == WAITING || getStatus() == FINISHED;
}
public void setAssociatedFiles(Set<String> associatedFiles) {
this.associatedFiles = associatedFiles;
}
public Set<String> getAssociatedFiles() {
return associatedFiles;
}
public Optional<File> getContactSheet() {
return getAssociatedFiles().stream()
.filter(filePath -> filePath.endsWith(".jpg"))
.findFirst()
.map(File::new);
}
public Future<RecordingProcess> getCurrentIteration() {
return currentIteration;
}
public void setCurrentIteration(Future<RecordingProcess> currentIteration) {
this.currentIteration = currentIteration;
}
}