diff --git a/client/src/main/java/ctbrec/ui/JavaFxRecording.java b/client/src/main/java/ctbrec/ui/JavaFxRecording.java index 0009d9d2..5ba78e6b 100644 --- a/client/src/main/java/ctbrec/ui/JavaFxRecording.java +++ b/client/src/main/java/ctbrec/ui/JavaFxRecording.java @@ -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); + } + + } diff --git a/client/src/main/java/ctbrec/ui/Player.java b/client/src/main/java/ctbrec/ui/Player.java index 96df79dd..af88b945 100644 --- a/client/src/main/java/ctbrec/ui/Player.java +++ b/client/src/main/java/ctbrec/ui/Player.java @@ -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(); diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java index 5fff704d..c48b41ad 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java @@ -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); diff --git a/common/src/main/java/ctbrec/Recording.java b/common/src/main/java/ctbrec/Recording.java index 508aee27..5ab665cd 100644 --- a/common/src/main/java/ctbrec/Recording.java +++ b/common/src/main/java/ctbrec/Recording.java @@ -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 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 { diff --git a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java index bffec23e..7825781d 100644 --- a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java @@ -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()); diff --git a/common/src/main/java/ctbrec/recorder/RecordingFileMonitor.java b/common/src/main/java/ctbrec/recorder/RecordingFileMonitor.java deleted file mode 100644 index 0a5fe253..00000000 --- a/common/src/main/java/ctbrec/recorder/RecordingFileMonitor.java +++ /dev/null @@ -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 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> 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 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> events) { - WatchEvent deleteEvent = cast(events.get(0)); - WatchEvent createEvent = cast(events.get(1)); - Path from = dir.resolve(deleteEvent.context()); - Path to = dir.resolve(createEvent.context()); - LOG.debug("{} -> {}", from, to); - List 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 getAffectedRecordings(Path from) { - String f = from.toAbsolutePath().toString(); - List 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 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> 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() { - @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 WatchEvent cast(WatchEvent event) { - return (WatchEvent) 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(); - } -} diff --git a/common/src/main/java/ctbrec/recorder/RecordingManager.java b/common/src/main/java/ctbrec/recorder/RecordingManager.java index e746b011..4678bb2d 100644 --- a/common/src/main/java/ctbrec/recorder/RecordingManager.java +++ b/common/src/main/java/ctbrec/recorder/RecordingManager.java @@ -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()); diff --git a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java index 37dcb6ec..321bf46c 100644 --- a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -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); diff --git a/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java b/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java index a5c21743..d6203b2a 100644 --- a/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/dash/DashDownload.java @@ -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);