forked from j62/ctbrec
1
0
Fork 0

Improve support for server-side single files

This commit is contained in:
0xboobface 2020-02-22 17:40:22 +01:00
parent 208aa16c80
commit 71be9b3665
15 changed files with 65 additions and 23 deletions

View File

@ -179,6 +179,11 @@ public class JavaFxRecording extends Recording {
return delegate.getMetaDataFile(); return delegate.getMetaDataFile();
} }
@Override
public boolean isSingleFile() {
return delegate.isSingleFile();
}
public boolean valueChanged() { public boolean valueChanged() {
boolean changed = getSizeInByte() != lastValue; boolean changed = getSizeInByte() != lastValue;
lastValue = getSizeInByte(); lastValue = getSizeInByte();

View File

@ -162,7 +162,7 @@ public class Player {
private String getRemoteRecordingUrl(Recording rec, Config cfg) private String getRemoteRecordingUrl(Recording rec, Config cfg)
throws MalformedURLException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException { throws MalformedURLException, InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
String hlsBase = Config.getInstance().getServerUrl() + "/hls"; String hlsBase = Config.getInstance().getServerUrl() + "/hls";
String recUrl = hlsBase + rec.getPath() + (rec.isSegmented() ? "/playlist.m3u8" : ""); String recUrl = hlsBase + rec.getPath() + (rec.isSingleFile() ? "" : "/playlist.m3u8");
if (cfg.getSettings().requireAuthentication) { if (cfg.getSettings().requireAuthentication) {
URL u = new URL(recUrl); URL u = new URL(recUrl);
String path = u.getPath(); String path = u.getPath();

View File

@ -404,7 +404,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
MenuItem rerunPostProcessing = new MenuItem("Rerun Post-Processing"); MenuItem rerunPostProcessing = new MenuItem("Rerun Post-Processing");
rerunPostProcessing.setOnAction(e -> triggerPostProcessing(first)); rerunPostProcessing.setOnAction(e -> triggerPostProcessing(first));
if (first.getStatus() == FAILED || first.getStatus() == WAITING || first.getStatus() == FINISHED) { if (first.getStatus() == FAILED || first.getStatus() == WAITING || first.getStatus() == FINISHED) {
if (first.isSegmented() || !first.isSegmented() && first.getPath().endsWith(".part")) { if (!first.isSingleFile() || first.isSingleFile() && first.getPath().endsWith(".part")) {
contextMenu.getItems().add(rerunPostProcessing); contextMenu.getItems().add(rerunPostProcessing);
} }
} }
@ -488,12 +488,12 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
private String proposeTargetFilename(Recording recording) { private String proposeTargetFilename(Recording recording) {
String path = recording.getPath().substring(1); String path = recording.getPath().substring(1);
if(recording.isSegmented()) { if(recording.isSingleFile()) {
return new File(path).getName();
} else {
String fileSuffix = config.getSettings().ffmpegFileSuffix; String fileSuffix = config.getSettings().ffmpegFileSuffix;
String filename = path.replace("/", "-").replace(".mp4", "") + '.' + fileSuffix; String filename = path.replace("/", "-").replace(".mp4", "") + '.' + fileSuffix;
return filename; return filename;
} else {
return new File(path).getName();
} }
} }
@ -501,16 +501,16 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
try { try {
String hlsBase = config.getServerUrl() + "/hls"; String hlsBase = config.getServerUrl() + "/hls";
if (recording.isSegmented()) { if (recording.isSingleFile()) {
URL url = new URL(hlsBase + recording.getPath());
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.getPath() + "/playlist.m3u8");
MergedFfmpegHlsDownload download = new MergedFfmpegHlsDownload(CamrecApplication.httpClient); MergedFfmpegHlsDownload download = new MergedFfmpegHlsDownload(CamrecApplication.httpClient);
download.init(config, recording.getModel(), Instant.now()); download.init(config, recording.getModel(), Instant.now());
LOG.info("Downloading {}", url); LOG.info("Downloading {}", url);
download.downloadFinishedRecording(url.toString(), target, createDownloadListener(recording)); download.downloadFinishedRecording(url.toString(), target, createDownloadListener(recording));
} else {
URL url = new URL(hlsBase + recording.getPath());
FileDownload download = new FileDownload(CamrecApplication.httpClient, createDownloadListener(recording));
download.start(url, target);
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
showErrorDialog(ERROR_WHILE_DOWNLOADING_RECORDING, "The target file couldn't be created", e); showErrorDialog(ERROR_WHILE_DOWNLOADING_RECORDING, "The target file couldn't be created", e);

View File

@ -230,7 +230,7 @@ public abstract class AbstractModel implements Model {
@Override @Override
public Download createDownload() { public Download createDownload() {
if (Config.isServerMode()) { if (Config.isServerMode() && !Config.getInstance().getSettings().recordSingleFile) {
return new HlsDownload(getSite().getHttpClient()); return new HlsDownload(getSite().getHttpClient());
} else { } else {
return new MergedFfmpegHlsDownload(getSite().getHttpClient()); return new MergedFfmpegHlsDownload(getSite().getHttpClient());

View File

@ -7,7 +7,6 @@ import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Optional;
import ctbrec.event.EventBusHolder; import ctbrec.event.EventBusHolder;
import ctbrec.event.RecordingStateChangedEvent; import ctbrec.event.RecordingStateChangedEvent;
@ -22,6 +21,7 @@ public class Recording implements Serializable {
private int progress = -1; private int progress = -1;
private long sizeInByte = -1; private long sizeInByte = -1;
private String metaDataFile; private String metaDataFile;
private boolean singleFile = false;
public enum State { public enum State {
RECORDING("recording"), RECORDING("recording"),
@ -123,6 +123,14 @@ public class Recording implements Serializable {
this.download = download; this.download = download;
} }
public boolean isSingleFile() {
return singleFile;
}
public void setSingleFile(boolean singleFile) {
this.singleFile = singleFile;
}
public String getMetaDataFile() { public String getMetaDataFile() {
return metaDataFile; return metaDataFile;
} }
@ -224,8 +232,4 @@ public class Recording implements Serializable {
public void refresh() { public void refresh() {
sizeInByte = getSize(); sizeInByte = getSize();
} }
public boolean isSegmented() {
return !Optional.ofNullable(getPath()).orElse("").endsWith(".mp4");
}
} }

View File

@ -103,6 +103,7 @@ public class Settings {
public DirectoryStructure recordingsDirStructure = DirectoryStructure.FLAT; public DirectoryStructure recordingsDirStructure = DirectoryStructure.FLAT;
public String recordingsSortColumn = ""; public String recordingsSortColumn = "";
public String recordingsSortType = ""; public String recordingsSortType = "";
public boolean recordSingleFile = false;
public boolean requireAuthentication = false; public boolean requireAuthentication = false;
public String servletContext = ""; public String servletContext = "";
public boolean showPlayerStarting = false; public boolean showPlayerStarting = false;

View File

@ -268,6 +268,7 @@ public class NextGenLocalRecorder implements Recorder {
rec.setPath(download.getPath(model).replaceAll("\\\\", "/")); rec.setPath(download.getPath(model).replaceAll("\\\\", "/"));
rec.setModel(model); rec.setModel(model);
rec.setStartDate(download.getStartTime()); rec.setStartDate(download.getStartTime());
rec.setSingleFile(download.isSingleFile());
recordingProcesses.put(model, rec); recordingProcesses.put(model, rec);
recordingManager.add(rec); recordingManager.add(rec);
completionService.submit(() -> { completionService.submit(() -> {

View File

@ -34,4 +34,10 @@ public interface Download extends Serializable {
* @see #getTarget() * @see #getTarget()
*/ */
public String getPath(Model model); public String getPath(Model model);
/**
* Specifies, if the final result of this recording is a single file or consists of segments + playlist
* @return true, if the recording is only a single file
*/
public boolean isSingleFile();
} }

View File

@ -425,4 +425,9 @@ public class DashDownload extends AbstractDownload {
} }
} }
@Override
public boolean isSingleFile() {
return false;
}
} }

View File

@ -151,4 +151,9 @@ public class FFmpegDownload extends AbstractHlsDownload {
stop(); stop();
} }
@Override
public boolean isSingleFile() {
return true;
}
} }

View File

@ -366,4 +366,9 @@ public class HlsDownload extends AbstractHlsDownload {
throw new FileNotFoundException(playlist.getAbsolutePath() + " does not exist"); throw new FileNotFoundException(playlist.getAbsolutePath() + " does not exist");
} }
} }
@Override
public boolean isSingleFile() {
return false;
}
} }

View File

@ -489,4 +489,9 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
public Duration getLength() { public Duration getLength() {
return Duration.between(getStartTime(), Instant.now()); return Duration.between(getStartTime(), Instant.now());
} }
@Override
public boolean isSingleFile() {
return true;
}
} }

View File

@ -381,7 +381,7 @@ public class Fc2Model extends AbstractModel {
@Override @Override
public Download createDownload() { public Download createDownload() {
if(Config.isServerMode()) { if(Config.isServerMode() && !Config.getInstance().getSettings().recordSingleFile) {
return new Fc2HlsDownload(getSite().getHttpClient()); return new Fc2HlsDownload(getSite().getHttpClient());
} else { } else {
return new Fc2MergedHlsDownload(getSite().getHttpClient()); return new Fc2MergedHlsDownload(getSite().getHttpClient());

View File

@ -297,7 +297,7 @@ public class LiveJasminModel extends AbstractModel {
@Override @Override
public Download createDownload() { public Download createDownload() {
if(Config.isServerMode()) { if(Config.isServerMode() && !Config.getInstance().getSettings().recordSingleFile) {
return new LiveJasminHlsDownload(getSite().getHttpClient()); return new LiveJasminHlsDownload(getSite().getHttpClient());
} else { } else {
return new LiveJasminMergedHlsDownload(getSite().getHttpClient()); return new LiveJasminMergedHlsDownload(getSite().getHttpClient());

View File

@ -158,7 +158,7 @@
<button class="btn btn-secondary fa fa-play" title="Play recording" data-bind="enable: ko_status() == 'FINISHED', click: play"></button> <button class="btn btn-secondary fa fa-play" title="Play recording" data-bind="enable: ko_status() == 'FINISHED', click: play"></button>
</td> </td>
<td> <td>
<button class="btn btn-secondary fa fa-download" title="Download recording" data-bind="enable: ko_status() == 'FINISHED', click: function() { $.notify('Not implemented, yet', 'info'); }"></button> <button class="btn btn-secondary fa fa-download" title="Download recording" data-bind="enable: ko_status() == 'FINISHED' && singleFile, click: function() { $.notify('Not implemented, yet', 'info'); }"></button>
</td> </td>
<td> <td>
<button class="btn btn-secondary fa fa-trash" title="Delete recording" data-bind="enable: ko_status() == 'FINISHED', click: ctbrec.deleteRecording"></button> <button class="btn btn-secondary fa fa-trash" title="Delete recording" data-bind="enable: ko_status() == 'FINISHED', click: ctbrec.deleteRecording"></button>
@ -389,7 +389,8 @@
}; };
function play(recording) { function play(recording) {
let src = recording.playlist; let src = recording.singleFile ? '/hls' + recording.path : recording.playlist;
console.log("####################", src);
let hmacOfPath = CryptoJS.HmacSHA256(src, hmac); let hmacOfPath = CryptoJS.HmacSHA256(src, hmac);
src = '..' + src; src = '..' + src;
if(console) console.log("Path", src, "HMAC", hmacOfPath); if(console) console.log("Path", src, "HMAC", hmacOfPath);
@ -399,15 +400,19 @@
if(console) console.log('Playing video from', src); if(console) console.log('Playing video from', src);
if(src.indexOf('.mp4') > 0) { if(recording.singleFile) {
var video = document.getElementById('player'); var video = document.getElementById('player');
video.src = src; video.src = src;
video.load(); video.load();
console.log(video);
video.oncanplay = function() { video.oncanplay = function() {
video.height = window.innerHeight * 0.85; video.height = window.innerHeight * 0.85;
$('#player-window').css('display', 'block'); $('#player-window').css('display', 'block');
video.play(); video.play();
} };
video.onerror = function() {
$.notify(video.error.message, 'error');
};
} else { } else {
if (Hls.isSupported()) { if (Hls.isSupported()) {
var video = document.getElementById('player'); var video = document.getElementById('player');