Move Help servlets to common module and embed it into the webinterface
This commit is contained in:
parent
fe7b263d2e
commit
35592e2f48
|
@ -80,11 +80,6 @@
|
|||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
<artifactId>flexmark</artifactId>
|
||||
<version>0.40.34</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
package ctbrec.docs;
|
||||
|
||||
import java.net.BindException;
|
||||
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import ctbrec.servlet.AbstractDocServlet;
|
||||
import ctbrec.servlet.MarkdownServlet;
|
||||
import ctbrec.servlet.SearchServlet;
|
||||
import ctbrec.servlet.StaticFileServlet;
|
||||
import org.eclipse.jetty.server.*;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.servlet.StaticFileServlet;
|
||||
import java.net.BindException;
|
||||
|
||||
public class DocServer {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DocServer.class);
|
||||
|
||||
private static volatile boolean started = false;
|
||||
|
||||
private DocServer() {}
|
||||
private DocServer() {
|
||||
}
|
||||
|
||||
public static synchronized void start() throws Exception {
|
||||
if(started) {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -39,7 +38,7 @@ public class DocServer {
|
|||
var handler = new ServletHandler();
|
||||
server.setHandler(handler);
|
||||
var handlers = new HandlerList();
|
||||
handlers.setHandlers(new Handler[] { handler });
|
||||
handlers.setHandlers(new Handler[]{handler});
|
||||
server.setHandler(handlers);
|
||||
|
||||
var markdownServlet = new MarkdownServlet();
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<h1>400 Bad Request</h1>
|
||||
<p>{message}</p>
|
||||
<a href="/docs/index.md">Try something else!</a>
|
|
@ -1,2 +0,0 @@
|
|||
<h1>404 File Not Found</h1>
|
||||
<a href="/docs/index.md">Try something else!</a>
|
|
@ -1,83 +0,0 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<!--
|
||||
<div class="col-md-4 mb-5 mb-lg-0">
|
||||
<h4 class="text-uppercase mb-4">Location</h4>
|
||||
<p class="lead mb-0">2215 John Daniel Drive
|
||||
<br>Clark, MO 65243</p>
|
||||
</div>
|
||||
<div class="col-md-4 mb-5 mb-lg-0">
|
||||
<h4 class="text-uppercase mb-4">Around the Web</h4>
|
||||
<ul class="list-inline mb-0">
|
||||
<li class="list-inline-item">
|
||||
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
|
||||
<i class="fa fa-fw fa-facebook"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
|
||||
<i class="fa fa-fw fa-google-plus"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
|
||||
<i class="fa fa-fw fa-twitter"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
|
||||
<i class="fa fa-fw fa-linkedin"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li class="list-inline-item">
|
||||
<a class="btn btn-outline-light btn-social text-center rounded-circle" href="#">
|
||||
<i class="fa fa-fw fa-dribbble"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4 class="text-uppercase mb-4">About Freelancer</h4>
|
||||
<p class="lead mb-0">Freelance is a free to use, open source Bootstrap theme created by
|
||||
<a href="http://startbootstrap.com">Start Bootstrap</a>.</p>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap core JavaScript -->
|
||||
<script src="/static/vendor/jquery/jquery.min.js"></script>
|
||||
<script src="/static/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Plugin JavaScript -->
|
||||
<script src="/static/vendor/jquery-ui/jquery-ui-1.12.1.js"></script>
|
||||
<script src="/static/vendor/jquery-easing/jquery.easing.min.js"></script>
|
||||
<script src="/static/vendor/magnific-popup/jquery.magnific-popup.min.js"></script>
|
||||
|
||||
<!-- Custom scripts for this template -->
|
||||
<script src="/static/freelancer.min.js"></script>
|
||||
|
||||
<script>
|
||||
$( "#search" ).autocomplete({
|
||||
source: "/search",
|
||||
minLength: 2,
|
||||
select: function( event, ui ) {
|
||||
location.href = "/docs/" + ui.item.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-129283922-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-129283922-1');
|
||||
</script>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="CTB Recorder is a free recording software for Chaturbate">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>CTB Recorder</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="/static/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom fonts for this template -->
|
||||
<link href="/static/vendor/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Plugin CSS -->
|
||||
<link href="/static/vendor/magnific-popup/magnific-popup.css" rel="stylesheet" type="text/css">
|
||||
<link href="/static/vendor/jquery-ui/jquery-ui-1.12.1.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="/static/freelancer.css" rel="stylesheet">
|
||||
|
||||
<link rel="shortcut icon" href="/static/favicon.png" type="image/x-icon" />
|
||||
|
||||
<style>
|
||||
.ui-front {
|
||||
z-index: 2000;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg bg-secondary fixed-top text-uppercase" id="mainNav" style="padding-pottom: 3rem">
|
||||
<div class="container">
|
||||
<a class="navbar-brand js-scroll-trigger" href="/docs/index.md"><img src="/static/icon64.png" alt="Logo"/>CTB Recorder</a>
|
||||
<button class="navbar-toggler navbar-toggler-right text-uppercase bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
Menu
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<div class="ui-widget">
|
||||
<label for="search">Search: </label>
|
||||
<input id="search" style="margin-top: .7rem" placeholder="Search">
|
||||
</div>
|
||||
<!--
|
||||
<div class="ui-widget"><input id="search" class="ui-autocomplete-input" autocomplete="off" placeholder="Search" style="margin-top: .7rem"></div>
|
||||
-->
|
||||
</li>
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="/docs/index.md">Index</a>
|
||||
</li>
|
||||
<!--
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="#faq">FAQ</a>
|
||||
</li>
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="#code">Source Code</a>
|
||||
</li>
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a href="#donate" class="js-scroll-trigger" style="float:right; margin-left: 10px">
|
||||
<img src="/static/button-red.png" alt="Buy a coffee" style="width:150px; padding-top: 7px"/>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Header -
|
||||
<header class="masthead bg-primary text-white text-center">
|
||||
<div class="container">
|
||||
<h1 class="text-uppercase mb-0">CTB Recorder</h1>
|
||||
<hr class="star-light">
|
||||
<h2 class="font-weight-light mb-0">A free recording software for different camsites.<br/>Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate</h2>
|
||||
</div>
|
||||
</header>
|
||||
-->
|
||||
|
||||
|
||||
<section id="code">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<p class="lead">
|
||||
|
||||
|
|
@ -79,6 +79,11 @@
|
|||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>${antlr.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.vladsch.flexmark</groupId>
|
||||
<artifactId>flexmark</artifactId>
|
||||
<version>0.40.34</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
|
|
|
@ -1,67 +1,71 @@
|
|||
package ctbrec.docs;
|
||||
package ctbrec.servlet;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractDocServlet extends HttpServlet {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(AbstractDocServlet.class);
|
||||
public static final String CLASSPATH_DIR = "/docs";
|
||||
private static final Pattern CONTEXT_AWARE_URL = Pattern.compile("@\\{(.*?)\\}");
|
||||
|
||||
String loadFile(String resource) throws IOException {
|
||||
InputStream resourceAsStream = getClass().getResourceAsStream(resource);
|
||||
if(resourceAsStream == null) {
|
||||
throw new FileNotFoundException();
|
||||
try (InputStream resourceAsStream = getClass().getResourceAsStream(resource)) {
|
||||
if (resourceAsStream == null) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
var out = new ByteArrayOutputStream();
|
||||
var length = 0;
|
||||
var buffer = new byte[1024];
|
||||
while ((length = resourceAsStream.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, length);
|
||||
}
|
||||
return out.toString(UTF_8);
|
||||
}
|
||||
var out = new ByteArrayOutputStream();
|
||||
var length = 0;
|
||||
var buffer = new byte[1024];
|
||||
while( (length = resourceAsStream.read(buffer)) >= 0 ) {
|
||||
out.write(buffer, 0, length);
|
||||
}
|
||||
return new String(out.toByteArray(), UTF_8);
|
||||
}
|
||||
|
||||
protected String getBaseDir() {
|
||||
return Optional.ofNullable(getServletContext().getContextPath()).orElse("") + CLASSPATH_DIR;
|
||||
}
|
||||
|
||||
String getHeader() throws IOException {
|
||||
return loadFile("/html/docs/header.html");
|
||||
return renderContextAwareUris(loadFile(CLASSPATH_DIR + "/header.html"));
|
||||
}
|
||||
|
||||
String getFooter() throws IOException {
|
||||
return loadFile("/html/docs/footer.html");
|
||||
return renderContextAwareUris(loadFile(CLASSPATH_DIR + "/footer.html"));
|
||||
}
|
||||
|
||||
private String renderContextAwareUris(String s) {
|
||||
Matcher m = CONTEXT_AWARE_URL.matcher(s);
|
||||
var contextPath = Optional.ofNullable(getServletContext().getContextPath()).orElse("");
|
||||
return m.replaceAll(matchResult -> contextPath + matchResult.group(1));
|
||||
}
|
||||
|
||||
List<String> getPages() throws IOException {
|
||||
List<String> pages = new ArrayList<>();
|
||||
URL resource = getClass().getResource("/html/docs");
|
||||
if(Objects.equals(resource.getProtocol(), "file")) {
|
||||
LOG.debug("FILE {}", resource);
|
||||
URL resource = getClass().getResource(CLASSPATH_DIR);
|
||||
if (Objects.equals(resource.getProtocol(), "file")) {
|
||||
log.debug("FILE {}", resource);
|
||||
indexDirectory(resource, pages);
|
||||
} else if(Objects.equals(resource.getProtocol(), "jar")) {
|
||||
LOG.debug("JAR {}", resource);
|
||||
} else if (Objects.equals(resource.getProtocol(), "jar")) {
|
||||
log.debug("JAR {}", resource);
|
||||
indexJar(resource, pages);
|
||||
}
|
||||
pages.add("index.md");
|
||||
Collections.sort(pages, (a, b) -> a.compareToIgnoreCase(b));
|
||||
pages.sort(String::compareToIgnoreCase);
|
||||
return pages;
|
||||
}
|
||||
|
||||
|
@ -91,23 +95,27 @@ public abstract class AbstractDocServlet extends HttpServlet {
|
|||
}
|
||||
|
||||
String loadMarkdown(String path) throws IOException {
|
||||
String resource = "/html" + path;
|
||||
return loadFile(resource);
|
||||
var contextPath = getServletContext().getContextPath();
|
||||
if (contextPath != null && path.startsWith(contextPath)) {
|
||||
path = path.substring(contextPath.length());
|
||||
}
|
||||
return loadFile(path);
|
||||
}
|
||||
|
||||
protected void error(HttpServletResponse resp, int status, String message) {
|
||||
try {
|
||||
resp.setStatus(status);
|
||||
resp.getWriter().println(getHeader());
|
||||
String html = loadFile("/html/docs/" + status + ".html");
|
||||
if(message == null || message.trim().isEmpty()) {
|
||||
String html = loadFile(CLASSPATH_DIR + "/" + status + ".html");
|
||||
if (message == null || message.trim().isEmpty()) {
|
||||
message = "";
|
||||
}
|
||||
html = html.replace("{message}", message);
|
||||
html = renderContextAwareUris(html);
|
||||
resp.getWriter().println(html);
|
||||
resp.getWriter().println(getFooter());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error while sending error response. Man, his is bad!", e);
|
||||
log.error("Error while sending error response. Man, his is bad!", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +1,26 @@
|
|||
package ctbrec.docs;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
package ctbrec.servlet;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.vladsch.flexmark.html.HtmlRenderer;
|
||||
import com.vladsch.flexmark.parser.Parser;
|
||||
import com.vladsch.flexmark.util.ast.Node;
|
||||
import com.vladsch.flexmark.util.options.MutableDataSet;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class MarkdownServlet extends AbstractDocServlet {
|
||||
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(MarkdownServlet.class);
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
String path = req.getRequestURI();
|
||||
LOG.trace("Path: [{}]", path);
|
||||
log.debug("Path: [{}]", path);
|
||||
try {
|
||||
if(Objects.equal(path, "/docs/index.md")) {
|
||||
if (Objects.equal(path, getBaseDir() + "/index.md")) {
|
||||
listPages(resp);
|
||||
} else {
|
||||
String md = loadMarkdown(path);
|
||||
|
@ -37,9 +31,11 @@ public class MarkdownServlet extends AbstractDocServlet {
|
|||
resp.getWriter().println(getFooter());
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
error(resp, HttpServletResponse.SC_NOT_FOUND, "");
|
||||
log.error("Error loading markdown page", e);
|
||||
error(resp, HttpServletResponse.SC_NOT_FOUND, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
error(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "");
|
||||
log.error("Error loading markdown page", e);
|
||||
error(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +43,7 @@ public class MarkdownServlet extends AbstractDocServlet {
|
|||
List<String> pages = getPages();
|
||||
var html = new StringBuilder("<ul>");
|
||||
for (String page : pages) {
|
||||
html.append("<li><a href=\"/docs/").append(page).append("\">").append(page).append("</a></li>");
|
||||
html.append("<li><a href=\"").append(getBaseDir()).append("/").append(page).append("\">").append(page).append("</a></li>");
|
||||
}
|
||||
html.append("</ul>");
|
||||
resp.setStatus(HttpServletResponse.SC_OK);
|
|
@ -1,25 +1,23 @@
|
|||
package ctbrec.docs;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.*;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
package ctbrec.servlet;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
|
||||
public class SearchServlet extends AbstractDocServlet {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SearchServlet.class);
|
||||
private static final String Q = "term";
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
|
||||
try {
|
||||
if (req.getParameter(Q) == null) {
|
||||
error(resp, HttpServletResponse.SC_BAD_REQUEST, "Parameter \"" + Q + "\" is missing");
|
||||
|
@ -34,7 +32,7 @@ public class SearchServlet extends AbstractDocServlet {
|
|||
String q = req.getParameter(Q).toLowerCase();
|
||||
String[] tokens = q.split("\\s+");
|
||||
searchPages(result, pages, tokens);
|
||||
resp.getWriter().println(result.toString());
|
||||
resp.getWriter().println(result);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
resp.sendError(SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
|
||||
|
@ -45,13 +43,15 @@ public class SearchServlet extends AbstractDocServlet {
|
|||
}
|
||||
|
||||
private void searchPages(JSONArray result, List<String> pages, String[] tokens) throws IOException {
|
||||
LOG.debug(pages.toString());
|
||||
for (String page : pages) {
|
||||
try {
|
||||
String content = loadMarkdown("/docs/" + page).toLowerCase();
|
||||
String content = loadMarkdown(CLASSPATH_DIR + "/" + page).toLowerCase();
|
||||
var allFound = true;
|
||||
for (String token : tokens) {
|
||||
if (!content.contains(token)) {
|
||||
allFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allFound) {
|
|
@ -0,0 +1,3 @@
|
|||
<h1>400 Bad Request</h1>
|
||||
<p>{message}</p>
|
||||
<a href="@{/docs/index.md}">Try something else!</a>
|
|
@ -0,0 +1,2 @@
|
|||
<h1>404 File Not Found</h1>
|
||||
<a href="@{/docs/index.md}">Try something else!</a>
|
|
@ -0,0 +1,3 @@
|
|||
<h1>500 Internal Server Error</h1>
|
||||
<p>{message}</p>
|
||||
<a href="@{/docs/index.md}">Try something else!</a>
|
|
@ -1,18 +1,23 @@
|
|||
#### Configuration File
|
||||
|
||||
The configuration file stores all your settings and recorded models.
|
||||
|
||||
##### Location
|
||||
|
||||
ctbrec application:
|
||||
|
||||
- Windows: `C:\Users\{your user name}\AppData\Roaming\ctbrec\settings.json`
|
||||
- Linux: `~/.config/ctbrec/settings.json`
|
||||
- macOS: `/Users/{your user name}/Library/Preferences/ctbrec/settings.json`
|
||||
- macOS: `/Users/{your user name}/Library/Preferences/ctbrec/settings.json`
|
||||
|
||||
server:
|
||||
|
||||
- Windows: `C:\Users\{your user name}\AppData\Roaming\ctbrec\server.json`
|
||||
- Linux: `~/.config/ctbrec/server.json`
|
||||
- macOS: `/Users/{your user name}/Library/Preferences/ctbrec/server.json`
|
||||
|
||||
##### Values
|
||||
|
||||
The application and the server share the same configuration file structure. That's why there are
|
||||
values, which don't make sense in the server configuration and vice versa. These values are simply
|
||||
ignored. This is a collection of the most interesting values:
|
||||
|
@ -20,20 +25,23 @@ ignored. This is a collection of the most interesting values:
|
|||
- **chooseStreamQuality** (app only) - [`true`,`false`] Opens the stream resolution selection dialog, when you start recording a model.
|
||||
|
||||
- **concurrentRecordings** - [0 - 2147483647] Limits the number of concurrently running recordings. Once this number is reached, now more recordings are started
|
||||
until a recording is finished. 0 means unlimited.
|
||||
until a recording is finished. 0 means unlimited.
|
||||
|
||||
- **determineResolution** (app only) - [`true`,`false`] Display the stream resolution on the thumbnails.
|
||||
|
||||
- **hlsdlExecutable** - Path to the hlsdl executable, which is used, if `useHlsdl` is set to true
|
||||
|
||||
- **httpPort** - [1 - 65536] The TCP port, the server listens on. In the server config, this will tell the server, which port to use. In the application this will set
|
||||
the port ctbrec tries to connect to, if it is run in remote mode.
|
||||
- **httpPort** - [1 - 65536] The TCP port, the server listens on. In the server config, this will tell the server, which port to use. In the application this
|
||||
will set
|
||||
the port ctbrec tries to connect to, if it is run in remote mode.
|
||||
|
||||
- **httpServer** (app only) - The TCP host where the server is running. Has no effect, if ctbrec is run in local reocrding mode.
|
||||
|
||||
- **httpTimeout** - [1 - 2147483647] in milliseconds. In the server configuration this sets the idle timeout for connections to the server. In the app this sets the connect and read timeout for any HTTP connection.
|
||||
- **httpTimeout** - [1 - 2147483647] in milliseconds. In the server configuration this sets the idle timeout for connections to the server. In the app this sets
|
||||
the connect and read timeout for any HTTP connection.
|
||||
|
||||
- **httpUserAgent** - The user agent, which is used in the HTTP header, when ctbrec connects to a camsite. This is used to disguise, that it actually is a recording software :)
|
||||
- **httpUserAgent** - The user agent, which is used in the HTTP header, when ctbrec connects to a camsite. This is used to disguise, that it actually is a
|
||||
recording software :)
|
||||
|
||||
- **httpUserAgentMobile** - Same as *httpUserAgent*, but in same cases we have to pretend to be a mobile phone :)
|
||||
|
||||
|
@ -45,39 +53,55 @@ the port ctbrec tries to connect to, if it is run in remote mode.
|
|||
|
||||
- **loghlsdlOutput** - [`true`,`false`] The output from hlsdl will be logged in temporary files. Only in effect, if `useHlsdl` is set to true
|
||||
|
||||
- **minimumResolution** - [1 - 2147483647]. Sets the minimum video height for a recording. ctbrec tries to find a stream quality, which is higher than or equal to this value. If the only provided stream quality is below this threshold, ctbrec won't record the stream.
|
||||
- **minimumResolution** - [1 - 2147483647]. Sets the minimum video height for a recording. ctbrec tries to find a stream quality, which is higher than or equal
|
||||
to this value. If the only provided stream quality is below this threshold, ctbrec won't record the stream.
|
||||
|
||||
- **maximumResolution** - [1 - 2147483647]. Sets the maximum video height for a recording. ctbrec tries to find a stream quality, which is lower than or equal to this value. If the only provided stream quality is above this threshold, ctbrec won't record the stream.
|
||||
- **maximumResolution** - [1 - 2147483647]. Sets the maximum video height for a recording. ctbrec tries to find a stream quality, which is lower than or equal
|
||||
to this value. If the only provided stream quality is above this threshold, ctbrec won't record the stream.
|
||||
|
||||
- **minimumLengthInSeconds** - **Deprecated. Add a post-processing step instead. See [Post-Processing](/docs/PostProcessing.md)** [0 - 2147483647] Automatically delete recordings, which are shorter than this amount of seconds. 0 disables this feature.
|
||||
- **minimumLengthInSeconds** - **Deprecated. Add a post-processing step instead. See [Post-Processing](PostProcessing.md)** [0 - 2147483647]
|
||||
Automatically delete recordings, which are shorter than this amount of seconds. 0 disables this feature.
|
||||
|
||||
- **minimumSpaceLeftInBytes** - [0 - 9223372036854775807] The space in bytes ctbrec should conserve on the hard drive. 1 GiB = 1024 MiB = 1048576 KiB = 1073741824 bytes
|
||||
- **minimumSpaceLeftInBytes** - [0 - 9223372036854775807] The space in bytes ctbrec should conserve on the hard drive. 1 GiB = 1024 MiB = 1048576 KiB =
|
||||
1073741824 bytes
|
||||
|
||||
- **onlineCheckIntervalInSecs** - [1 - 2147483647] How often ctbrec checks, if a model is online. This is not a guaranteed interval: If you record many models, the online check for all models can take longer than this interval. A minute is a reasonable value, but you can go lower, if you don't want to miss a anything. But don't go too low, or you risk to do too many requests in a short amount of time and get banned by some sites.
|
||||
- **onlineCheckIntervalInSecs** - [1 - 2147483647] How often ctbrec checks, if a model is online. This is not a guaranteed interval: If you record many models,
|
||||
the online check for all models can take longer than this interval. A minute is a reasonable value, but you can go lower, if you don't want to miss a
|
||||
anything. But don't go too low, or you risk to do too many requests in a short amount of time and get banned by some sites.
|
||||
|
||||
- **onlineCheckSkipsPausedModels** - [`true`,`false`] Skip the online check for paused models. If you have many models in the recording list, this can reduce the delay when a recording starts after a model came online.
|
||||
- **onlineCheckSkipsPausedModels** - [`true`,`false`] Skip the online check for paused models. If you have many models in the recording list, this can reduce
|
||||
the delay when a recording starts after a model came online.
|
||||
|
||||
- **postProcessing** - **Deprecated. See [Post-Processing](/docs/PostProcessing.md)** Absolute path to a script, which is executed once a recording is finished.
|
||||
- **postProcessing** - **Deprecated. See [Post-Processing](PostProcessing.md)** Absolute path to a script, which is executed once a recording is
|
||||
finished.
|
||||
|
||||
- **recordingsDir** - Where ctbrec saves the recordings.
|
||||
|
||||
- **recordingsDirStructure** (server only) - [`FLAT`, `ONE_PER_MODEL`, `ONE_PER_RECORDING`] How recordings are stored in the file system. `FLAT` - all recordings in one directory; `ONE_PER_MODEL` - one directory per model; `ONE_PER_RECORDING` - each recordings ends up in its own directory. Change this only, if you have `recordSingleFile` set to `true`
|
||||
- **recordingsDirStructure** (server only) - [`FLAT`, `ONE_PER_MODEL`, `ONE_PER_RECORDING`] How recordings are stored in the file system. `FLAT` - all
|
||||
recordings in one directory; `ONE_PER_MODEL` - one directory per model; `ONE_PER_RECORDING` - each recordings ends up in its own directory. Change this only,
|
||||
if you have `recordSingleFile` set to `true`
|
||||
|
||||
- **recordSingleFile** (server only) - [`true`,`false`] - How recordings are stored in the file system. `true` means, each recording is saved in one large file. `false` means, ctbrec just downloads the stream segments.
|
||||
- **recordSingleFile** (server only) - [`true`,`false`] - How recordings are stored in the file system. `true` means, each recording is saved in one large
|
||||
file. `false` means, ctbrec just downloads the stream segments.
|
||||
|
||||
- **splitStrategy** - [`DONT`, `TIME`, `SIZE`, `TIME_OR_SIZE`] Defines if and how to split recordings. Also see `splitRecordingsAfterSecs` and `splitRecordingsBiggerThanBytes`
|
||||
- **splitStrategy** - [`DONT`, `TIME`, `SIZE`, `TIME_OR_SIZE`] Defines if and how to split recordings. Also see `splitRecordingsAfterSecs`
|
||||
and `splitRecordingsBiggerThanBytes`
|
||||
|
||||
- **splitRecordingsAfterSecs** - [0 - 2147483647] in seconds. Split recordings after this amount of seconds. The recordings are split up into several individual recordings,
|
||||
which have the defined length (roughly). Has to be activated with `splitStrategy`.
|
||||
- **splitRecordingsAfterSecs** - [0 - 2147483647] in seconds. Split recordings after this amount of seconds. The recordings are split up into several individual
|
||||
recordings,
|
||||
which have the defined length (roughly). Has to be activated with `splitStrategy`.
|
||||
|
||||
- **splitRecordingsBiggerThanBytes** - [0 - 9223372036854775807] in bytes. Split recordings, if the size on disk exceeds this value. The recordings are split up into several individual recordings,
|
||||
which have the defined size (roughly). Has to be activated with `splitStrategy`.
|
||||
- **splitRecordingsBiggerThanBytes** - [0 - 9223372036854775807] in bytes. Split recordings, if the size on disk exceeds this value. The recordings are split up
|
||||
into several individual recordings,
|
||||
which have the defined size (roughly). Has to be activated with `splitStrategy`.
|
||||
|
||||
- **timeoutRecordingStartingAt** - [00:00 - 23:59] - Start of the recording timeout timeframe - No new recordings will be started in this period
|
||||
|
||||
- **timeoutRecordingEndingAt** - [00:00 - 23:59] - End of the recording timeout timeframe - No new recordings will be started in this period
|
||||
|
||||
- **useHlsdl** - [`true`,`false`] Use hlsdl to record the live streams. You also have to set `hlsdlExecutable`, if hlsdl is not globally available on your system. hlsdl won't be used for MV Live, LiveJasmin and Showup.
|
||||
- **useHlsdl** - [`true`,`false`] Use hlsdl to record the live streams. You also have to set `hlsdlExecutable`, if hlsdl is not globally available on your
|
||||
system. hlsdl won't be used for MV Live, LiveJasmin and Showup.
|
||||
|
||||
- **webinterface** (server only) - [`true`,`false`] Enables the webinterface for the server. You can access it with http://host:port/static/index.html Don't activate this on
|
||||
a machine, which can be accessed from the internet, because this is totally unprotected at the moment.
|
||||
- **webinterface** (server only) - [`true`,`false`] Enables the webinterface for the server. You can access it with http://host:port/static/index.html Don't
|
||||
activate this on
|
||||
a machine, which can be accessed from the internet, because this is totally unprotected at the moment.
|
|
@ -1,8 +1,10 @@
|
|||
#### FFmpeg
|
||||
|
||||
[FFmpeg](https://ffmpeg.org) is a multimedia framework, which comes with a very powerful command line tool to process multimedia files.
|
||||
It is available for all major platforms. FFmpeg can be used to manually and automatically remux recordings.
|
||||
|
||||
##### Installation
|
||||
|
||||
* Linux: Use the package manager of your distribution to find and install ffmpeg
|
||||
* Windows:
|
||||
1. Download the latest stable version from the homepage
|
||||
|
@ -13,14 +15,16 @@ It is available for all major platforms. FFmpeg can be used to manually and auto
|
|||
* macOS: See Windows
|
||||
|
||||
##### Manual remuxing
|
||||
|
||||
* Open a terminal (command prompt) and cd into the directory of the recording
|
||||
* Run FFmpeg:
|
||||
|
||||
`ffmpeg -i AwesomeGirl_-2019-04-04_15-46-19_195.ts -c:v copy -c:a copy AwesomeGirl_-2019-04-04_15-46-19_195.mp4`
|
||||
`ffmpeg -i AwesomeGirl_-2019-04-04_15-46-19_195.ts -c:v copy -c:a copy AwesomeGirl_-2019-04-04_15-46-19_195.mp4`
|
||||
|
||||
As you can see, the codecs for video and audio are set to copy, which means, that the recording is not reencoded, but just "copied" to
|
||||
As you can see, the codecs for video and audio are set to copy, which means, that the recording is not reencoded, but just "copied" to
|
||||
another container format. You could also use mkv or avi for the output file suffix and FFmpeg would create that respective file. Depending
|
||||
on your hardware specs and the length of the recording, this process probably takes a few seconds up to a couple of minutes.
|
||||
|
||||
|
||||
##### Automatic remuxing
|
||||
FFmpeg can also be used to automatically remux recordings. ctbrec provides a [post-processing](/docs/PostProcessing.md) mechanism for this purpose.
|
||||
|
||||
FFmpeg can also be used to automatically remux recordings. ctbrec provides a [post-processing](PostProcessing.md) mechanism for this purpose.
|
|
@ -137,7 +137,7 @@ The part you have to copy is
|
|||
|
||||
|
||||
|
||||
For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html)
|
||||
For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/DateTimeFormatter.html)
|
||||
|
||||
#### Full Example
|
||||
|
|
@ -1,26 +1,33 @@
|
|||
#### Are ticket / gold / private shows supported?
|
||||
|
||||
No. I never intended to record these kind of shows and I never tested it. You might be able to record
|
||||
them, but you have to figure it out yourself.
|
||||
|
||||
#### How can I convert the recordings to mp4 / mkv?
|
||||
|
||||
To convert the files to another format, you have to remux them. You don't have to reencode them, since the
|
||||
files (usually, have not seen an exception yet) already contain H.264 video and AAC audio. You can remux the
|
||||
files manually with tools like [Avidemux](/docs/Avidemux.md), [MKVToolNix](/docs/MKVToolNix.md) or
|
||||
[FFmpeg](/docs/FFmpeg.md) or use one of the [post-processing](/docs/PostProcessing.md) scripts.
|
||||
files manually with tools like [Avidemux](Avidemux.md), [MKVToolNix](MKVToolNix.md) or
|
||||
[FFmpeg](FFmpeg.md) or use one of the [post-processing](PostProcessing.md) scripts.
|
||||
|
||||
#### Streams are not getting recorded even though the model is online
|
||||
|
||||
- Is "Leave space on device set" and do you have enough space left?
|
||||
- Is "Maximum resolution" set? In case maximum resolution is set and ctbrec cannot determine the
|
||||
- Is "Maximum resolution" set? In case maximum resolution is set and ctbrec cannot determine the
|
||||
resolution of a stream, the stream will not be recorded.
|
||||
- Is "Concurrent Recordings" set and you reached the maximum?
|
||||
|
||||
#### How can I playback the recorded .ts files?
|
||||
|
||||
Use one of the following players:
|
||||
|
||||
- [mpv](https://mpv.io/installation/)
|
||||
- [VLC](https://www.videolan.org/vlc/)
|
||||
|
||||
#### How can I playback the server recordings?
|
||||
|
||||
Use one of the following players:
|
||||
|
||||
- [mpv](https://mpv.io/installation/)
|
||||
- [VLC](https://www.videolan.org/vlc/)
|
||||
|
||||
|
@ -30,17 +37,20 @@ Under **General** select the **Player** of your choice. Then you can start the p
|
|||
**Recordings** tab.
|
||||
|
||||
#### The login for site XYZ does not work anymore and the credentails work in a browser
|
||||
Stop CTB Recorder. Then open the settings directory (check [ConfigurationFile](/docs/ConfigurationFile.md) for the location) and
|
||||
|
||||
Stop CTB Recorder. Then open the settings directory (check [ConfigurationFile](ConfigurationFile.md) for the location) and
|
||||
delete the cookies file for that site. Start CTB Recorder again and it should work again. If it does not work, check the log
|
||||
file for errors. The log file is called ctbrec.log and you can find it in the installation directory of CTB Recorder.
|
||||
If that does not work go back to your settings directory, go up to the parent directory and delete ctbrec-minimal-browser, if it
|
||||
exists.
|
||||
|
||||
#### It takes a long time until a recording starts for a model
|
||||
|
||||
You probably have a lot of models in the "Recording" list. CTB Recorder checks the models one after the other. This is done on
|
||||
purpose to not fire too many requests in a short amount of time, because this can cause blocks by the camsites.
|
||||
|
||||
#### Can I run several instances of CTB Recorder
|
||||
|
||||
It is possible to define the configuration directory and configuration file, which is used by ctbrec. This way you
|
||||
can create several instances with different configurations.
|
||||
On Windows, create a file called `ctbrec.l4j.ini` right next to `ctbrec.exe`. Add one of the following settings
|
||||
|
@ -58,4 +68,4 @@ On Linux and macOS edit `ctbrec.sh` and add one or both of the above mentioned s
|
|||
|
||||
`$JAVA -Dctbrec.config=alternate-settings.json -Djdk.gtk.version=3 -cp ctbrec-1.19.1-final.jar ctbrec.ui.Launcher`
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/ATeS6HgADUo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/ATeS6HgADUo" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
|
@ -0,0 +1,41 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap core JavaScript -->
|
||||
<script src="@{/static/vendor/jquery/jquery.min.js}"></script>
|
||||
<script src="@{/static/vendor/bootstrap/js/bootstrap.bundle.min.js}"></script>
|
||||
|
||||
<!-- Plugin JavaScript -->
|
||||
<script src="@{/static/vendor/jquery-ui/jquery-ui-1.12.1.js}"></script>
|
||||
<script src="@{/static/vendor/jquery-easing/jquery.easing.min.js}"></script>
|
||||
<script src="@{/static/vendor/magnific-popup/jquery.magnific-popup.min.js}"></script>
|
||||
|
||||
<!-- Custom scripts for this template -->
|
||||
<script src="@{/static/freelancer.min.js}"></script>
|
||||
|
||||
<script>
|
||||
$( "#search" ).autocomplete({
|
||||
source: "@{/search}",
|
||||
minLength: 2,
|
||||
select: function( event, ui ) {
|
||||
location.href = "@{/docs/}" + ui.item.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-129283922-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'UA-129283922-1');
|
||||
</script>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="CTB Recorder is a free recording software for Chaturbate">
|
||||
<meta name="author" content="">
|
||||
|
||||
<title>CTB Recorder</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="@{/static/vendor/bootstrap/css/bootstrap.min.css}" rel="stylesheet">
|
||||
|
||||
<!-- Custom fonts for this template -->
|
||||
<link href="@{/static/vendor/font-awesome/css/font-awesome.min.css}" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Plugin CSS -->
|
||||
<link href="@{/static/vendor/magnific-popup/magnific-popup.css}" rel="stylesheet" type="text/css">
|
||||
<link href="@{/static/vendor/jquery-ui/jquery-ui-1.12.1.css}" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="@{/static/freelancer.css}" rel="stylesheet">
|
||||
|
||||
<link rel="shortcut icon" href="@{/static/favicon.png}" type="image/x-icon"/>
|
||||
|
||||
<style>
|
||||
.ui-front {
|
||||
z-index: 2000;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg bg-secondary fixed-top text-uppercase" id="mainNav" style="padding-pottom: 3rem">
|
||||
<div class="container">
|
||||
<a class="navbar-brand js-scroll-trigger" href="@{/docs/index.md}"><img src="@{/static/icon64.png}" alt="Logo"/>CTB Recorder</a>
|
||||
<button class="navbar-toggler navbar-toggler-right text-uppercase bg-primary text-white rounded" type="button" data-toggle="collapse"
|
||||
data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
Menu
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<div class="ui-widget">
|
||||
<label for="search">Search: </label>
|
||||
<input id="search" style="margin-top: .7rem" placeholder="Search">
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item mx-0 mx-lg-1">
|
||||
<a class="nav-link py-3 px-0 px-lg-3 rounded js-scroll-trigger" href="@{/docs/index.md}">Index</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<section id="code">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-10 mx-auto">
|
||||
<p class="lead">
|
|
@ -11,6 +11,9 @@ import ctbrec.image.LocalPortraitStore;
|
|||
import ctbrec.recorder.OnlineMonitor;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.recorder.SimplifiedLocalRecorder;
|
||||
import ctbrec.servlet.AbstractDocServlet;
|
||||
import ctbrec.servlet.MarkdownServlet;
|
||||
import ctbrec.servlet.SearchServlet;
|
||||
import ctbrec.servlet.StaticFileServlet;
|
||||
import ctbrec.sites.Site;
|
||||
import ctbrec.sites.amateurtv.AmateurTv;
|
||||
|
@ -228,6 +231,14 @@ public class HttpServer {
|
|||
holder = new ServletHolder(hlsServlet);
|
||||
defaultContext.addServlet(holder, "/hls/*");
|
||||
|
||||
var markdownServlet = new MarkdownServlet();
|
||||
holder = new ServletHolder(markdownServlet);
|
||||
defaultContext.addServlet(holder, "/docs/*");
|
||||
|
||||
AbstractDocServlet searchServlet = new SearchServlet();
|
||||
holder = new ServletHolder(searchServlet);
|
||||
defaultContext.addServlet(holder, "/search/*");
|
||||
|
||||
LocalPortraitStore portraitStore = new LocalPortraitStore(config);
|
||||
ImageServlet imageServlet = new ImageServlet(portraitStore, config);
|
||||
holder = new ServletHolder(imageServlet);
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="190.60297mm"
|
||||
height="50.232338mm"
|
||||
viewBox="0 0 190.60297 50.232338"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
sodipodi:docname="drawing5.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.50458838"
|
||||
inkscape:cx="113.95427"
|
||||
inkscape:cy="112.96336"
|
||||
inkscape:window-width="1350"
|
||||
inkscape:window-height="1041"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="201.10301"
|
||||
y="249.36774"
|
||||
width="341.87512"
|
||||
height="192.25449"
|
||||
id="rect5394" />
|
||||
<rect
|
||||
x="46.642029"
|
||||
y="540.36493"
|
||||
width="171.77917"
|
||||
height="101.24732"
|
||||
id="rect3848" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-6.0957813,-65.698975)">
|
||||
<path
|
||||
style="fill:#8f0000;fill-opacity:1;stroke-width:0.4"
|
||||
d="m 31.187601,65.676483 c -14.867087,0 -25.0604297,12.145925 -25.0604297,25.060438 0,12.871899 10.2026987,25.060429 25.0604297,25.060429 10.942038,-0.0419 17.568167,-6.46268 20.333815,-10.41362 0.890505,-1.33072 0.239004,-2.77067 -0.671995,-3.29126 C 47.226626,100.00085 35.09478,92.992731 31.187605,90.736921 35.116469,88.468591 47.083491,81.599702 50.721886,79.499073 51.71263,78.899657 52.39459,77.352751 51.42337,76.021375 49.135092,72.445016 42.071934,65.676483 31.187601,65.676483 Z"
|
||||
id="path810"
|
||||
sodipodi:nodetypes="csccccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:63.5px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#ffffff;stroke-width:0.264583"
|
||||
x="86.197777"
|
||||
y="59.167858"
|
||||
id="text712"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan710"
|
||||
style="stroke-width:0.264583"
|
||||
x="86.197777"
|
||||
y="59.167858" /></text>
|
||||
<circle
|
||||
style="fill:#666666;stroke-width:0.264583"
|
||||
id="path402"
|
||||
cx="31.159586"
|
||||
cy="90.792908"
|
||||
r="12.868871" />
|
||||
<circle
|
||||
style="fill:#000000;stroke-width:0.264583"
|
||||
id="path456"
|
||||
cx="31.082405"
|
||||
cy="90.551949"
|
||||
r="9.6072893" />
|
||||
<circle
|
||||
style="fill:#1a1a1a;stroke-width:0.264583"
|
||||
id="path510"
|
||||
cx="31.082407"
|
||||
cy="90.709946"
|
||||
r="4.8020678" />
|
||||
<circle
|
||||
style="fill:#000000;stroke-width:0.264583"
|
||||
id="path512"
|
||||
cx="31.139042"
|
||||
cy="90.831169"
|
||||
r="2.2975652" />
|
||||
<circle
|
||||
style="fill:#ffffff;stroke-width:0.264583"
|
||||
id="path568"
|
||||
cx="26.182495"
|
||||
cy="85.988632"
|
||||
r="1.9699489" />
|
||||
<path
|
||||
style="fill:#8f0000;fill-opacity:1;stroke-width:0.398684"
|
||||
d="m 119.15358,79.057993 c 3.2261,0.01024 3.35522,2.293559 3.35363,3.240357 -0.002,1.012946 -0.27631,3.463011 -3.23792,3.600477 -1.56017,0.07242 0.8945,8.482753 1.29225,10.250005 3.71783,0.353582 3.7632,3.488658 3.76023,3.930868 -0.003,0.46249 -0.0372,3.73451 -3.71204,3.87107 -1.09056,0.0405 -1.30226,7.18462 -0.13206,11.95844 16.47009,-0.0127 18.98277,-10.12282 18.87138,-14.3112 -0.27154,-10.21057 -8.98698,-11.653292 -8.47037,-11.505267 4.30881,-1.13225 6.80963,-4.831078 6.75397,-10.314565 -0.11279,-11.112067 -10.58989,-12.892556 -18.89328,-12.69598 -9.78444,-0.0052 -13.00459,-6.29e-4 -18.36807,-0.005 -0.818535,-0.03837 -1.854428,0.677635 -1.909663,1.937322 0.0195,3.5776 -0.0031,40.4314 -6e-5,45.06496 0.03427,0.80542 0.501638,1.74136 1.765573,1.83658 2.36851,-0.005 17.15294,-9.9e-4 20.43463,-0.003 3.88419,-3.82572 2.519,-9.59872 0.29245,-11.96976 -3.44554,0.002 -5.9282,0.0237 -7.20475,8e-4 -0.0142,-1.84124 -0.0202,-5.872239 -0.002,-7.786072 2.08555,-0.01709 6.03598,-0.01808 7.26166,-9.93e-4 0.83121,-1.284336 0.45169,-8.506761 -1.48429,-10.272981 -2.94584,0.0051 -4.16513,0.0403 -5.79006,0.0074 0.0161,-1.992368 -0.0228,-4.964702 0.0379,-6.814219 1.57549,-0.03118 4.05157,-0.04582 4.9703,-0.0189 z"
|
||||
id="path2440-1"
|
||||
sodipodi:nodetypes="csscsscscsccccccccccccccc" />
|
||||
<rect
|
||||
style="fill:#8f0000;fill-opacity:1;stroke-width:1.08691;stroke-dasharray:none"
|
||||
id="rect2586"
|
||||
width="51.620258"
|
||||
height="21.671476"
|
||||
x="144.98834"
|
||||
y="67.318497"
|
||||
rx="3.3109202"
|
||||
ry="3.3109202" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.4"
|
||||
d="m 149.68826,84.382097 c 0.009,0.24101 0.058,0.37468 0.54809,0.50443 1.25233,0.20687 2.41151,0.0861 2.96861,0.0664 0.52547,-0.0925 0.91165,-0.14481 0.91264,-0.52544 -0.002,-4.221695 -5.5e-4,-4.210255 0.0165,-7.853216 1.02479,-1.396222 1.91698,-1.947401 2.65078,-2.146347 0.93204,-0.231986 1.62419,0.124008 2.12628,0.184665 0.7664,0.258899 0.72253,-0.39777 0.82774,-0.630105 0.0226,-1.240063 0.0272,-0.733665 -0.0201,-2.301515 -0.1134,-0.3784 -0.0675,-0.285045 -0.62301,-0.480823 -1.18326,-0.269704 -2.09471,-0.213567 -2.73468,-0.0035 -0.91643,0.290478 -1.89427,1.067622 -2.89113,2.195197 0.004,-0.847684 -4.3e-4,-0.94918 0.006,-1.59271 -0.0113,-0.106182 -0.0555,-0.349656 -0.56284,-0.446024 -0.52495,-0.113889 -1.89444,-0.140362 -2.65845,0.0035 -0.16209,0.03718 -0.29785,0.07383 -0.4157,0.162786 -0.0249,0.02404 -0.14479,0.109872 -0.14977,0.27049 -0.0987,7.138557 0.0131,6.7101 -8.1e-4,12.592252 z"
|
||||
id="path3123"
|
||||
sodipodi:nodetypes="cccccccccccccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.4"
|
||||
d="m 165.65002,79.013973 h 9.84827"
|
||||
id="path3136" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.4"
|
||||
d="m 176.56658,77.945686 0.0334,-1.26859"
|
||||
id="path3138" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.4"
|
||||
d="m 165.6914,79.033966 c 1.77192,-6.79e-4 7.85149,0.0062 9.62622,-2.97e-4 0.56592,-0.01272 1.17843,-0.238656 1.27815,-1.020665 0.0232,-0.199099 0.0537,-1.289102 -0.0843,-2.053725 -0.12345,-0.758422 -0.4162,-1.71006 -0.98525,-2.444694 -0.56284,-0.738768 -1.21797,-1.311299 -2.36267,-1.805261 -1.05375,-0.465024 -2.46344,-0.725212 -3.86932,-0.707315 -1.40589,0.0179 -3.11591,0.219899 -4.42541,0.908105 -1.17659,0.544226 -1.84039,1.285417 -2.32592,1.877409 -0.7292,1.023936 -1.29198,2.255156 -1.36915,4.319905 -0.0107,1.765712 0.36956,3.018485 0.70381,3.642039 0.53362,1.09426 1.1713,1.76501 2.37751,2.42471 1.15636,0.65416 2.85904,0.97033 4.35458,1.04421 1.4457,0.12925 3.11512,-0.0713 4.22548,-0.2416 1.11037,-0.1703 1.80298,-0.38358 2.42035,-0.59715 0.40649,-0.18564 0.51996,-0.39147 0.57566,-0.772 0.0229,-0.30137 0.0894,-0.89129 -0.0196,-1.4911 -0.0508,-0.24262 -0.17733,-0.65423 -1.23071,-0.26447 -0.76385,0.18978 -1.06904,0.31713 -1.93734,0.47253 -0.97218,0.15158 -0.95492,0.20748 -2.9019,0.23185 -1.20493,-0.0421 -2.01481,-0.23541 -2.82695,-0.74813 -0.67923,-0.534871 -0.79521,-0.844257 -1.02815,-1.420722 -0.13327,-0.42141 -0.17209,-0.714714 -0.19516,-1.353629 z"
|
||||
id="path3161"
|
||||
sodipodi:nodetypes="cccccccccccccccccccccccc" />
|
||||
<path
|
||||
style="fill:#8f0000;fill-opacity:1;stroke-width:0.4"
|
||||
d="m 172.31113,76.739987 c 0.0323,-0.595988 -0.0829,-1.344069 -0.40263,-1.871529 -0.16038,-0.338611 -0.70141,-0.956149 -1.4689,-1.144832 -0.76748,-0.259502 -2.05463,-0.289685 -2.92986,0.111955 -0.3854,0.189184 -0.82359,0.410035 -1.27968,1.084681 -0.32626,0.497601 -0.50498,1.188439 -0.53414,1.819428 3.12512,0.0019 3.0538,2.32e-4 6.61521,2.97e-4 z"
|
||||
id="path3159"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.4"
|
||||
d="m 191.06207,75.119637 c 0.25207,0.07579 0.58292,0.195892 0.8507,-0.268818 0.25289,-0.666763 0.14003,-1.80035 0.0849,-2.027107 -0.0168,-0.05661 0.0103,-0.404027 -0.54919,-0.768549 -0.46589,-0.360269 -1.26122,-0.69132 -2.43179,-0.881815 -1.17058,-0.190495 -3.20149,-0.230489 -4.54786,0.144377 -1.34637,0.374866 -2.44727,0.937664 -3.3387,1.88291 -0.89143,0.945246 -1.42712,1.987517 -1.70244,3.534212 -0.22419,1.405326 -0.158,3.34504 0.38801,4.63508 0.52495,1.272 1.19614,2.06061 2.43936,2.84542 1.01162,0.64645 2.15728,0.83195 3.29659,0.96251 1.22307,0.11531 2.37253,0.0457 3.44523,-0.17549 0.91808,-0.18825 1.65769,-0.39041 2.5662,-0.91863 0.43422,-0.31341 0.38372,-0.29722 0.51595,-0.80258 0.0791,-0.49047 0.13529,-1.6034 -0.12031,-2.221614 -0.15351,-0.316206 -0.54506,-0.241497 -0.83048,-0.06635 -0.47896,0.287872 -0.99351,0.669404 -2.03019,1.028854 -0.89205,0.29778 -1.92915,0.27515 -2.63201,0.14717 -0.64758,-0.12372 -1.14088,-0.38309 -1.57113,-0.79686 -0.58764,-0.603056 -0.81895,-1.299233 -0.97314,-2.164182 -0.13292,-0.869202 -0.0842,-2.047527 0.13453,-2.830895 0.3038,-1.053481 0.75725,-1.533806 1.40202,-1.980192 0.75749,-0.437879 1.3897,-0.476425 2.10397,-0.47913 1.2162,0.02495 1.69232,0.326048 2.25947,0.539475 0.0333,-0.0014 1.11579,0.633195 1.24026,0.662204 z"
|
||||
id="path3232"
|
||||
sodipodi:nodetypes="ccccccccccccccccccccccccc" />
|
||||
<path
|
||||
style="fill:#8f0000;fill-opacity:1;stroke-width:0.4"
|
||||
d="m 68.069712,114.17695 c 0.04384,0.82565 0.675803,1.67069 1.782647,1.75277 1.577625,0 10.337314,-0.003 11.310276,-0.003 1.187936,-0.0579 1.799261,-1.10663 1.775915,-1.81938 0,-0.89667 0.0055,-30.607591 0.0055,-34.961726 1.373899,0 8.80444,-0.0061 10.971753,-0.0061 1.043333,-0.0046 1.853172,-0.768748 1.92019,-1.712231 0,-1.808451 0.01021,-6.704946 0.01021,-8.46579 -0.08801,-1.493452 -1.380867,-1.843375 -1.855758,-1.821404 -1.787762,0 -30.931261,-0.0035 -37.027701,-0.0035 -0.926527,0.07783 -1.724763,0.677268 -1.813719,1.75371 0,1.743415 -0.0072,6.754003 -0.0072,8.48533 0.0737,0.894093 0.705997,1.618213 1.658628,1.766706 2.948641,0 8.09298,0 11.262544,0 0,5.287671 0.0067,29.784295 0.0067,35.034225 z"
|
||||
id="path7989"
|
||||
sodipodi:nodetypes="ccccccccccccccc" />
|
||||
<path
|
||||
style="opacity:0.12;fill:#b3b3b3;fill-opacity:1;stroke-width:0.4"
|
||||
d="M 41.411832,67.937638 C 40.658169,76.919778 37.128974,84.947516 31.223631,90.872512 25.309527,96.806299 16.444692,99.962726 8.3458661,101.14067 6.6432207,97.293039 6.0719726,93.762142 6.0957813,90.369955 6.2452293,83.254278 9.3168733,77.03745 13.816884,72.643399 c 4.500011,-4.394051 10.687755,-6.854807 17.041251,-6.944424 1.579204,-0.02109 6.007668,0.109573 10.553697,2.238663 z"
|
||||
id="path9591"
|
||||
sodipodi:nodetypes="cscczcc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="45.850365mm"
|
||||
height="50.120869mm"
|
||||
viewBox="0 0 45.850366 50.120869"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
sodipodi:docname="favicon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.50458838"
|
||||
inkscape:cx="113.95427"
|
||||
inkscape:cy="110.98155"
|
||||
inkscape:window-width="1350"
|
||||
inkscape:window-height="1041"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<rect
|
||||
x="201.10301"
|
||||
y="249.36774"
|
||||
width="341.87512"
|
||||
height="192.25449"
|
||||
id="rect5394" />
|
||||
<rect
|
||||
x="46.642029"
|
||||
y="540.36493"
|
||||
width="171.77917"
|
||||
height="101.24732"
|
||||
id="rect3848" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-6.0950711,-65.676483)">
|
||||
<path
|
||||
style="fill:#8f0000;fill-opacity:1;stroke-width:0.4"
|
||||
d="m 31.187601,65.676483 c -14.867087,0 -25.0604297,12.145925 -25.0604297,25.060438 0,12.871899 10.2026987,25.060429 25.0604297,25.060429 10.942038,-0.0419 17.568167,-6.46268 20.333815,-10.41362 0.890505,-1.33072 0.239004,-2.77067 -0.671995,-3.29126 C 47.226626,100.00085 35.09478,92.992731 31.187605,90.736921 35.116469,88.468591 47.083491,81.599702 50.721886,79.499073 51.71263,78.899657 52.39459,77.352751 51.42337,76.021375 49.135092,72.445016 42.071934,65.676483 31.187601,65.676483 Z"
|
||||
id="path810"
|
||||
sodipodi:nodetypes="csccccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:63.5px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';fill:#ffffff;stroke-width:0.264583"
|
||||
x="86.197777"
|
||||
y="59.167858"
|
||||
id="text712"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan710"
|
||||
style="stroke-width:0.264583"
|
||||
x="86.197777"
|
||||
y="59.167858" /></text>
|
||||
<circle
|
||||
style="fill:#666666;stroke-width:0.264583"
|
||||
id="path402"
|
||||
cx="31.159586"
|
||||
cy="90.792908"
|
||||
r="12.868871" />
|
||||
<circle
|
||||
style="fill:#000000;stroke-width:0.264583"
|
||||
id="path456"
|
||||
cx="31.082405"
|
||||
cy="90.551949"
|
||||
r="9.6072893" />
|
||||
<circle
|
||||
style="fill:#1a1a1a;stroke-width:0.264583"
|
||||
id="path510"
|
||||
cx="31.082407"
|
||||
cy="90.709946"
|
||||
r="4.8020678" />
|
||||
<circle
|
||||
style="fill:#000000;stroke-width:0.264583"
|
||||
id="path512"
|
||||
cx="31.139042"
|
||||
cy="90.831169"
|
||||
r="2.2975652" />
|
||||
<circle
|
||||
style="fill:#ffffff;stroke-width:0.264583"
|
||||
id="path568"
|
||||
cx="26.182495"
|
||||
cy="85.988632"
|
||||
r="1.9699489" />
|
||||
<path
|
||||
style="opacity:0.12;fill:#b3b3b3;fill-opacity:1;stroke-width:0.4"
|
||||
d="M 41.411832,67.937638 C 40.658169,76.919778 37.128974,84.947516 31.223631,90.872512 25.309527,96.806299 16.444692,99.962726 8.3458661,101.14067 6.6432207,97.293039 6.0719726,93.762142 6.0957813,90.369955 6.2452293,83.254278 9.3168733,77.03745 13.816884,72.643399 c 4.500011,-4.394051 10.687755,-6.854807 17.041251,-6.944424 1.579204,-0.02109 6.007668,0.109573 10.553697,2.238663 z"
|
||||
id="path9591"
|
||||
sodipodi:nodetypes="cscczcc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue