Compare commits
No commits in common. "1791fd909d6cc3493207f4bb582792c6e877e878" and "ae52c1cd492fd3aaa5f577406fc788e54debac70" have entirely different histories.
1791fd909d
...
ae52c1cd49
11 changed files with 17 additions and 248 deletions
|
@ -1,31 +0,0 @@
|
||||||
package ru.redrise.marinesco;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.ui.Model;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
|
|
||||||
import ru.redrise.marinesco.data.InpEntryRepository;
|
|
||||||
import ru.redrise.marinesco.library.InpEntry;
|
|
||||||
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/book")
|
|
||||||
public class BookController {
|
|
||||||
InpEntryRepository inpEntryRepository;
|
|
||||||
|
|
||||||
public BookController(InpEntryRepository inpEntryRepository){
|
|
||||||
this.inpEntryRepository = inpEntryRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{bookId}")
|
|
||||||
public String getPage(@PathVariable("bookId") Long bookId, Model model) {
|
|
||||||
InpEntry book = inpEntryRepository.findById(bookId).orElse(null);
|
|
||||||
if (book == null){
|
|
||||||
model.addAttribute("Error", "Not found");
|
|
||||||
return "book";
|
|
||||||
}
|
|
||||||
model.addAttribute("book", book);
|
|
||||||
return "book";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package ru.redrise.marinesco;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
|
|
||||||
import org.springframework.core.io.FileSystemResource;
|
|
||||||
import org.springframework.http.ContentDisposition;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.stereotype.Controller;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
|
|
||||||
import jakarta.servlet.ServletOutputStream;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import ru.redrise.marinesco.settings.ApplicationSettings;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Controller
|
|
||||||
@RequestMapping("/download")
|
|
||||||
public class DownloadController {
|
|
||||||
|
|
||||||
private String filesLocation;
|
|
||||||
|
|
||||||
public DownloadController(ApplicationSettings applicationSettings) {
|
|
||||||
this.filesLocation = applicationSettings.getFilesLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping(value = "/")
|
|
||||||
public void getMethodName(@RequestParam String container,
|
|
||||||
@RequestParam String file,
|
|
||||||
HttpServletResponse response) throws Exception {
|
|
||||||
|
|
||||||
final FileSystemResource libraryLocation = new FileSystemResource(filesLocation + File.separator + container);
|
|
||||||
try (ZipInputStream zipInputStream = new ZipInputStream(libraryLocation.getInputStream())) {
|
|
||||||
ZipEntry zipEntry = zipInputStream.getNextEntry();
|
|
||||||
|
|
||||||
while (zipEntry != null) {
|
|
||||||
if (zipEntry.getName().contains(file)) {
|
|
||||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
|
||||||
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
|
|
||||||
.filename(file+".fb2", StandardCharsets.UTF_8) //TODO: fix
|
|
||||||
.build()
|
|
||||||
.toString());
|
|
||||||
ServletOutputStream outStream = response.getOutputStream();
|
|
||||||
sendFile(zipEntry.getSize(), zipInputStream, outStream);
|
|
||||||
outStream.flush();
|
|
||||||
outStream.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
zipEntry = zipInputStream.getNextEntry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new Exception("file not found " +
|
|
||||||
filesLocation + File.separator + container + " → " + file);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendFile(long fileSize,
|
|
||||||
ZipInputStream zipInputStream,
|
|
||||||
ServletOutputStream outStream) throws Exception {
|
|
||||||
|
|
||||||
int blockSize = 0x200;
|
|
||||||
|
|
||||||
if (fileSize < 0x200)
|
|
||||||
blockSize = (int) fileSize;
|
|
||||||
|
|
||||||
byte[] block = new byte[blockSize];
|
|
||||||
long i = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
int actuallyRead = zipInputStream.read(block);
|
|
||||||
outStream.write(block, 0, actuallyRead);
|
|
||||||
i += actuallyRead;
|
|
||||||
if ((i + blockSize) > fileSize) {
|
|
||||||
blockSize = (int) (fileSize - i);
|
|
||||||
if (blockSize == 0)
|
|
||||||
break;
|
|
||||||
block = new byte[blockSize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -2,20 +2,12 @@ package ru.redrise.marinesco;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/")
|
|
||||||
public class RootController {
|
public class RootController {
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping("/")
|
||||||
public String getPage(@ModelAttribute("search") String text) {
|
public String home(){
|
||||||
// TODO: SEARCH PAGE + CONTROLLER
|
|
||||||
log.info(text);
|
|
||||||
return "root";
|
return "root";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
package ru.redrise.marinesco.library;
|
|
||||||
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import ru.redrise.marinesco.data.AuthorRepository;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class AthorByIdConverter implements Converter<Long, Author>{
|
|
||||||
|
|
||||||
private AuthorRepository authorRepository;
|
|
||||||
|
|
||||||
public AthorByIdConverter(AuthorRepository authorRepository){
|
|
||||||
this.authorRepository = authorRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Author convert(Long id) {
|
|
||||||
return authorRepository.findById(id).orElse(null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package ru.redrise.marinesco.library;
|
|
||||||
|
|
||||||
import org.springframework.core.convert.converter.Converter;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import ru.redrise.marinesco.data.GenreRepository;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class GenreByIdConverter implements Converter<String, Genre>{
|
|
||||||
|
|
||||||
private GenreRepository genreRepository;
|
|
||||||
|
|
||||||
public GenreByIdConverter(GenreRepository genreRepository){
|
|
||||||
this.genreRepository = genreRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Genre convert(String id) {
|
|
||||||
return genreRepository.findById(id).orElse(null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,29 +17,27 @@ import ru.redrise.marinesco.data.AuthorRepository;
|
||||||
import ru.redrise.marinesco.data.GenreRepository;
|
import ru.redrise.marinesco.data.GenreRepository;
|
||||||
import ru.redrise.marinesco.data.InpEntryRepository;
|
import ru.redrise.marinesco.data.InpEntryRepository;
|
||||||
import ru.redrise.marinesco.data.LibraryMetadataRepository;
|
import ru.redrise.marinesco.data.LibraryMetadataRepository;
|
||||||
import ru.redrise.marinesco.settings.ApplicationSettings;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "marinesco.library")
|
||||||
public class InpxScanner implements Runnable {
|
public class InpxScanner implements Runnable {
|
||||||
|
|
||||||
private static volatile Thread parser;
|
private static volatile Thread parser;
|
||||||
private static volatile String lastRunErrors;
|
private static volatile String lastRunErrors;
|
||||||
|
|
||||||
|
private String filesLocation = "";
|
||||||
|
|
||||||
private LibraryMetadata libraryMetadata;
|
private LibraryMetadata libraryMetadata;
|
||||||
private LibraryMetadataRepository libraryMetadataRepository;
|
private LibraryMetadataRepository libraryMetadataRepository;
|
||||||
private AuthorRepository authorRepository;
|
private AuthorRepository authorRepository;
|
||||||
private GenreRepository genreRepository;
|
private GenreRepository genreRepository;
|
||||||
private InpEntryRepository inpEntryRepository;
|
private InpEntryRepository inpEntryRepository;
|
||||||
|
|
||||||
private String filesLocation;
|
public InpxScanner(AuthorRepository authorRepository,
|
||||||
|
|
||||||
public InpxScanner(ApplicationSettings applicationSettings,
|
|
||||||
AuthorRepository authorRepository,
|
|
||||||
GenreRepository genreRepository,
|
GenreRepository genreRepository,
|
||||||
InpEntryRepository inpEntryRepository,
|
InpEntryRepository inpEntryRepository,
|
||||||
LibraryMetadataRepository libraryMetadataRepository) {
|
LibraryMetadataRepository libraryMetadataRepository) {
|
||||||
this.filesLocation = applicationSettings.getFilesLocation();
|
|
||||||
this.authorRepository = authorRepository;
|
this.authorRepository = authorRepository;
|
||||||
this.genreRepository = genreRepository;
|
this.genreRepository = genreRepository;
|
||||||
this.inpEntryRepository = inpEntryRepository;
|
this.inpEntryRepository = inpEntryRepository;
|
||||||
|
@ -49,7 +47,7 @@ public class InpxScanner implements Runnable {
|
||||||
/*
|
/*
|
||||||
* @return true if executed, false if already running
|
* @return true if executed, false if already running
|
||||||
*/
|
*/
|
||||||
public boolean reScan() {
|
public boolean reScan(){
|
||||||
if (parser == null || !parser.isAlive()) {
|
if (parser == null || !parser.isAlive()) {
|
||||||
parser = new Thread(this);
|
parser = new Thread(this);
|
||||||
parser.start();
|
parser.start();
|
||||||
|
@ -135,9 +133,6 @@ public class InpxScanner implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseInp(File inpxFile) throws Exception {
|
private void parseInp(File inpxFile) throws Exception {
|
||||||
log.warn("REMOVE TEMPORARY SOLUTION - BREAKER");
|
|
||||||
log.warn("REMOVE TEMPORARY SOLUTION - BREAKER");
|
|
||||||
log.warn("REMOVE TEMPORARY SOLUTION - BREAKER");
|
|
||||||
boolean breaker = false;
|
boolean breaker = false;
|
||||||
|
|
||||||
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inpxFile))) {
|
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inpxFile))) {
|
||||||
|
@ -192,6 +187,14 @@ public class InpxScanner implements Runnable {
|
||||||
return i + 1 < content.length && (content[i + 1] == '\r');
|
return i + 1 < content.length && (content[i + 1] == '\r');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFilesLocation() {
|
||||||
|
return filesLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilesLocation(String location) {
|
||||||
|
filesLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getLastRunErrors() {
|
public static String getLastRunErrors() {
|
||||||
return lastRunErrors;
|
return lastRunErrors;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
package ru.redrise.marinesco.settings;
|
package ru.redrise.marinesco.settings;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties(prefix = "marinesco.library")
|
|
||||||
public class ApplicationSettings {
|
public class ApplicationSettings {
|
||||||
private static final String ALLOW_REGISTRATION = "allow_registration";
|
private static final String ALLOW_REGISTRATION = "allow_registration";
|
||||||
|
|
||||||
private String filesLocation = "";
|
|
||||||
|
|
||||||
private KeyValueRepository keyValueRepository;
|
private KeyValueRepository keyValueRepository;
|
||||||
|
|
||||||
|
@ -38,13 +34,4 @@ public class ApplicationSettings {
|
||||||
public synchronized boolean isRegistrationAllowed() {
|
public synchronized boolean isRegistrationAllowed() {
|
||||||
return registrationAllowed;
|
return registrationAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilesLocation() {
|
|
||||||
return filesLocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFilesLocation(String location) {
|
|
||||||
filesLocation = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,6 @@
|
||||||
src: url('/styles/Arimo-VariableFont_wght.ttf');
|
src: url('/styles/Arimo-VariableFont_wght.ttf');
|
||||||
}
|
}
|
||||||
|
|
||||||
html{
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Arimo;
|
font-family: Arimo;
|
||||||
background-color: #212121;
|
background-color: #212121;
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Marinesco</title>
|
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
|
||||||
<link rel="alternate icon" href="/favicon.png" type="image/png">
|
|
||||||
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="page">
|
|
||||||
<div th:replace="~{fragments/header :: 'header'}"></div>
|
|
||||||
<div class="container base">
|
|
||||||
<span class="validationError" th:if="${Error} != null" th:text="${Error}"></span>
|
|
||||||
<div th:if="${book} != null">
|
|
||||||
<br /><span th:text="${'Title: ' + book.title}"></span>
|
|
||||||
<br /><span>Authors: </span>
|
|
||||||
<div th:each="author : ${book.authors}">
|
|
||||||
<span th:text="${' * ' + author.authorName}"></span>
|
|
||||||
</div>
|
|
||||||
<br /><span>Genres: </span>
|
|
||||||
<div th:each="genre : ${book.genres}">
|
|
||||||
<span th:text="${' * ' + genre.genreId + ' — ' + genre.humanReadableDescription}"></span>
|
|
||||||
</div>
|
|
||||||
<br /><span th:if="${book.series} != null" th:text="${'Series: ' + book.series}"></span>
|
|
||||||
<br /><span th:if="${book.serNo} != null" th:text="${'Series # : ' + book.serNo}"></span>
|
|
||||||
<br /><span th:text="${'Format: ' + book.fileExtension}"></span>
|
|
||||||
<br /><span th:if="${book.addedDate} != null" th:text="${'Added : ' + book.addedDate}"></span>
|
|
||||||
<br /><span th:text="${'Size: ' + book.fileSize + ' bytes'}"></span>
|
|
||||||
<p>
|
|
||||||
<a th:href="${'/download/?container=' + book.container + '&file=' + book.fsFileName}" th:text="Download"></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div th:replace="~{fragments/footer :: 'footer'}"></div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -9,7 +9,7 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<footer class="inner_footer">
|
<footer class="inner_footer">
|
||||||
<a class="entry" href="https://redrise.ru">
|
<a class="entry" href="https://redrise.ru">
|
||||||
<div class="copy_link">© 2023-2024 Dmitry Isaenko</div>
|
<div class="copy_link">© 2023 Dmitry Isaenko</div>
|
||||||
</a>
|
</a>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Marinesco</title>
|
<title>Marinesco</title>
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||||
<link rel="alternate icon" href="/favicon.png" type="image/png">
|
<link rel="alternate icon" href="/favicon.png" type="image/png">
|
||||||
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -16,14 +16,7 @@
|
||||||
<br /><a href="/profile">/profile</a>
|
<br /><a href="/profile">/profile</a>
|
||||||
<br /><a href="/manage_users">/manage_users</a>
|
<br /><a href="/manage_users">/manage_users</a>
|
||||||
<br /><a href="/settings">/settings</a>
|
<br /><a href="/settings">/settings</a>
|
||||||
<br /><a href="/book/59992766">/book/59992766</a>
|
|
||||||
<br /><a href="/h2">H2</a>
|
<br /><a href="/h2">H2</a>
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
<form action='' method='get'>
|
|
||||||
<input type='text' name='search'>
|
|
||||||
<button class="sign" type='submit'>Search</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div th:replace="~{fragments/footer :: 'footer'}"></div>
|
<div th:replace="~{fragments/footer :: 'footer'}"></div>
|
||||||
|
|
Loading…
Reference in a new issue