Add drafts to parse inp+inpx structures
This commit is contained in:
parent
7f66f20b53
commit
9b41f9c3c0
7 changed files with 283 additions and 26 deletions
101
src/main/java/ru/redrise/marinesco/library/InpEntry.java
Normal file
101
src/main/java/ru/redrise/marinesco/library/InpEntry.java
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package ru.redrise.marinesco.library;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Data
|
||||||
|
public class InpEntry {
|
||||||
|
private List<String> authors = new ArrayList<>(); // Surname,name,by-father
|
||||||
|
private List<String> generes = new ArrayList<>();
|
||||||
|
private String title;
|
||||||
|
private String series;
|
||||||
|
private String serNo;
|
||||||
|
private String fsFileName;
|
||||||
|
private String fileSize; // extracted
|
||||||
|
private String libId; // same to filename
|
||||||
|
private String deleted; // is deleted flag
|
||||||
|
private String fileExtension; // - concatenate to fsFileName
|
||||||
|
private LocalDate addedDate;
|
||||||
|
|
||||||
|
private int position;
|
||||||
|
|
||||||
|
public InpEntry(byte[] line) throws Exception{
|
||||||
|
// AUTHOR;GENRE;TITLE;SERIES;SERNO;FILE;SIZE;LIBID;DEL;EXT;DATE;
|
||||||
|
parseAuthors(line);
|
||||||
|
parseGenere(line);
|
||||||
|
this.title = parseNextString(line);
|
||||||
|
this.series = parseNextString(line);
|
||||||
|
this.serNo = parseNextString(line);
|
||||||
|
this.fsFileName = parseNextString(line);
|
||||||
|
this.fileSize = parseNextString(line);
|
||||||
|
this.libId = parseNextString(line);
|
||||||
|
this.deleted = parseNextString(line);
|
||||||
|
this.fileExtension = parseNextString(line);
|
||||||
|
this.addedDate = LocalDate.parse(parseNextString(line));
|
||||||
|
|
||||||
|
for (String author : authors)
|
||||||
|
log.info(author);
|
||||||
|
for (String gen : generes)
|
||||||
|
log.info(gen);
|
||||||
|
|
||||||
|
log.info(title);
|
||||||
|
log.info(series);
|
||||||
|
log.info(serNo);
|
||||||
|
log.info(fsFileName);
|
||||||
|
log.info(fileSize);
|
||||||
|
log.info(libId);
|
||||||
|
log.info(deleted);
|
||||||
|
log.info(fileExtension);
|
||||||
|
log.info(addedDate.toString());
|
||||||
|
|
||||||
|
log.info("-----------------");
|
||||||
|
}
|
||||||
|
private void parseAuthors(byte[] line) throws Exception{
|
||||||
|
for (int i = 0; i < line.length; i++){
|
||||||
|
if (line[i] == ':' && i+1 < line.length && line[i+1] == 0x04){
|
||||||
|
splitAuthors(new String(line, 0, i, StandardCharsets.UTF_8));
|
||||||
|
position = i+1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Invalid 'inp' file format (parse Authors)");
|
||||||
|
}
|
||||||
|
private void splitAuthors(String allAuthors){
|
||||||
|
authors = Arrays.asList(allAuthors.split(":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseGenere(byte[] line) throws Exception{
|
||||||
|
for (int i = position; i < line.length; i++){
|
||||||
|
if (line[i] == ':'){
|
||||||
|
if (line[i] == ':' && i+1 < line.length && line[i+1] == 0x04){
|
||||||
|
generes.add(new String(line, position, i-position, StandardCharsets.UTF_8));
|
||||||
|
position = i+2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
generes.add(new String(line, position, i-position, StandardCharsets.UTF_8));
|
||||||
|
position = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception("Invalid 'inp' file format (parse Genere)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String parseNextString(byte[] line) throws Exception{
|
||||||
|
for (int i = position; i < line.length; i++){
|
||||||
|
if (line[i] == 0x04){
|
||||||
|
String resultingString = new String(line, position, i-position, StandardCharsets.UTF_8);
|
||||||
|
position = i+1;
|
||||||
|
return resultingString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("Invalid 'inp' file format (parse Title)");
|
||||||
|
}
|
||||||
|
}
|
45
src/main/java/ru/redrise/marinesco/library/InpFile.java
Normal file
45
src/main/java/ru/redrise/marinesco/library/InpFile.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package ru.redrise.marinesco.library;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
//@Entity
|
||||||
|
@Data
|
||||||
|
public class InpFile {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final List<InpEntry> inpEntries;
|
||||||
|
|
||||||
|
public InpFile(byte[] content, String name) throws Exception{
|
||||||
|
this.name = name.substring(0, name.lastIndexOf('.'));
|
||||||
|
this.inpEntries = new ArrayList<>();
|
||||||
|
log.info("FILE RELATED "+this.name);
|
||||||
|
parseContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseContent(byte[] content) throws Exception{
|
||||||
|
int lastIndex = 0;
|
||||||
|
for (int i = 0; i < content.length; i++){
|
||||||
|
if (content[i] == '\n'){
|
||||||
|
byte[] line = new byte[i-lastIndex];
|
||||||
|
System.arraycopy(content, lastIndex, line, 0, i-lastIndex-1);
|
||||||
|
inpEntries.add(new InpEntry(line));
|
||||||
|
//RainbowDump.hexDumpUTF8(line);
|
||||||
|
|
||||||
|
if (isNextCarriageReturn(i, content)){
|
||||||
|
i += 2;
|
||||||
|
lastIndex = i;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lastIndex = ++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private boolean isNextCarriageReturn(int i, byte[] content) {
|
||||||
|
return i + 1 < content.length && (content[i + 1] == '\r');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
package ru.redrise.marinesco.library;
|
package ru.redrise.marinesco.library;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
@ -15,21 +21,98 @@ public class InpxScanner {
|
||||||
|
|
||||||
private String filesLocation = "";
|
private String filesLocation = "";
|
||||||
|
|
||||||
private FileSystemResource inpxFile;
|
private FileSystemResource libraryLocation;
|
||||||
|
|
||||||
public void reScan() {
|
private LibraryMetadata collectionFileMeta;
|
||||||
inpxFile = new FileSystemResource(filesLocation);
|
private LibraryMetadata versionFileMeta;
|
||||||
|
|
||||||
//todo
|
public void reScan() throws Exception {
|
||||||
Stream.of(inpxFile.getFile().listFiles())
|
libraryLocation = new FileSystemResource(filesLocation);
|
||||||
.forEach(file -> log.info(file.toString()));
|
|
||||||
|
File inpxFile = Stream.of(libraryLocation.getFile().listFiles())
|
||||||
|
.filter(file -> file.getName().endsWith(".inpx"))
|
||||||
|
.findFirst()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
log.info("INPX file found as " + inpxFile.getName());
|
||||||
|
|
||||||
|
boolean breaker = false;
|
||||||
|
|
||||||
|
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inpxFile))) {
|
||||||
|
ZipEntry zipEntry = zipInputStream.getNextEntry();
|
||||||
|
while (zipEntry != null) {
|
||||||
|
//log.info("Now parsing: " + zipEntry.getName());
|
||||||
|
if (zipEntry.isDirectory()) {
|
||||||
|
zipEntry = zipInputStream.getNextEntry();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* Lines:
|
||||||
|
* 1 - Collection Display Name
|
||||||
|
* 2 - Collection file name
|
||||||
|
* 3 - num: 0 - fb2, 1 - non-FB2
|
||||||
|
* 4 - description
|
||||||
|
*/
|
||||||
|
if (zipEntry.getName().toLowerCase().equals("collection.info"))
|
||||||
|
setMetadata("collection.info", zipInputStream);
|
||||||
|
|
||||||
|
// version.info contains only 1 string
|
||||||
|
if (zipEntry.getName().toLowerCase().equals("version.info"))
|
||||||
|
setMetadata("version.info", zipInputStream);
|
||||||
|
|
||||||
|
if (zipEntry.getName().toLowerCase().endsWith(".inp")){
|
||||||
|
if (breaker)
|
||||||
|
break;
|
||||||
|
breaker = true;
|
||||||
|
parseInp(zipInputStream, zipEntry.getSize(), zipEntry.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
zipEntry = zipInputStream.getNextEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFilesLocation(){
|
private void setMetadata(String filename, ZipInputStream zipInputStream) throws Exception {
|
||||||
|
collectionFileMeta = new LibraryMetadata(filename, readPlainText(zipInputStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readPlainText(ZipInputStream zipInputStream) throws Exception {
|
||||||
|
byte[] content = new byte[1024];
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
while (zipInputStream.read(content) > 0)
|
||||||
|
stringBuilder.append(new String(content, StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseInp(ZipInputStream stream, long fileSize, String fileName) throws Exception {
|
||||||
|
ByteBuffer inpByteBuffer = ByteBuffer.allocate((int) fileSize);
|
||||||
|
int blockSize = 0x200;
|
||||||
|
if (fileSize < 0x200)
|
||||||
|
blockSize = (int) fileSize;
|
||||||
|
|
||||||
|
long i = 0;
|
||||||
|
byte[] block = new byte[blockSize];
|
||||||
|
|
||||||
|
int actuallyRead;
|
||||||
|
while (true) {
|
||||||
|
actuallyRead = stream.read(block);
|
||||||
|
inpByteBuffer.put(block, 0, actuallyRead);
|
||||||
|
i += actuallyRead;
|
||||||
|
if ((i + blockSize) > fileSize) {
|
||||||
|
blockSize = (int) (fileSize - i);
|
||||||
|
if (blockSize == 0)
|
||||||
|
break;
|
||||||
|
block = new byte[blockSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new InpFile(inpByteBuffer.array(), fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilesLocation() {
|
||||||
return filesLocation;
|
return filesLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFilesLocation(String location){
|
public void setFilesLocation(String location) {
|
||||||
filesLocation = location;
|
filesLocation = location;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ru.redrise.marinesco.library;
|
||||||
|
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LibraryMetadata {
|
||||||
|
@Id
|
||||||
|
private String filename;
|
||||||
|
private String content;
|
||||||
|
}
|
|
@ -6,49 +6,55 @@ import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
import org.springframework.web.servlet.view.RedirectView;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import ru.redrise.marinesco.library.InpxScanner;
|
import ru.redrise.marinesco.library.InpxScanner;
|
||||||
|
|
||||||
//@Slf4j
|
@Slf4j
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/settings")
|
@RequestMapping("/settings")
|
||||||
@PreAuthorize("hasRole('ADMIN')")
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
public class SettingsController {
|
public class SettingsController {
|
||||||
private KeyValueRepository keyValueRepository;
|
|
||||||
private ApplicationSettings applicationSettings;
|
private ApplicationSettings applicationSettings;
|
||||||
|
|
||||||
private InpxScanner inpxScanner;
|
private InpxScanner inpxScanner;
|
||||||
|
|
||||||
public SettingsController(KeyValueRepository keyValueRepository, ApplicationSettings applicationSettings, InpxScanner inpxScanner){
|
public SettingsController(ApplicationSettings applicationSettings,
|
||||||
this.keyValueRepository = keyValueRepository;
|
InpxScanner inpxScanner) {
|
||||||
this.applicationSettings = applicationSettings;
|
this.applicationSettings = applicationSettings;
|
||||||
this.inpxScanner = inpxScanner;
|
this.inpxScanner = inpxScanner;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public String getPage() {
|
public String getPage(@ModelAttribute("rescanError") String error) {
|
||||||
|
|
||||||
return "settings";
|
return "settings";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute(name = "allowRegistration")
|
@ModelAttribute(name = "allowRegistration")
|
||||||
public Boolean setRegistrationSetting(){
|
public Boolean setRegistrationSetting() {
|
||||||
return applicationSettings.isRegistrationAllowed();
|
return applicationSettings.isRegistrationAllowed();
|
||||||
//return keyValueRepository.findById(ApplicationSettings.ALLOW_REGISTRATION).get().getM_value();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/allow_registration/{sw}")
|
@GetMapping("/allow_registration/{sw}")
|
||||||
public String switchRegistration(@PathVariable("sw") Boolean sw){
|
public String switchRegistration(@PathVariable("sw") Boolean sw) {
|
||||||
//log.info("{}", sw);
|
|
||||||
//keyValueRepository.save(new KeyValue(ApplicationSettings.ALLOW_REGISTRATION, sw));
|
|
||||||
applicationSettings.setAllowRegistraion(sw);
|
applicationSettings.setAllowRegistraion(sw);
|
||||||
|
|
||||||
return "redirect:/settings";
|
return "redirect:/settings";
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/rescan")
|
@GetMapping("/rescan")
|
||||||
public String rescan(){
|
public RedirectView rescan(RedirectAttributes redirectAttributes) {
|
||||||
inpxScanner.reScan();
|
final RedirectView redirectView = new RedirectView("/settings", true);
|
||||||
|
try {
|
||||||
return "redirect:/settings";
|
inpxScanner.reScan();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
redirectAttributes.addAttribute("rescanError",
|
||||||
|
"Rescan failed. No '.inpx' file at '"+inpxScanner.getFilesLocation()+"'?");
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirectView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,10 @@ spring:
|
||||||
settings:
|
settings:
|
||||||
web-allow-others: true
|
web-allow-others: true
|
||||||
trace: false
|
trace: false
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
org:
|
||||||
|
springframework: INFO
|
||||||
marinesco:
|
marinesco:
|
||||||
library:
|
library:
|
||||||
filesLocation: "./lib"
|
filesLocation: "./lib"
|
|
@ -17,7 +17,10 @@
|
||||||
<a th:href="${'/settings/allow_registration/' + !allowRegistration }"
|
<a th:href="${'/settings/allow_registration/' + !allowRegistration }"
|
||||||
th:text="${'Click here to ' + (allowRegistration ? 'disable' : 'enable' )}"></a>
|
th:text="${'Click here to ' + (allowRegistration ? 'disable' : 'enable' )}"></a>
|
||||||
<p>
|
<p>
|
||||||
<a href="/settings/rescan" th:text="Click to rescan library"></a>
|
<span class="validationError" th:if="${rescanError} != null"
|
||||||
|
th:text="${rescanError}"></span>
|
||||||
|
<br />
|
||||||
|
<a href="/settings/rescan" >Click to rescan library</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue