Fix server stuff for new recording path handling
This commit is contained in:
parent
4f526fd13e
commit
90192d9b8f
|
@ -230,5 +230,15 @@ public class JavaFxRecording extends Recording {
|
||||||
delegate.setAbsoluteFile(absoluteFile);
|
delegate.setAbsoluteFile(absoluteFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return delegate.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setId(String id) {
|
||||||
|
delegate.setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.time.format.DateTimeFormatter;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -72,9 +71,6 @@ public class Recording implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
if (id == null) {
|
|
||||||
id = UUID.randomUUID().toString();
|
|
||||||
}
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,6 +281,7 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
private Recording createRecording(Download download) throws IOException {
|
private Recording createRecording(Download download) throws IOException {
|
||||||
Model model = download.getModel();
|
Model model = download.getModel();
|
||||||
Recording rec = new Recording();
|
Recording rec = new Recording();
|
||||||
|
rec.setId(UUID.randomUUID().toString());
|
||||||
rec.setDownload(download);
|
rec.setDownload(download);
|
||||||
String recordingFile = download.getPath(model).replaceAll("\\\\", "/");
|
String recordingFile = download.getPath(model).replaceAll("\\\\", "/");
|
||||||
File absoluteFile = new File(config.getSettings().recordingsDir, recordingFile);
|
File absoluteFile = new File(config.getSettings().recordingsDir, recordingFile);
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -82,6 +83,10 @@ public class RecordingManager {
|
||||||
if (recording.getStatus() == RECORDING || recording.getStatus() == GENERATING_PLAYLIST || recording.getStatus() == POST_PROCESSING) {
|
if (recording.getStatus() == RECORDING || recording.getStatus() == GENERATING_PLAYLIST || recording.getStatus() == POST_PROCESSING) {
|
||||||
recording.setStatus(WAITING);
|
recording.setStatus(WAITING);
|
||||||
}
|
}
|
||||||
|
if (recording.getId() == null) {
|
||||||
|
recording.setId(UUID.randomUUID().toString());
|
||||||
|
saveRecording(recording);
|
||||||
|
}
|
||||||
if (recordingExists(recording)) {
|
if (recordingExists(recording)) {
|
||||||
recordings.add(recording);
|
recordings.add(recording);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,6 +9,8 @@ import java.nio.file.Paths;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -20,6 +22,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
|
import ctbrec.Recording;
|
||||||
|
import ctbrec.recorder.Recorder;
|
||||||
|
|
||||||
public class HlsServlet extends AbstractCtbrecServlet {
|
public class HlsServlet extends AbstractCtbrecServlet {
|
||||||
|
|
||||||
|
@ -27,50 +31,69 @@ public class HlsServlet extends AbstractCtbrecServlet {
|
||||||
|
|
||||||
private final Config config;
|
private final Config config;
|
||||||
|
|
||||||
public HlsServlet(Config config) {
|
private Recorder recorder;
|
||||||
|
|
||||||
|
public HlsServlet(Config config, Recorder recorder) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
this.recorder = recorder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||||
|
|
||||||
|
|
||||||
String contextPath = getServletContext().getContextPath();
|
String contextPath = getServletContext().getContextPath();
|
||||||
String request = req.getRequestURI().substring(contextPath.length() + 5);
|
String request = req.getRequestURI().substring(contextPath.length() + 5);
|
||||||
Path recordingsDirPath = Paths.get(config.getSettings().recordingsDir).toAbsolutePath().normalize();
|
Path recordingsDirPath = Paths.get(config.getSettings().recordingsDir).toAbsolutePath().normalize();
|
||||||
Path requestedFilePath = recordingsDirPath.resolve(request).toAbsolutePath().normalize();
|
Path requestedFilePath = recordingsDirPath.resolve(request).toAbsolutePath().normalize();
|
||||||
|
|
||||||
boolean isValidRequestedPath = requestedFilePath.startsWith(recordingsDirPath);
|
|
||||||
if (isValidRequestedPath) {
|
|
||||||
File requestedFile = requestedFilePath.toFile();
|
File requestedFile = requestedFilePath.toFile();
|
||||||
if (requestedFile.getName().equals("playlist.m3u8")) {
|
|
||||||
try {
|
try {
|
||||||
|
if (requestedFile.getName().equals("playlist.m3u8")) {
|
||||||
boolean isRequestAuthenticated = checkAuthentication(req, req.getRequestURI());
|
boolean isRequestAuthenticated = checkAuthentication(req, req.getRequestURI());
|
||||||
if (!isRequestAuthenticated) {
|
if (!isRequestAuthenticated) {
|
||||||
writeResponse(resp, SC_UNAUTHORIZED, "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}");
|
writeResponse(resp, SC_UNAUTHORIZED, "{\"status\": \"error\", \"msg\": \"HMAC does not match\"}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) {
|
|
||||||
writeResponse(resp, SC_UNAUTHORIZED, "{\"status\": \"error\", \"msg\": \"Authentication failed\"}");
|
String id = request.substring(0, request.indexOf('/'));
|
||||||
|
Optional<Recording> rec = getRecordingById(id);
|
||||||
|
if (rec.isPresent()) {
|
||||||
|
servePlaylist(req, resp, rec.get().getAbsoluteFile());
|
||||||
|
} else {
|
||||||
|
error404(req, resp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
servePlaylist(req, resp, requestedFile);
|
|
||||||
} else {
|
} else {
|
||||||
if (requestedFile.exists()) {
|
String id = request.split("/")[0];
|
||||||
|
Optional<Recording> rec = getRecordingById(id);
|
||||||
|
if (rec.isPresent()) {
|
||||||
|
File file = rec.get().getAbsoluteFile();
|
||||||
|
if (LOG.isTraceEnabled()) {
|
||||||
Enumeration<String> headerNames = req.getHeaderNames();
|
Enumeration<String> headerNames = req.getHeaderNames();
|
||||||
while (headerNames.hasMoreElements()) {
|
while (headerNames.hasMoreElements()) {
|
||||||
String header = headerNames.nextElement();
|
String header = headerNames.nextElement();
|
||||||
LOG.trace("{}: {}", header, req.getHeader(header));
|
LOG.trace("{}: {}", header, req.getHeader(header));
|
||||||
}
|
}
|
||||||
serveSegment(req, resp, requestedFile);
|
}
|
||||||
|
serveSegment(req, resp, file);
|
||||||
} else {
|
} else {
|
||||||
error404(req, resp);
|
error404(req, resp);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) {
|
||||||
writeResponse(resp, SC_FORBIDDEN, "Stop it!");
|
writeResponse(resp, SC_UNAUTHORIZED, "{\"status\": \"error\", \"msg\": \"Authentication failed\"}");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<Recording> getRecordingById(String id) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
|
||||||
|
return recorder.getRecordings().stream()
|
||||||
|
.filter(r -> Objects.equals(id, r.getId()))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
private void writeResponse(HttpServletResponse resp, int code, String body) {
|
private void writeResponse(HttpServletResponse resp, int code, String body) {
|
||||||
try {
|
try {
|
||||||
resp.setStatus(code);
|
resp.setStatus(code);
|
||||||
|
@ -81,6 +104,7 @@ public class HlsServlet extends AbstractCtbrecServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void error404(HttpServletRequest req, HttpServletResponse resp) {
|
private void error404(HttpServletRequest req, HttpServletResponse resp) {
|
||||||
|
writeResponse(resp, SC_NOT_FOUND, "{\"status\": \"error\", \"msg\": \"Recording not found\"}");
|
||||||
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +114,9 @@ public class HlsServlet extends AbstractCtbrecServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void servePlaylist(HttpServletRequest req, HttpServletResponse resp, File requestedFile) throws IOException {
|
private void servePlaylist(HttpServletRequest req, HttpServletResponse resp, File requestedFile) throws IOException {
|
||||||
serveFile(req, resp, requestedFile, "application/x-mpegURL");
|
LOG.debug("Serving playlist {}", requestedFile);
|
||||||
|
File playlist = new File(requestedFile, "playlist.m3u8");
|
||||||
|
serveFile(req, resp, playlist, "application/x-mpegURL");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void serveFile(HttpServletRequest req, HttpServletResponse resp, File file, String contentType) throws IOException {
|
private void serveFile(HttpServletRequest req, HttpServletResponse resp, File file, String contentType) throws IOException {
|
||||||
|
@ -103,6 +129,7 @@ public class HlsServlet extends AbstractCtbrecServlet {
|
||||||
byte[] buffer = new byte[1024 * 100];
|
byte[] buffer = new byte[1024 * 100];
|
||||||
long bytesLeft = range.to - range.from;
|
long bytesLeft = range.to - range.from;
|
||||||
resp.setContentLengthLong(bytesLeft);
|
resp.setContentLengthLong(bytesLeft);
|
||||||
|
resp.addHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
|
||||||
if (range.set) {
|
if (range.set) {
|
||||||
resp.setHeader("Content-Range", "bytes " + range.from + '-' + range.to + '/' + file.length());
|
resp.setHeader("Content-Range", "bytes " + range.from + '-' + range.to + '/' + file.length());
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,7 +213,7 @@ public class HttpServer {
|
||||||
holder = new ServletHolder(configServlet);
|
holder = new ServletHolder(configServlet);
|
||||||
defaultContext.addServlet(holder, "/config");
|
defaultContext.addServlet(holder, "/config");
|
||||||
|
|
||||||
HlsServlet hlsServlet = new HlsServlet(this.config);
|
HlsServlet hlsServlet = new HlsServlet(this.config, recorder);
|
||||||
holder = new ServletHolder(hlsServlet);
|
holder = new ServletHolder(hlsServlet);
|
||||||
defaultContext.addServlet(holder, "/hls/*");
|
defaultContext.addServlet(holder, "/hls/*");
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
function play(recording) {
|
function play(recording) {
|
||||||
let src = recording.singleFile ? '/hls' + recording.path : recording.playlist;
|
let src = recording.singleFile ? '/hls/' + recording.id : recording.playlist;
|
||||||
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);
|
||||||
|
@ -48,7 +48,7 @@ function play(recording) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function download(recording) {
|
function download(recording) {
|
||||||
let src = recording.singleFile ? '/hls' + recording.path : recording.playlist;
|
let src = recording.singleFile ? '/hls/' + recording.id : recording.playlist;
|
||||||
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);
|
||||||
|
@ -77,7 +77,7 @@ function calculateSize(sizeInByte) {
|
||||||
function isRecordingInArray(array, recording) {
|
function isRecordingInArray(array, recording) {
|
||||||
for ( let idx in array) {
|
for ( let idx in array) {
|
||||||
let r = array[idx];
|
let r = array[idx];
|
||||||
if (r.path === recording.path) {
|
if (r.id === recording.id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,10 +115,10 @@ function syncRecordings(recordings) {
|
||||||
recording.ko_progressString = ko.observable(recording.progress === -1 ? '' : recording.progress);
|
recording.ko_progressString = ko.observable(recording.progress === -1 ? '' : recording.progress);
|
||||||
recording.ko_size = ko.observable(calculateSize(recording.sizeInByte));
|
recording.ko_size = ko.observable(calculateSize(recording.sizeInByte));
|
||||||
recording.ko_status = ko.observable(recording.status);
|
recording.ko_status = ko.observable(recording.status);
|
||||||
if (recording.path.endsWith('.mp4')) {
|
if (recording.singleFile) {
|
||||||
recording.playlist = '/hls' + recording.path;
|
recording.playlist = '/hls/' + recording.id;
|
||||||
} else {
|
} else {
|
||||||
recording.playlist = '/hls' + recording.path + '/playlist.m3u8';
|
recording.playlist = '/hls/' + recording.id + '/playlist.m3u8';
|
||||||
}
|
}
|
||||||
observableRecordingsArray.push(recording);
|
observableRecordingsArray.push(recording);
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ function syncRecordings(recordings) {
|
||||||
let recording = recordings[i];
|
let recording = recordings[i];
|
||||||
for ( let j in observableRecordingsArray()) {
|
for ( let j in observableRecordingsArray()) {
|
||||||
let r = observableRecordingsArray()[j];
|
let r = observableRecordingsArray()[j];
|
||||||
if (recording.path === r.path) {
|
if (recording.id === r.id) {
|
||||||
r.progress = recording.progress;
|
r.progress = recording.progress;
|
||||||
r.sizeInByte = recording.sizeInByte;
|
r.sizeInByte = recording.sizeInByte;
|
||||||
r.status = recording.status;
|
r.status = recording.status;
|
||||||
|
|
Loading…
Reference in New Issue