Move Help servlets to common module and embed it into the webinterface

This commit is contained in:
0xb00bface 2023-11-12 13:48:31 +01:00
parent fe7b263d2e
commit 35592e2f48
27 changed files with 1097 additions and 814 deletions

View File

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

View File

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

View File

@ -1,3 +0,0 @@
<h1>400 Bad Request</h1>
<p>{message}</p>
<a href="/docs/index.md">Try something else!</a>

View File

@ -1,2 +0,0 @@
<h1>404 File Not Found</h1>
<a href="/docs/index.md">Try something else!</a>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,3 @@
<h1>400 Bad Request</h1>
<p>{message}</p>
<a href="@{/docs/index.md}">Try something else!</a>

View File

@ -0,0 +1,2 @@
<h1>404 File Not Found</h1>
<a href="@{/docs/index.md}">Try something else!</a>

View File

@ -0,0 +1,3 @@
<h1>500 Internal Server Error</h1>
<p>{message}</p>
<a href="@{/docs/index.md}">Try something else!</a>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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