forked from j62/ctbrec
1
0
Fork 0

Store absolute path in metadata file instead of path relative to rec dir

This commit is contained in:
0xb00bface 2020-09-13 17:34:58 +02:00
parent 4f7d080f56
commit 4f526fd13e
9 changed files with 65 additions and 271 deletions

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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 {

View File

@ -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());

View File

@ -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();
}
}

View File

@ -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());

View File

@ -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);

View File

@ -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);