diff --git a/src/main/java/ru/redrise/marinesco/library/InpEntry.java b/src/main/java/ru/redrise/marinesco/library/InpEntry.java new file mode 100644 index 0000000..440275d --- /dev/null +++ b/src/main/java/ru/redrise/marinesco/library/InpEntry.java @@ -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 authors = new ArrayList<>(); // Surname,name,by-father + private List 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)"); + } +} diff --git a/src/main/java/ru/redrise/marinesco/library/InpFile.java b/src/main/java/ru/redrise/marinesco/library/InpFile.java new file mode 100644 index 0000000..af5415b --- /dev/null +++ b/src/main/java/ru/redrise/marinesco/library/InpFile.java @@ -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 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'); + } +} diff --git a/src/main/java/ru/redrise/marinesco/library/InpxScanner.java b/src/main/java/ru/redrise/marinesco/library/InpxScanner.java index 46587f2..9703c8f 100644 --- a/src/main/java/ru/redrise/marinesco/library/InpxScanner.java +++ b/src/main/java/ru/redrise/marinesco/library/InpxScanner.java @@ -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; } } diff --git a/src/main/java/ru/redrise/marinesco/library/LibraryMetadata.java b/src/main/java/ru/redrise/marinesco/library/LibraryMetadata.java new file mode 100644 index 0000000..78d145d --- /dev/null +++ b/src/main/java/ru/redrise/marinesco/library/LibraryMetadata.java @@ -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; +} diff --git a/src/main/java/ru/redrise/marinesco/settings/SettingsController.java b/src/main/java/ru/redrise/marinesco/settings/SettingsController.java index cacc1c8..02b83f0 100644 --- a/src/main/java/ru/redrise/marinesco/settings/SettingsController.java +++ b/src/main/java/ru/redrise/marinesco/settings/SettingsController.java @@ -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; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4e53fab..9973be0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -21,6 +21,10 @@ spring: settings: web-allow-others: true trace: false +logging: + level: + org: + springframework: INFO marinesco: library: filesLocation: "./lib" \ No newline at end of file diff --git a/src/main/resources/templates/settings.html b/src/main/resources/templates/settings.html index e581f47..18369db 100644 --- a/src/main/resources/templates/settings.html +++ b/src/main/resources/templates/settings.html @@ -17,7 +17,10 @@

- + +
+ Click to rescan library