From f5e2f86803fd6c59cf16fa9ffd585a007ca475df Mon Sep 17 00:00:00 2001 From: lanthale Date: Sun, 19 Nov 2023 12:17:26 +0100 Subject: [PATCH] Working cache now. Open topic: Handle changed files --- .../src/main/java/org/photoslide/App.java | 9 +- .../browserlighttable/MediaLoadingTask.java | 135 ++++--- .../org/photoslide/datamodel/MediaFile.java | 45 ++- .../datamodel/MediaFile_Serializable.java | 380 +++++++++++++++--- 4 files changed, 443 insertions(+), 126 deletions(-) diff --git a/PhotoSlide/src/main/java/org/photoslide/App.java b/PhotoSlide/src/main/java/org/photoslide/App.java index f6e71c3..eb0c866 100644 --- a/PhotoSlide/src/main/java/org/photoslide/App.java +++ b/PhotoSlide/src/main/java/org/photoslide/App.java @@ -8,6 +8,8 @@ import javafx.stage.Stage; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; @@ -85,7 +87,7 @@ public void init() throws Exception { checkSearchDBStructure(); } catch (ClassNotFoundException | SQLException e) { if (e.getMessage().startsWith("Unsupported database")) { - new File(Utility.getAppData() + File.separator + "SearchMediaFilesDB").delete(); + new File(Utility.getAppData() + File.separator + "SearchMediaFilesDB").delete(); } else { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, e); } @@ -104,6 +106,11 @@ public void init() throws Exception { } catch (UnsatisfiedLinkError e) { Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, e); } + //create cache dir + if (Files.exists(Path.of(Utility.getAppData() + File.separatorChar + "cache")) == false) { + Files.createDirectory(Path.of(Utility.getAppData() + File.separatorChar + "cache")); + } + notifyPreloader(new ProgressNotification(0.8)); } diff --git a/PhotoSlide/src/main/java/org/photoslide/browserlighttable/MediaLoadingTask.java b/PhotoSlide/src/main/java/org/photoslide/browserlighttable/MediaLoadingTask.java index 6bacdeb..3f407fa 100644 --- a/PhotoSlide/src/main/java/org/photoslide/browserlighttable/MediaLoadingTask.java +++ b/PhotoSlide/src/main/java/org/photoslide/browserlighttable/MediaLoadingTask.java @@ -17,8 +17,11 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.math.BigInteger; import java.nio.file.Files; import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -79,24 +82,23 @@ protected MediaFile call() throws Exception { updateTitle("Reading cache..."); //restore cache - File inPath = new File(Utility.getAppData() + File.separatorChar + selectedPath.toFile().getName() + ".bin"); + File inPath = new File(Utility.getAppData() + File.separatorChar + "cache" + File.separatorChar + createMD5Hash(selectedPath.toString()+"-") + selectedPath.toFile().getName() + ".bin"); FileInputStream fileInputStream; try { fileInputStream = new FileInputStream(inPath); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); List e2 = (ArrayList) objectInputStream.readObject(); - objectInputStream.close(); - cacheList.addAll(e2); + objectInputStream.close(); + cacheList.addAll(e2); + Platform.runLater(() -> { + factory.setListFilesActive(false); + fullMediaList.addAll(cacheList); + }); } catch (IOException | ClassNotFoundException ex) { } updateTitle("Reading cache...finished"); - Platform.runLater(() -> { - factory.setListFilesActive(false); - fullMediaList.addAll(cacheList); - }); - updateTitle("Counting mediafiles..."); Stream fileList = Files.list(selectedPath).filter((t) -> { @@ -130,76 +132,65 @@ protected MediaFile call() throws Exception { long starttime = System.currentTimeMillis(); AtomicInteger iatom = new AtomicInteger(1); - fileList.parallel().forEach((fileItem) -> { - if (this.isCancelled()) { - return; + if (qty != cacheList.size()) { + if (!cacheList.isEmpty()) { + Platform.runLater(() -> { + mainController.getProgressPane().setVisible(false); + mainController.getStatusLabelLeft().setVisible(false); + }); } - if (this.isCancelled() == false) { - if (Files.isDirectory(fileItem) == false) { - if (FileTypes.isValidType(fileItem.toString())) { - MediaFile m = new MediaFile(); - m.setName(fileItem.getFileName().toString()); - m.setPathStorage(fileItem); - m.setMediaType(MediaFile.MediaTypes.IMAGE); - if (fullMediaList.contains(m) == false) { - if (Utility.nativeMemorySize > 4194500) { - Thread.ofVirtual().start(() -> { + fileList.parallel().forEach((fileItem) -> { + if (this.isCancelled()) { + return; + } + if (this.isCancelled() == false) { + if (Files.isDirectory(fileItem) == false) { + if (FileTypes.isValidType(fileItem.toString())) { + MediaFile m = new MediaFile(); + m.setName(fileItem.getFileName().toString()); + m.setPathStorage(fileItem); + m.setMediaType(MediaFile.MediaTypes.IMAGE); + if (fullMediaList.contains(m) == false) { + if (Utility.nativeMemorySize > 4194500) { + Thread.ofVirtual().start(() -> { + try { + loadItem(fileItem, m); + updateValue(m); + } catch (IOException ex) { + m.setMediaType(MediaFile.MediaTypes.NONE); + } + }); + } else { try { loadItem(fileItem, m); updateValue(m); } catch (IOException ex) { m.setMediaType(MediaFile.MediaTypes.NONE); } - }); - } else { - try { - loadItem(fileItem, m); - updateValue(m); - } catch (IOException ex) { - m.setMediaType(MediaFile.MediaTypes.NONE); } } - } else { - if (Utility.nativeMemorySize > 4194500) { - Thread.ofVirtual().start(() -> { - try { - loadItem(fileItem, m); - //updateValue(m); - } catch (IOException ex) { - m.setMediaType(MediaFile.MediaTypes.NONE); - } - }); - } else { - try { - loadItem(fileItem, m); - //updateValue(m); - } catch (IOException ex) { - m.setMediaType(MediaFile.MediaTypes.NONE); - } + if (cacheList.contains(m) == false) { + cacheList.add(m); } } - if (cacheList.contains(m) == false) { - cacheList.add(m); - } } - } - updateMessage(iatom.get() + " / " + qty); - iatom.addAndGet(1); - if (qty > 1000) { - double percentage = (double) iatom.get() / qty * 100; - if (percentage >= loadingLimit) { - factory.setListFilesActive(false); + updateMessage(iatom.get() + " / " + qty); + iatom.addAndGet(1); + if (qty > 1000) { + double percentage = (double) iatom.get() / qty * 100; + if (percentage >= loadingLimit) { + factory.setListFilesActive(false); + } } } + }); + if (this.isCancelled()) { + return null; } - }); - if (this.isCancelled()) { - return null; } - //save cache to disk - File outpath = new File(Utility.getAppData() + File.separatorChar + selectedPath.toFile().getName() + ".bin"); - + File outpath = new File(Utility.getAppData() + File.separatorChar + "cache" + File.separatorChar + createMD5Hash(selectedPath.toString()+"-") + selectedPath.toFile().getName() + ".bin"); + FileOutputStream fileOutputStream; try { fileOutputStream = new FileOutputStream(outpath, false); @@ -308,4 +299,26 @@ protected void succeeded() { executorParallel.shutdown(); } + public String createMD5Hash(final String input) + throws NoSuchAlgorithmException { + + String hashtext = null; + MessageDigest md = MessageDigest.getInstance("MD5"); + // Compute message digest of the input + byte[] messageDigest = md.digest(input.getBytes()); + + hashtext = convertToHex(messageDigest); + + return hashtext; + } + + private String convertToHex(final byte[] messageDigest) { + BigInteger bigint = new BigInteger(1, messageDigest); + String hexText = bigint.toString(16); + while (hexText.length() < 32) { + hexText = "0".concat(hexText); + } + return hexText; + } + } diff --git a/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile.java b/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile.java index 4aed68f..5a2eac8 100644 --- a/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile.java +++ b/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile.java @@ -96,13 +96,13 @@ public class MediaFile implements Serializable { private transient double gpsHeight; private transient SimpleBooleanProperty bookmarked; - public enum MediaTypes { + public static enum MediaTypes { IMAGE, VIDEO, NONE } - public enum VideoTypes { + public static enum VideoTypes { SUPPORTED, UNSUPPORTED } @@ -111,13 +111,17 @@ public enum VideoTypes { private void writeObject(ObjectOutputStream oos) throws IOException { - oos.defaultWriteObject(); + //oos.defaultWriteObject(); + MediaFile_Serializable convertToSerializable = MediaFile_Serializable.convertToSerializable(this); + oos.writeObject(convertToSerializable); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { - ois.defaultReadObject(); + //ois.defaultReadObject(); initData(); + MediaFile_Serializable mserial=(MediaFile_Serializable)ois.readObject(); + MediaFile_Serializable.convertToMediaFile(mserial, this); } public MediaFile() { @@ -900,6 +904,39 @@ public SimpleBooleanProperty bookmarkedProperty() { return bookmarked; } + public SimpleStringProperty getPlace() { + return place; + } + + public void setPlace(String place) { + this.place.set(place); + } + + public SimpleStringProperty getFaces() { + return faces; + } + + public void setFaces(String faces) { + this.faces.set(faces); + } + + public SimpleStringProperty getTitle() { + return title; + } + + public SimpleStringProperty getCamera() { + return camera; + } + + public SimpleStringProperty getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments.set(comments); + } + + public void removeAllEdits() { String fileNameWithExt = getEditFilePath().toString(); File editFile = new File(fileNameWithExt); diff --git a/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile_Serializable.java b/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile_Serializable.java index 20b8554..2409da5 100644 --- a/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile_Serializable.java +++ b/PhotoSlide/src/main/java/org/photoslide/datamodel/MediaFile_Serializable.java @@ -7,54 +7,20 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInput; -import java.io.ObjectInputStream; -import java.io.ObjectOutput; -import java.io.ObjectOutputStream; -import java.io.OutputStream; import java.io.Serializable; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; import java.util.ArrayList; -import java.util.Comparator; -import java.util.Enumeration; import java.util.List; import java.util.Objects; -import java.util.Properties; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; -import javafx.application.Platform; -import javafx.beans.Observable; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Point2D; import javafx.geometry.Rectangle2D; -import javafx.scene.image.Image; -import javafx.scene.image.PixelFormat; -import javafx.scene.image.PixelReader; -import javafx.scene.image.WritableImage; -import javafx.scene.media.Media; -import javafx.util.Callback; -import org.photoslide.browsermetadata.Coordinate; -import org.photoslide.browsermetadata.DMSCoordinate; -import org.photoslide.browsermetadata.DegreeCoordinate; import org.photoslide.browsermetadata.Point; import org.photoslide.imageops.ImageFilter; @@ -69,16 +35,12 @@ public class MediaFile_Serializable implements Serializable { private String pathStorage; private boolean loading; - private transient Image image; - private transient Image unModifiyAbleImage; - private transient Media media; - private String title; private String keywords; private String camera; private String comments; - private SimpleDoubleProperty rotationAngle; - private SimpleIntegerProperty rating; + private double rotationAngle; + private int rating; private String place; private String faces; private Rectangle2D cropView; @@ -87,28 +49,21 @@ public class MediaFile_Serializable implements Serializable { private boolean deleted; private boolean selected; private String stackName; - private SimpleIntegerProperty stackPos; + private int stackPos; private boolean stacked; private FileTime creationTime; private boolean subViewSelected; - private List filterList; private Point gpsPosition; private LocalDateTime gpsDateTime; private double gpsHeight; private boolean bookmarked; + private MediaFile.VideoTypes videoSupported; + private MediaFile.MediaTypes mediaType; + private List filterList; - public enum MediaTypes { - IMAGE, - VIDEO, - NONE - } - - public enum VideoTypes { - SUPPORTED, - UNSUPPORTED + public MediaFile_Serializable() { + filterList = new ArrayList<>(); } - private VideoTypes videoSupported; - private MediaTypes mediaType; @Override public int hashCode() { @@ -142,15 +97,320 @@ public boolean equals(Object obj) { } return true; } - - public static MediaFile_Serializable convertToSerializable(MediaFile m){ - MediaFile_Serializable mserial=new MediaFile_Serializable(); + + public static MediaFile_Serializable convertToSerializable(MediaFile m) { + MediaFile_Serializable mserial = new MediaFile_Serializable(); + mserial.setName(m.getName()); + mserial.setPathStorage(m.getPathStorage().toString()); + mserial.setBookmarked(m.isBookmarked()); + mserial.setLoading(true); + mserial.setSubViewSelected(false); + mserial.setDeleted(m.isDeleted()); + mserial.setSelected(m.isSelected()); + mserial.setStackName(m.getStackName()); + mserial.setStackPos(m.getStackPos()); + mserial.setStacked(m.isStacked()); + mserial.setPlace(m.getPlace().get()); + mserial.setFaces(m.getFaces().get()); + mserial.setFilterList(mserial.convertImageFilterToStringList(m.getFilterListWithoutImageData())); + mserial.setMediaType(m.getMediaType()); + mserial.setTitle(m.getTitle().get()); + mserial.setKeywords(m.getKeywords()); + mserial.setCamera(m.getCamera().get()); + mserial.setComments(m.getCamera().get()); + mserial.setRotationAngle(m.getRotationAngleProperty().get()); + mserial.setRating(m.getRatingProperty().get()); + mserial.setGpsHeight(-1); + mserial.setOrignalImageSize(m.getOrignalImageSize()); + mserial.setCropView(m.getCropView()); return mserial; } - - public static MediaFile convertToSerializable(MediaFile_Serializable mserial){ - MediaFile m=new MediaFile(); - return m; + + public static void convertToMediaFile(MediaFile_Serializable mserial, MediaFile m) { + m.setName(mserial.getName()); + m.setPathStorage(Path.of(mserial.getPathStorage())); + m.setBookmarked(mserial.isBookmarked()); + m.setLoading(true); + m.setSubViewSelected(false); + m.setDeleted(mserial.isDeleted()); + m.setSelected(mserial.isSelected()); + m.setStackName(mserial.getStackName()); + m.setStackPos(mserial.getStackPos()); + m.setStacked(mserial.isStacked()); + m.setPlace(mserial.getPlace()); + m.setFaces(mserial.getFaces()); + m.setFilterList(mserial.convertToImageFilterList(mserial.getFilterList())); + m.setMediaType(mserial.getMediaType()); + m.setTitle(mserial.getTitle()); + m.setKeywords(mserial.getKeywords()); + m.setCamera(mserial.getCamera()); + m.setComments(mserial.getCamera()); + m.setRotationAngle(mserial.getRotationAngle()); + m.setRating(mserial.getRating()); + m.setGpsHeight(-1); + m.setOrignalImageSize(mserial.getOrignalImageSize()); + m.setCropView(mserial.getCropView()); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPathStorage() { + return pathStorage; + } + + public void setPathStorage(String pathStorage) { + this.pathStorage = pathStorage; + } + + public boolean isLoading() { + return loading; + } + + public void setLoading(boolean loading) { + this.loading = loading; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getKeywords() { + return keywords; + } + + public void setKeywords(String keywords) { + this.keywords = keywords; + } + + public String getCamera() { + return camera; + } + + public void setCamera(String camera) { + this.camera = camera; + } + + public String getComments() { + return comments; + } + + public void setComments(String comments) { + this.comments = comments; + } + + public double getRotationAngle() { + return rotationAngle; + } + + public void setRotationAngle(double rotationAngle) { + this.rotationAngle = rotationAngle; + } + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + public String getFaces() { + return faces; + } + + public void setFaces(String faces) { + this.faces = faces; + } + + public Rectangle2D getCropView() { + return cropView; + } + + public void setCropView(Rectangle2D cropView) { + this.cropView = cropView; + } + + public Point2D getOrignalImageSize() { + return orignalImageSize; + } + + public void setOrignalImageSize(Point2D orignalImageSize) { + this.orignalImageSize = orignalImageSize; + } + + public LocalDateTime getRecordTime() { + return recordTime; + } + + public void setRecordTime(LocalDateTime recordTime) { + this.recordTime = recordTime; + } + + public boolean isDeleted() { + return deleted; + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public String getStackName() { + return stackName; + } + + public void setStackName(String stackName) { + this.stackName = stackName; + } + + public int getStackPos() { + return stackPos; + } + + public void setStackPos(int stackPos) { + this.stackPos = stackPos; + } + + public boolean isStacked() { + return stacked; + } + + public void setStacked(boolean stacked) { + this.stacked = stacked; + } + + public FileTime getCreationTime() { + return creationTime; + } + + public void setCreationTime(FileTime creationTime) { + this.creationTime = creationTime; + } + + public boolean isSubViewSelected() { + return subViewSelected; + } + + public void setSubViewSelected(boolean subViewSelected) { + this.subViewSelected = subViewSelected; + } + + public List getFilterList() { + return filterList; + } + + public void setFilterList(List filterList) { + this.filterList = filterList; + } + + public Point getGpsPosition() { + return gpsPosition; + } + + public void setGpsPosition(Point gpsPosition) { + this.gpsPosition = gpsPosition; + } + + public LocalDateTime getGpsDateTime() { + return gpsDateTime; + } + + public void setGpsDateTime(LocalDateTime gpsDateTime) { + this.gpsDateTime = gpsDateTime; + } + + public double getGpsHeight() { + return gpsHeight; + } + + public void setGpsHeight(double gpsHeight) { + this.gpsHeight = gpsHeight; + } + + public boolean isBookmarked() { + return bookmarked; + } + + public void setBookmarked(boolean bookmarked) { + this.bookmarked = bookmarked; + } + + public MediaFile.VideoTypes getVideoSupported() { + return videoSupported; + } + + public void setVideoSupported(MediaFile.VideoTypes videoSupported) { + this.videoSupported = videoSupported; + } + + public MediaFile.MediaTypes getMediaType() { + return mediaType; + } + + public void setMediaType(MediaFile.MediaTypes mediaType) { + this.mediaType = mediaType; + } + + public ObservableList convertToImageFilterList(List sourceList) { + ObservableList destinationList = FXCollections.observableArrayList(); + ObjectMapper mapper = new ObjectMapper(); + sourceList.forEach((filter) -> { + StringTokenizer st = new StringTokenizer(filter, ";"); + String fname = (String) st.nextElement(); + String value = (String) st.nextElement(); + ImageFilter ifm = null; + try { + String cname = fname.substring(12); + ifm = (ImageFilter) mapper.readValue(value, Class.forName("org.photoslide.imageops." + cname)); + } catch (JsonProcessingException | ClassNotFoundException ex) { + Logger.getLogger(MediaFile_Serializable.class.getName()).log(Level.SEVERE, "Cannot find class name in config file", ex); + } + if (ifm != null) { + destinationList.add(ifm); + } + }); + return destinationList; + } + + public List convertImageFilterToStringList(List sourceList) { + ArrayList destinationList = new ArrayList<>(); + if (sourceList.isEmpty() == false) { + ObjectMapper mapper = new ObjectMapper(); + sourceList.forEach((imgFilter) -> { + try { + String value = mapper.writeValueAsString(imgFilter); + destinationList.add("ImageFilter:" + imgFilter.getName() + ";" + value); + } catch (JsonProcessingException ex) { + Logger.getLogger(MediaFile.class.getName()).log(Level.SEVERE, null, ex); + } + }); + } + return destinationList; } }