forked from j62/ctbrec
Improve support for server-side single files
This commit is contained in:
parent
208aa16c80
commit
71be9b3665
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(() -> {
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -425,4 +425,9 @@ public class DashDownload extends AbstractDownload {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSingleFile() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,4 +151,9 @@ public class FFmpegDownload extends AbstractHlsDownload {
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSingleFile() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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,9 +389,10 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
if (hmac.length > 0) {
|
if (hmac.length > 0) {
|
||||||
src += "?hmac=" + hmacOfPath;
|
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');
|
||||||
|
|
Loading…
Reference in New Issue