Add drafts to parse inp+inpx structures

master
Dmitry Isaenko 2024-01-06 23:33:16 +03:00
parent 7f66f20b53
commit 9b41f9c3c0
7 changed files with 283 additions and 26 deletions

View 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)");
}
}

View 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');
}
}

View File

@ -1,6 +1,12 @@
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.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.FileSystemResource;
@ -15,21 +21,98 @@ public class InpxScanner {
private String filesLocation = "";
private FileSystemResource inpxFile;
private FileSystemResource libraryLocation;
public void reScan() {
inpxFile = new FileSystemResource(filesLocation);
private LibraryMetadata collectionFileMeta;
private LibraryMetadata versionFileMeta;
//todo
Stream.of(inpxFile.getFile().listFiles())
.forEach(file -> log.info(file.toString()));
public void reScan() throws Exception {
libraryLocation = new FileSystemResource(filesLocation);
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;
}
public void setFilesLocation(String location){
public void setFilesLocation(String location) {
filesLocation = location;
}
}

View File

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

View File

@ -6,49 +6,55 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
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;
//@Slf4j
@Slf4j
@Controller
@RequestMapping("/settings")
@PreAuthorize("hasRole('ADMIN')")
public class SettingsController {
private KeyValueRepository keyValueRepository;
private ApplicationSettings applicationSettings;
private InpxScanner inpxScanner;
public SettingsController(KeyValueRepository keyValueRepository, ApplicationSettings applicationSettings, InpxScanner inpxScanner){
this.keyValueRepository = keyValueRepository;
public SettingsController(ApplicationSettings applicationSettings,
InpxScanner inpxScanner) {
this.applicationSettings = applicationSettings;
this.inpxScanner = inpxScanner;
}
@GetMapping
public String getPage() {
public String getPage(@ModelAttribute("rescanError") String error) {
return "settings";
}
@ModelAttribute(name = "allowRegistration")
public Boolean setRegistrationSetting(){
public Boolean setRegistrationSetting() {
return applicationSettings.isRegistrationAllowed();
//return keyValueRepository.findById(ApplicationSettings.ALLOW_REGISTRATION).get().getM_value();
}
@GetMapping("/allow_registration/{sw}")
public String switchRegistration(@PathVariable("sw") Boolean sw){
//log.info("{}", sw);
//keyValueRepository.save(new KeyValue(ApplicationSettings.ALLOW_REGISTRATION, sw));
public String switchRegistration(@PathVariable("sw") Boolean sw) {
applicationSettings.setAllowRegistraion(sw);
return "redirect:/settings";
}
@GetMapping("/rescan")
public String rescan(){
inpxScanner.reScan();
return "redirect:/settings";
public RedirectView rescan(RedirectAttributes redirectAttributes) {
final RedirectView redirectView = new RedirectView("/settings", true);
try {
inpxScanner.reScan();
} catch (Exception e) {
e.printStackTrace();
redirectAttributes.addAttribute("rescanError",
"Rescan failed. No '.inpx' file at '"+inpxScanner.getFilesLocation()+"'?");
}
return redirectView;
}
}

View File

@ -21,6 +21,10 @@ spring:
settings:
web-allow-others: true
trace: false
logging:
level:
org:
springframework: INFO
marinesco:
library:
filesLocation: "./lib"

View File

@ -17,7 +17,10 @@
<a th:href="${'/settings/allow_registration/' + !allowRegistration }"
th:text="${'Click here to ' + (allowRegistration ? 'disable' : 'enable' )}"></a>
<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>
</div>
</div>