Store absolute path in metadata file instead of path relative to rec dir
This commit is contained in:
parent
4f7d080f56
commit
4f526fd13e
|
@ -1,5 +1,6 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Instant;
|
||||
|
||||
import ctbrec.Config;
|
||||
|
@ -158,11 +159,6 @@ public class JavaFxRecording extends Recording {
|
|||
setSizeInByte(updated.getSizeInByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return delegate.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPath(String path) {
|
||||
delegate.setPath(path);
|
||||
|
@ -223,4 +219,16 @@ public class JavaFxRecording extends Recording {
|
|||
public StringProperty getNoteProperty() {
|
||||
return notesProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getAbsoluteFile() {
|
||||
return delegate.getAbsoluteFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAbsoluteFile(File absoluteFile) {
|
||||
delegate.setAbsoluteFile(absoluteFile);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -152,7 +152,7 @@ public class Player {
|
|||
Config cfg = Config.getInstance();
|
||||
try {
|
||||
if (cfg.getSettings().localRecording && rec != null) {
|
||||
File file = new File(cfg.getSettings().recordingsDir, rec.getPath());
|
||||
File file = rec.getAbsoluteFile();
|
||||
String[] cmdline = createCmdline(file.getAbsolutePath());
|
||||
playerProcess = rt.exec(cmdline, OS.getEnvironment(), file.getParentFile());
|
||||
} else {
|
||||
|
@ -206,7 +206,7 @@ public class Player {
|
|||
private String getRemoteRecordingUrl(Recording rec, Config cfg)
|
||||
throws MalformedURLException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
|
||||
String hlsBase = Config.getInstance().getServerUrl() + "/hls";
|
||||
String recUrl = hlsBase + rec.getPath() + (rec.isSingleFile() ? "" : "/playlist.m3u8");
|
||||
String recUrl = hlsBase + '/' + rec.getId() + (rec.isSingleFile() ? "" : "/playlist.m3u8");
|
||||
if (cfg.getSettings().requireAuthentication) {
|
||||
URL u = new URL(recUrl);
|
||||
String path = u.getPath();
|
||||
|
|
|
@ -560,8 +560,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
|
||||
private void onOpenDirectory(JavaFxRecording first) {
|
||||
String recordingsDir = Config.getInstance().getSettings().recordingsDir;
|
||||
String path = first.getPath();
|
||||
File tsFile = new File(recordingsDir, path);
|
||||
File tsFile = first.getAbsoluteFile();
|
||||
new Thread(() -> DesktopIntegration.open(tsFile.getParent())).start();
|
||||
}
|
||||
|
||||
|
@ -579,7 +578,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
private void download(Recording recording) {
|
||||
LOG.debug("Path {}", recording.getPath());
|
||||
LOG.debug("Path {}", recording.getAbsoluteFile());
|
||||
String filename = proposeTargetFilename(recording);
|
||||
FileChooser chooser = new FileChooser();
|
||||
chooser.setInitialFileName(filename);
|
||||
|
@ -600,7 +599,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
private String proposeTargetFilename(Recording recording) {
|
||||
String path = recording.getPath().substring(1);
|
||||
String path = recording.getAbsoluteFile().getAbsolutePath().substring(1);
|
||||
if(recording.isSingleFile()) {
|
||||
return new File(path).getName();
|
||||
} else {
|
||||
|
@ -615,11 +614,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
try {
|
||||
String hlsBase = config.getServerUrl() + "/hls";
|
||||
if (recording.isSingleFile()) {
|
||||
URL url = new URL(hlsBase + recording.getPath());
|
||||
URL url = new URL(hlsBase + '/' + recording.getId());
|
||||
FileDownload download = new FileDownload(CamrecApplication.httpClient, createDownloadListener(recording));
|
||||
download.start(url, target);
|
||||
} else {
|
||||
URL url = new URL(hlsBase + recording.getPath() + "/playlist.m3u8");
|
||||
URL url = new URL(hlsBase + '/' + recording.getId() + "/playlist.m3u8");
|
||||
MergedFfmpegHlsDownload download = new MergedFfmpegHlsDownload(CamrecApplication.httpClient);
|
||||
download.init(config, recording.getModel(), Instant.now());
|
||||
LOG.info("Downloading {}", url);
|
||||
|
@ -641,7 +640,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
});
|
||||
t.setDaemon(true);
|
||||
t.setName("Download Thread " + recording.getPath());
|
||||
t.setName("Download Thread " + recording.getAbsoluteFile().toString());
|
||||
t.start();
|
||||
}
|
||||
|
||||
|
@ -650,7 +649,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
if (progress == 100) {
|
||||
recording.setStatus(FINISHED);
|
||||
recording.setProgress(-1);
|
||||
LOG.debug("Download finished for recording {}", recording.getPath());
|
||||
LOG.debug("Download finished for recording {} - {}", recording.getId(), recording.getAbsoluteFile());
|
||||
} else {
|
||||
recording.setStatus(DOWNLOADING);
|
||||
recording.setProgress(progress);
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.time.format.DateTimeFormatter;
|
|||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -30,6 +31,7 @@ import ctbrec.recorder.download.Download;
|
|||
public class Recording implements Serializable {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Recording.class);
|
||||
|
||||
private String id;
|
||||
private Model model;
|
||||
private transient Download download;
|
||||
private Instant startDate;
|
||||
|
@ -42,6 +44,7 @@ public class Recording implements Serializable {
|
|||
private boolean pinned = false;
|
||||
private String note;
|
||||
private Set<String> associatedFiles = new HashSet<>();
|
||||
private File absoluteFile = null;
|
||||
private File postProcessedFile = null;
|
||||
|
||||
public enum State {
|
||||
|
@ -68,6 +71,17 @@ public class Recording implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
if (id == null) {
|
||||
id = UUID.randomUUID().toString();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Instant getStartDate() {
|
||||
return startDate;
|
||||
}
|
||||
|
@ -97,18 +111,27 @@ public class Recording implements Serializable {
|
|||
this.progress = progress;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
// public String getPath() {
|
||||
// return path;
|
||||
// }
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public File getAbsoluteFile() {
|
||||
String recordingsDir = Config.getInstance().getSettings().recordingsDir;
|
||||
File recordingsFile = new File(recordingsDir, getPath());
|
||||
return recordingsFile;
|
||||
if (absoluteFile == null) {
|
||||
String recordingsDir = Config.getInstance().getSettings().recordingsDir;
|
||||
File recordingsFile = new File(recordingsDir, path);
|
||||
absoluteFile = recordingsFile;
|
||||
return absoluteFile;
|
||||
} else {
|
||||
return absoluteFile;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAbsoluteFile(File absoluteFile) {
|
||||
this.absoluteFile = absoluteFile;
|
||||
}
|
||||
|
||||
public File getPostProcessedFile() {
|
||||
|
@ -222,11 +245,11 @@ public class Recording implements Serializable {
|
|||
} else if (!getModel().equals(other.getModel())) {
|
||||
return false;
|
||||
}
|
||||
if (getPath() == null) {
|
||||
if (other.getPath() != null) {
|
||||
if (path == null) {
|
||||
if (other.path != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!getPath().equals(other.getPath())) {
|
||||
} else if (!path.equals(other.path)) {
|
||||
return false;
|
||||
}
|
||||
if (getStartDate() == null) {
|
||||
|
@ -247,7 +270,7 @@ public class Recording implements Serializable {
|
|||
}
|
||||
|
||||
private long getSize() {
|
||||
File rec = new File(Config.getInstance().getSettings().recordingsDir, getPath());
|
||||
File rec = getAbsoluteFile();
|
||||
if (rec.isDirectory()) {
|
||||
return getDirectorySize(rec);
|
||||
} else {
|
||||
|
|
|
@ -282,7 +282,9 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
Model model = download.getModel();
|
||||
Recording rec = new Recording();
|
||||
rec.setDownload(download);
|
||||
rec.setPath(download.getPath(model).replaceAll("\\\\", "/"));
|
||||
String recordingFile = download.getPath(model).replaceAll("\\\\", "/");
|
||||
File absoluteFile = new File(config.getSettings().recordingsDir, recordingFile);
|
||||
rec.setAbsoluteFile(absoluteFile);
|
||||
rec.setModel(model);
|
||||
rec.setStartDate(download.getStartTime());
|
||||
rec.setSingleFile(download.isSingleFile());
|
||||
|
|
|
@ -1,236 +0,0 @@
|
|||
package ctbrec.recorder;
|
||||
|
||||
import static java.nio.file.StandardWatchEventKinds.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.ClosedWatchServiceException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.nio.file.WatchService;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Recording;
|
||||
|
||||
public class RecordingFileMonitor {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(RecordingFileMonitor.class);
|
||||
private WatchService watcher;
|
||||
private Map<WatchKey, Path> keys;
|
||||
private boolean running = true;
|
||||
private RecordingManager manager;
|
||||
|
||||
public RecordingFileMonitor(RecordingManager manager) throws IOException {
|
||||
this.manager = manager;
|
||||
this.watcher = FileSystems.getDefault().newWatchService();
|
||||
this.keys = new HashMap<>();
|
||||
registerAll(new File(Config.getInstance().getSettings().recordingsDir).toPath());
|
||||
}
|
||||
|
||||
void processEvents() {
|
||||
while (running) {
|
||||
// wait for key to be signalled
|
||||
WatchKey key;
|
||||
try {
|
||||
key = watcher.take();
|
||||
} catch (InterruptedException | ClosedWatchServiceException x) {
|
||||
return;
|
||||
}
|
||||
|
||||
Path dir = keys.get(key);
|
||||
if (dir == null) {
|
||||
LOG.error("WatchKey not recognized!!");
|
||||
continue;
|
||||
}
|
||||
|
||||
List<WatchEvent<?>> events = key.pollEvents();
|
||||
LOG.debug("Size: {}", events.size());
|
||||
if (isRenameProcess(events)) {
|
||||
handleRename(dir, events);
|
||||
} else {
|
||||
for (WatchEvent<?> event : events) {
|
||||
WatchEvent.Kind<?> kind = event.kind();
|
||||
|
||||
// TBD - provide example of how OVERFLOW event is handled
|
||||
if (kind == OVERFLOW) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Context for directory entry event is the file name of entry
|
||||
WatchEvent<Path> ev = cast(event);
|
||||
Path name = ev.context();
|
||||
Path child = dir.resolve(name);
|
||||
|
||||
if(Files.isRegularFile(child)) {
|
||||
if (kind == ENTRY_CREATE) {
|
||||
handleFileCreation(child);
|
||||
} else if (kind == ENTRY_DELETE) {
|
||||
handleFileDeletion(child);
|
||||
}
|
||||
} else {
|
||||
if (kind == ENTRY_CREATE) {
|
||||
handleDirCreation(child);
|
||||
} else if (kind == ENTRY_DELETE) {
|
||||
handleDirDeletion(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset key and remove from set if directory no longer accessible
|
||||
boolean valid = key.reset();
|
||||
if (!valid) {
|
||||
keys.remove(key);
|
||||
|
||||
// all directories are inaccessible
|
||||
if (keys.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRename(Path dir, List<WatchEvent<?>> events) {
|
||||
WatchEvent<Path> deleteEvent = cast(events.get(0));
|
||||
WatchEvent<Path> createEvent = cast(events.get(1));
|
||||
Path from = dir.resolve(deleteEvent.context());
|
||||
Path to = dir.resolve(createEvent.context());
|
||||
LOG.debug("{} -> {}", from, to);
|
||||
List<Recording> affectedRecordings = getAffectedRecordings(from);
|
||||
adjustPaths(affectedRecordings, from, to);
|
||||
if (Files.isDirectory(to, LinkOption.NOFOLLOW_LINKS)) {
|
||||
unregister(from);
|
||||
try {
|
||||
registerAll(to);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Recording> getAffectedRecordings(Path from) {
|
||||
String f = from.toAbsolutePath().toString();
|
||||
List<Recording> affected = new ArrayList<>();
|
||||
for (Recording rec : manager.getAll()) {
|
||||
String r = rec.getAbsoluteFile().getAbsolutePath();
|
||||
if (r.startsWith(f)) {
|
||||
affected.add(rec);
|
||||
}
|
||||
}
|
||||
return affected;
|
||||
}
|
||||
|
||||
private void adjustPaths(List<Recording> affectedRecordings, Path from, Path to) {
|
||||
for (Recording rec : affectedRecordings) {
|
||||
String oldPath = rec.getAbsoluteFile().getAbsolutePath();
|
||||
String newPath = oldPath.replace(from.toString(), to.toString());
|
||||
String recordingsDir = Config.getInstance().getSettings().recordingsDir;
|
||||
String relativePath = newPath.replaceFirst(Pattern.quote(recordingsDir), "");
|
||||
LOG.debug("Recording path has changed {} -> {}", rec.getPath(), relativePath);
|
||||
rec.setPath(relativePath);
|
||||
try {
|
||||
manager.saveRecording(rec);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Couldn't update recording path in meta data file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFileCreation(Path child) {
|
||||
LOG.trace("File created {}", child);
|
||||
}
|
||||
|
||||
private void handleFileDeletion(Path child) {
|
||||
LOG.trace("File deleted {}", child);
|
||||
}
|
||||
|
||||
private void handleDirCreation(Path dir) {
|
||||
try {
|
||||
registerAll(dir);
|
||||
LOG.trace("Directory added {}", dir);
|
||||
} catch (IOException x) {
|
||||
// ignore to keep sample readbale
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDirDeletion(Path dir) {
|
||||
// TODO unregister key ?!?
|
||||
|
||||
// only delete directories, which have actually been deleted
|
||||
if(Files.notExists(dir, LinkOption.NOFOLLOW_LINKS)) {
|
||||
LOG.trace("Directory Deleted {}", dir);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRenameProcess(List<WatchEvent<?>> events) {
|
||||
if(events.size() == 2) {
|
||||
boolean deleteFirst = events.get(0).kind() == ENTRY_DELETE;
|
||||
boolean createSecond = events.get(1).kind() == ENTRY_CREATE;
|
||||
return deleteFirst && createSecond;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given directory, and all its sub-directories, with the
|
||||
* WatchService.
|
||||
*/
|
||||
private void registerAll(final Path start) throws IOException {
|
||||
// register directory and sub-directories
|
||||
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
register(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given directory with the WatchService
|
||||
*/
|
||||
void register(Path dir) {
|
||||
try {
|
||||
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE);
|
||||
keys.put(key, dir);
|
||||
LOG.debug("Monitor {}", dir);
|
||||
} catch(IOException e) {
|
||||
LOG.warn("Couldn't register directory monitor for directory {}", dir, e);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(Path path) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
|
||||
return (WatchEvent<T>) event;
|
||||
}
|
||||
|
||||
public void addDirectory(Path dir) throws IOException {
|
||||
LOG.info("Adding monitor for {}", dir);
|
||||
registerAll(dir);
|
||||
}
|
||||
|
||||
public void stop() throws IOException {
|
||||
running = false;
|
||||
watcher.close();
|
||||
}
|
||||
}
|
|
@ -96,8 +96,7 @@ public class RecordingManager {
|
|||
}
|
||||
|
||||
private boolean recordingExists(Recording recording) {
|
||||
File rec = new File(config.getSettings().recordingsDir, recording.getPath());
|
||||
return rec.exists();
|
||||
return recording.getAbsoluteFile().exists();
|
||||
}
|
||||
|
||||
private File getDir() {
|
||||
|
@ -122,8 +121,7 @@ public class RecordingManager {
|
|||
recording = recordings.get(idx);
|
||||
|
||||
recording.setStatus(State.DELETING);
|
||||
File recordingsDir = new File(config.getSettings().recordingsDir);
|
||||
File path = new File(recordingsDir, recording.getPath());
|
||||
File path = recording.getAbsoluteFile();
|
||||
boolean isFile = path.isFile();
|
||||
LOG.debug("Deleting {}", path);
|
||||
|
||||
|
@ -172,8 +170,7 @@ public class RecordingManager {
|
|||
try {
|
||||
int idx = recordings.indexOf(recording);
|
||||
recording = recordings.get(idx);
|
||||
File recordingsDir = new File(config.getSettings().recordingsDir);
|
||||
File path = new File(recordingsDir, recording.getPath());
|
||||
File path = recording.getAbsoluteFile();
|
||||
deleteEmptyParents(path.getParentFile());
|
||||
// delete the meta data
|
||||
Files.deleteIfExists(new File(recording.getMetaDataFile()).toPath());
|
||||
|
|
|
@ -330,7 +330,7 @@ public class RemoteRecorder implements Recorder {
|
|||
int idx = newRecordings.indexOf(recording);
|
||||
Recording newRecording = newRecordings.get(idx);
|
||||
if (newRecording.getStatus() != recording.getStatus()) {
|
||||
File file = new File(recording.getPath());
|
||||
File file = recording.getAbsoluteFile();
|
||||
RecordingStateChangedEvent evt = new RecordingStateChangedEvent(file, newRecording.getStatus(), recording.getModel(),
|
||||
recording.getStartDate());
|
||||
EventBusHolder.BUS.post(evt);
|
||||
|
@ -342,7 +342,7 @@ public class RemoteRecorder implements Recorder {
|
|||
justStarted.removeAll(recordings);
|
||||
for (Recording recording : justStarted) {
|
||||
if (recording.getStatus() == Recording.State.RECORDING) {
|
||||
File file = new File(recording.getPath());
|
||||
File file = recording.getAbsoluteFile();
|
||||
RecordingStateChangedEvent evt = new RecordingStateChangedEvent(file, recording.getStatus(), recording.getModel(),
|
||||
recording.getStartDate());
|
||||
EventBusHolder.BUS.post(evt);
|
||||
|
|
|
@ -391,7 +391,8 @@ public class DashDownload extends AbstractDownload {
|
|||
try {
|
||||
Thread.currentThread().setName("PP " + model.getName());
|
||||
recording.setStatus(POST_PROCESSING);
|
||||
String path = recording.getPath();
|
||||
// FIXME this was recording.getPath() before and is currently not working. This has to be fixed once DASH is used for a download again
|
||||
String path = recording.getAbsoluteFile().getAbsolutePath();
|
||||
File dir = new File(Config.getInstance().getSettings().recordingsDir, path);
|
||||
File file = new File(dir.getParentFile(), dir.getName().substring(0, dir.getName().length() - 5));
|
||||
new FfmpegMuxer(dir, file);
|
||||
|
|
Loading…
Reference in New Issue