Finishing basic part of INPX file parsing
This commit is contained in:
parent
5a79d038eb
commit
ae52c1cd49
6 changed files with 134 additions and 53 deletions
|
@ -6,12 +6,12 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.Transient;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.redrise.marinesco.RainbowDump;
|
||||
import ru.redrise.marinesco.data.AuthorRepository;
|
||||
|
@ -20,11 +20,13 @@ import ru.redrise.marinesco.data.GenreRepository;
|
|||
@Slf4j
|
||||
@Entity
|
||||
@Data
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
|
||||
public class InpEntry {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
|
||||
private Integer id;
|
||||
private Long libraryId;
|
||||
private String libraryVersion;
|
||||
|
||||
@ManyToMany
|
||||
private List<Author> authors; // Surname,name,by-father
|
||||
@ManyToMany
|
||||
|
@ -33,12 +35,13 @@ public class InpEntry {
|
|||
private String series;
|
||||
private String serNo;
|
||||
private String fsFileName; // inside zip
|
||||
private String fileSize; // extracted, in bytes
|
||||
private Long fileSize; // extracted, in bytes
|
||||
private String libId; // same to filename
|
||||
private String deleted; // is deleted flag
|
||||
private String fileExtension; // - concatenate to fsFileName
|
||||
private LocalDate addedDate;
|
||||
private String container;
|
||||
|
||||
|
||||
@Transient
|
||||
private int position = 0;
|
||||
|
@ -48,9 +51,14 @@ public class InpEntry {
|
|||
public InpEntry(byte[] line,
|
||||
String container,
|
||||
AuthorRepository authorRepository,
|
||||
GenreRepository genreRepository) throws Exception {
|
||||
GenreRepository genreRepository,
|
||||
Long libraryId,
|
||||
String libraryVersion) throws Exception {
|
||||
// AUTHOR;GENRE;TITLE;SERIES;SERNO;FILE;SIZE;LIBID;DEL;EXT;DATE;
|
||||
this.line = line;
|
||||
this.libraryId = libraryId;
|
||||
this.libraryVersion = libraryVersion;
|
||||
this.id = new String(line).hashCode();
|
||||
this.container = container + ".zip";
|
||||
this.authors = new ArrayList<>();
|
||||
this.genres = new ArrayList<>();
|
||||
|
@ -60,7 +68,7 @@ public class InpEntry {
|
|||
this.series = parseNextString();
|
||||
this.serNo = parseNextString();
|
||||
this.fsFileName = parseNextString();
|
||||
this.fileSize = parseNextString();
|
||||
this.fileSize = Long.valueOf(parseNextString());
|
||||
this.libId = parseNextString();
|
||||
this.deleted = parseNextString();
|
||||
this.fileExtension = parseNextString();
|
||||
|
|
|
@ -21,10 +21,14 @@ import ru.redrise.marinesco.data.LibraryMetadataRepository;
|
|||
@Slf4j
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "marinesco.library")
|
||||
public class InpxScanner {
|
||||
public class InpxScanner implements Runnable {
|
||||
|
||||
private static volatile Thread parser;
|
||||
private static volatile String lastRunErrors;
|
||||
|
||||
private String filesLocation = "";
|
||||
|
||||
private LibraryMetadata libraryMetadata;
|
||||
private LibraryMetadataRepository libraryMetadataRepository;
|
||||
private AuthorRepository authorRepository;
|
||||
private GenreRepository genreRepository;
|
||||
|
@ -36,54 +40,63 @@ public class InpxScanner {
|
|||
LibraryMetadataRepository libraryMetadataRepository) {
|
||||
this.authorRepository = authorRepository;
|
||||
this.genreRepository = genreRepository;
|
||||
this.inpEntryRepository = inpEntryRepository;
|
||||
this.libraryMetadataRepository = libraryMetadataRepository;
|
||||
}
|
||||
|
||||
public void reScan() throws Exception {
|
||||
LibraryMetadata libraryMetadata = new LibraryMetadata();
|
||||
final FileSystemResource libraryLocation = new FileSystemResource(filesLocation);
|
||||
/*
|
||||
* @return true if executed, false if already running
|
||||
*/
|
||||
public boolean reScan(){
|
||||
if (parser == null || !parser.isAlive()) {
|
||||
parser = new Thread(this);
|
||||
parser.start();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
File inpxFile = Stream.of(libraryLocation.getFile().listFiles())
|
||||
.filter(file -> file.getName().endsWith(".inpx"))
|
||||
.findFirst()
|
||||
.get();
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final FileSystemResource libraryLocation = new FileSystemResource(filesLocation);
|
||||
|
||||
log.info("INPX file found as " + inpxFile.getName());
|
||||
final File inpxFile = Stream.of(libraryLocation.getFile().listFiles())
|
||||
.filter(file -> file.getName().endsWith(".inpx"))
|
||||
.findFirst()
|
||||
.get();
|
||||
|
||||
boolean breaker = false;
|
||||
log.debug("INPX file found as " + inpxFile.getName());
|
||||
|
||||
getLibraryMetadata(inpxFile);
|
||||
parseInp(inpxFile);
|
||||
// Once multiple libraries imlemented, add here 'delete recrodds with old
|
||||
// version of the library'
|
||||
// TODO: fix lirary ID changes on every update: add selector on the front
|
||||
} catch (Exception e) {
|
||||
log.error("{}", e);
|
||||
InpxScanner.lastRunErrors = e.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void getLibraryMetadata(File inpxFile) throws Exception {
|
||||
libraryMetadata = new LibraryMetadata();
|
||||
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inpxFile))) {
|
||||
ZipEntry zipEntry = zipInputStream.getNextEntry();
|
||||
|
||||
while (zipEntry != null) {
|
||||
if (zipEntry.isDirectory()) {
|
||||
zipEntry = zipInputStream.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zipEntry.getName().toLowerCase().contains("collection.info"))
|
||||
libraryMetadata.setCollectionInfo(readPlainText(zipInputStream));
|
||||
|
||||
else if (zipEntry.getName().toLowerCase().contains("version.info"))
|
||||
libraryMetadata.setVersionInfo(readPlainText(zipInputStream));
|
||||
|
||||
else if (zipEntry.getName().toLowerCase().endsWith(".inp")) {
|
||||
//*
|
||||
if (breaker) {
|
||||
zipEntry = zipInputStream.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
breaker = true;//
|
||||
//*/
|
||||
byte[] content = inpToByteArray(zipInputStream, zipEntry.getSize());
|
||||
parseInpContent(content, zipEntry.getName());
|
||||
}
|
||||
|
||||
zipEntry = zipInputStream.getNextEntry();
|
||||
}
|
||||
}
|
||||
|
||||
libraryMetadataRepository.save(libraryMetadata);
|
||||
libraryMetadata = libraryMetadataRepository.save(libraryMetadata);
|
||||
}
|
||||
|
||||
private String readPlainText(ZipInputStream zipInputStream) throws Exception {
|
||||
|
@ -119,6 +132,29 @@ public class InpxScanner {
|
|||
return inpByteBuffer.array();
|
||||
}
|
||||
|
||||
private void parseInp(File inpxFile) throws Exception {
|
||||
boolean breaker = false;
|
||||
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(inpxFile))) {
|
||||
ZipEntry zipEntry = zipInputStream.getNextEntry();
|
||||
|
||||
while (zipEntry != null) {
|
||||
if (zipEntry.getName().toLowerCase().endsWith(".inp")) {
|
||||
// *
|
||||
if (breaker) {
|
||||
zipEntry = zipInputStream.getNextEntry();
|
||||
continue;
|
||||
}
|
||||
breaker = true;//
|
||||
// */
|
||||
byte[] content = inpToByteArray(zipInputStream, zipEntry.getSize());
|
||||
parseInpContent(content, zipEntry.getName());
|
||||
}
|
||||
zipEntry = zipInputStream.getNextEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseInpContent(byte[] content, String name) throws Exception {
|
||||
name = name.substring(0, name.lastIndexOf('.'));
|
||||
|
||||
|
@ -129,10 +165,14 @@ public class InpxScanner {
|
|||
byte[] line = new byte[i - lastIndex];
|
||||
System.arraycopy(content, lastIndex, line, 0, i - lastIndex - 1);
|
||||
|
||||
InpEntry book = new InpEntry(line, name, authorRepository, genreRepository);
|
||||
//inpEntryRepository.save(book);
|
||||
InpEntry book = new InpEntry(line,
|
||||
name,
|
||||
authorRepository,
|
||||
genreRepository,
|
||||
libraryMetadata.getId(),
|
||||
libraryMetadata.getVersion());
|
||||
|
||||
// RainbowDump.hexDumpUTF8(line);
|
||||
inpEntryRepository.save(book);
|
||||
|
||||
if (isNextCarriageReturn(i, content)) {
|
||||
i += 2;
|
||||
|
@ -154,4 +194,8 @@ public class InpxScanner {
|
|||
public void setFilesLocation(String location) {
|
||||
filesLocation = location;
|
||||
}
|
||||
|
||||
public static String getLastRunErrors() {
|
||||
return lastRunErrors;
|
||||
}
|
||||
}
|
||||
|
|
19
src/main/java/ru/redrise/marinesco/library/ParseThread.java
Normal file
19
src/main/java/ru/redrise/marinesco/library/ParseThread.java
Normal file
|
@ -0,0 +1,19 @@
|
|||
package ru.redrise.marinesco.library;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
|
||||
public class ParseThread implements DisposableBean, Runnable{
|
||||
private static volatile boolean running;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
running = true;
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(){
|
||||
running = false;
|
||||
}
|
||||
}
|
|
@ -9,10 +9,8 @@ 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
|
||||
@Controller
|
||||
@RequestMapping("/settings")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
|
@ -28,7 +26,7 @@ public class SettingsController {
|
|||
}
|
||||
|
||||
@GetMapping
|
||||
public String getPage(@ModelAttribute("rescanError") String error) {
|
||||
public String getPage(@ModelAttribute("rescanError") String err, @ModelAttribute("rescanOk") String passStatus) {
|
||||
return "settings";
|
||||
}
|
||||
|
||||
|
@ -36,6 +34,13 @@ public class SettingsController {
|
|||
public Boolean setRegistrationSetting() {
|
||||
return applicationSettings.isRegistrationAllowed();
|
||||
}
|
||||
|
||||
@ModelAttribute(name = "lastScanErrors")
|
||||
public String setLastRunErrors(){
|
||||
if (InpxScanner.getLastRunErrors() != null)
|
||||
return "Last run attempt failed: "+InpxScanner.getLastRunErrors();
|
||||
return null;
|
||||
}
|
||||
|
||||
@GetMapping("/allow_registration/{sw}")
|
||||
public String switchRegistration(@PathVariable("sw") Boolean sw) {
|
||||
|
@ -47,13 +52,11 @@ public class SettingsController {
|
|||
@GetMapping("/rescan")
|
||||
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()+"'?");
|
||||
}
|
||||
|
||||
if (inpxScanner.reScan())
|
||||
redirectAttributes.addAttribute("rescanOk", "Rescan started");
|
||||
else
|
||||
redirectAttributes.addAttribute("rescanError", "Rescan is currently in progress");
|
||||
|
||||
return redirectView;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ body {
|
|||
color: red;
|
||||
}
|
||||
|
||||
.validationPass {
|
||||
color: #00b185;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
text-align: left;
|
||||
position: relative;
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
th:text="${'New users registration is now ' + (allowRegistration ? 'enabled. ' : 'disabled. ' )}"></span>
|
||||
<a th:href="${'/settings/allow_registration/' + !allowRegistration }"
|
||||
th:text="${'Click here to ' + (allowRegistration ? 'disable' : 'enable' )}"></a>
|
||||
<p>
|
||||
<span class="validationError" th:if="${rescanError} != null"
|
||||
th:text="${rescanError}"></span>
|
||||
<br />
|
||||
<p>
|
||||
|
||||
<span class="validationError" th:if="${rescanError} != null" th:text="${rescanError}"></span>
|
||||
<span class="validationPass" th:if="${rescanOk} != null" th:text="${rescanOk}"></span>
|
||||
<br />
|
||||
<a href="/settings/rescan" >Click to rescan library</a>
|
||||
<br />
|
||||
<span class="validationError" th:if="${lastScanErrors} != ''" th:text="${lastScanErrors}"></span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue