From 6b9b0dc8bc725b748afdcfd1e1fe48206f4310c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 23:55:58 +0000 Subject: [PATCH 01/10] Bump org.codehaus.mojo:buildnumber-maven-plugin from 1.4 to 3.2.0 Bumps [org.codehaus.mojo:buildnumber-maven-plugin](https://github.com/mojohaus/buildnumber-maven-plugin) from 1.4 to 3.2.0. - [Release notes](https://github.com/mojohaus/buildnumber-maven-plugin/releases) - [Commits](https://github.com/mojohaus/buildnumber-maven-plugin/compare/buildnumber-maven-plugin-1.4...3.2.0) --- updated-dependencies: - dependency-name: org.codehaus.mojo:buildnumber-maven-plugin dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4d800ebc..57416f9b4 100644 --- a/pom.xml +++ b/pom.xml @@ -197,7 +197,7 @@ org.codehaus.mojo buildnumber-maven-plugin - 1.4 + 3.2.0 org.apache.cxf From 70f13412fa3e142be01c2ddf97779bba35950330 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 23:56:13 +0000 Subject: [PATCH 02/10] Bump com.google.errorprone:error_prone_annotations from 2.26.1 to 2.27.1 Bumps [com.google.errorprone:error_prone_annotations](https://github.com/google/error-prone) from 2.26.1 to 2.27.1. - [Release notes](https://github.com/google/error-prone/releases) - [Commits](https://github.com/google/error-prone/compare/v2.26.1...v2.27.1) --- updated-dependencies: - dependency-name: com.google.errorprone:error_prone_annotations dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4d800ebc..8ce96f13d 100644 --- a/pom.xml +++ b/pom.xml @@ -161,7 +161,7 @@ com.google.errorprone error_prone_annotations - 2.26.1 + 2.27.1 net.java.dev.jna From beebc535b629487f308e0a4b85d74f9fa4231174 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 23:56:39 +0000 Subject: [PATCH 03/10] Bump org.checkerframework:checker-qual from 3.42.0 to 3.43.0 Bumps [org.checkerframework:checker-qual](https://github.com/typetools/checker-framework) from 3.42.0 to 3.43.0. - [Release notes](https://github.com/typetools/checker-framework/releases) - [Changelog](https://github.com/typetools/checker-framework/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/typetools/checker-framework/compare/checker-framework-3.42.0...checker-framework-3.43.0) --- updated-dependencies: - dependency-name: org.checkerframework:checker-qual dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e4d800ebc..1d041181f 100644 --- a/pom.xml +++ b/pom.xml @@ -151,7 +151,7 @@ org.checkerframework checker-qual - 3.42.0 + 3.43.0 com.google.j2objc From 938ca1b5e069a0c9cd1e0b3a46ca1322d8fe403a Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Wed, 8 May 2024 14:24:15 +0000 Subject: [PATCH 04/10] kagemomiji/airsonic-advanced#432 check media file existence before getting cover art --- .../org/airsonic/player/domain/MediaFile.java | 20 +++++++++++++++++++ .../player/service/CoverArtCreateService.java | 17 ++++++++-------- .../player/service/MediaFileService.java | 13 ++++++++++-- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/domain/MediaFile.java b/airsonic-main/src/main/java/org/airsonic/player/domain/MediaFile.java index 831df8c73..cfe4d8037 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/domain/MediaFile.java +++ b/airsonic-main/src/main/java/org/airsonic/player/domain/MediaFile.java @@ -21,13 +21,16 @@ package org.airsonic.player.domain; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.airsonic.player.util.FileUtil; import org.apache.commons.io.FilenameUtils; import jakarta.persistence.*; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -188,6 +191,23 @@ public Path getFullPath() { return folder.getPath().resolve(path); } + @JsonIgnore + public boolean isExist() { + return Files.exists(getFullPath()); + } + + /** + * Returns true if the file has been changed since the last time it was scanned. + * + * @return true if the file has been changed since the last time it was scanned. True if the file does not exist. + */ + @JsonIgnore + public boolean isChanged() { + Path fullPath = this.getFullPath(); + return !Files.exists(fullPath) || this.changed.truncatedTo(ChronoUnit.MICROS) + .compareTo(FileUtil.lastModified(fullPath).truncatedTo(ChronoUnit.MICROS)) < 0; + } + public String getIndexPath() { return indexPath; } diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java index 5bc3c7130..5693360aa 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java @@ -191,7 +191,7 @@ public CoverArtRequest createMediaFileCoverArtRequest(int mediaFileId, int offse * @return the cover art request */ @Nullable - public CoverArtRequest createMediaFileCoverArtRequest(MediaFile mediaFile, int offset) { + public CoverArtRequest createMediaFileCoverArtRequest(@Nullable MediaFile mediaFile, int offset) { if (mediaFile == null) { return null; } @@ -199,6 +199,9 @@ public CoverArtRequest createMediaFileCoverArtRequest(MediaFile mediaFile, int o return new VideoCoverArtRequest(mediaFile, offset); } MediaFile dir = mediaFile.isDirectory() ? mediaFile : mediaFileService.getParentOf(mediaFile); + if (dir == null || !dir.isExist()) { + return null; + } CoverArt coverArt = coverArtService.getMediaFileArt(dir.getId()); return new MediaFileCoverArtRequest(coverArt, dir, mediaFile.isDirectory() ? null : mediaFile.getId()); } @@ -310,24 +313,22 @@ private InputStream getImageInputStream(CoverArt art) throws IOException { * the embedded album art is returned. In addition returns the mime type */ public Pair getImageInputStreamWithType(Path file) throws IOException { - InputStream is; - String mimeType; if (JaudiotaggerParser.isImageAvailable(file)) { LOG.trace("Using Jaudio Tagger for reading artwork from {}", file); try { LOG.trace("Reading artwork from file {}", file); Artwork artwork = JaudiotaggerParser.getArtwork(file); - is = new ByteArrayInputStream(artwork.getBinaryData()); - mimeType = artwork.getMimeType(); + return Pair.of(new ByteArrayInputStream(artwork.getBinaryData()), artwork.getMimeType()); } catch (Exception e) { LOG.debug("Could not read artwork from file {}", file); throw new RuntimeException(e); } } else { - is = new BufferedInputStream(Files.newInputStream(file)); - mimeType = StringUtil.getMimeType(FilenameUtils.getExtension(file.toString())); + return Pair.of( + new BufferedInputStream(Files.newInputStream(file)), + StringUtil.getMimeType(FilenameUtils.getExtension(file.toString())) + ); } - return Pair.of(is, mimeType); } private InputStream getImageInputStreamForVideo(MediaFile mediaFile, int width, int height, int offset) diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java b/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java index d3210eaf2..545e5b0d9 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java @@ -229,7 +229,15 @@ public MediaFile getParentOf(MediaFile mediaFile) { return getParentOf(mediaFile, settingsService.isFastCacheEnabled()); } - public MediaFile getParentOf(MediaFile mediaFile, boolean minimizeDiskAccess) { + /** + * Returns the parent of the given media file. + * + * @param mediaFile The media file. Must not be {@code null}. + * @param minimizeDiskAccess Whether to refrain from checking for new or changed files + * @return The parent of the given media file. May be {@code null}. + */ + @Nullable + public MediaFile getParentOf(@Nonnull MediaFile mediaFile, boolean minimizeDiskAccess) { if (mediaFile.getParentPath() == null) { return null; } @@ -241,7 +249,7 @@ private boolean needsUpdate(MediaFile mediaFile, boolean minimizeDiskAccess) { && !mediaFile.isIndexedTrack() // ignore virtual track && (mediaFile.getVersion() < MediaFile.VERSION || settingsService.getFullScan() - || mediaFile.getChanged().truncatedTo(ChronoUnit.MICROS).compareTo(FileUtil.lastModified(mediaFile.getFullPath()).truncatedTo(ChronoUnit.MICROS)) < 0 + || mediaFile.isChanged() || (mediaFile.hasIndex() && mediaFile.getChanged().truncatedTo(ChronoUnit.MICROS).compareTo(FileUtil.lastModified(mediaFile.getFullIndexPath()).truncatedTo(ChronoUnit.MICROS)) < 0) ); } @@ -1658,6 +1666,7 @@ public MediaFile delete(MediaFile file) { file.setPresent(false); file.setChildrenLastUpdated(Instant.ofEpochMilli(1)); mediaFileRepository.save(file); + coverArtService.delete(EntityType.MEDIA_FILE, file.getId()); return file; } From 925c17de351f5b59a966bf48c5c41e82e7f4e161 Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Thu, 9 May 2024 15:38:42 +0000 Subject: [PATCH 05/10] kagemomiji/airsonic-advanced#432 add null check for cover art existence --- .../player/controller/CoverArtController.java | 3 +++ .../player/service/CoverArtCreateService.java | 25 ++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/controller/CoverArtController.java b/airsonic-main/src/main/java/org/airsonic/player/controller/CoverArtController.java index 8b81addd8..a8aea6634 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/controller/CoverArtController.java +++ b/airsonic-main/src/main/java/org/airsonic/player/controller/CoverArtController.java @@ -189,6 +189,9 @@ private void sendUnscaled(CoverArtRequest coverArtRequest, HttpServletResponse r coverArtRequest.getCoverArt().getFullPath()); try (InputStream in = imageInputStreamWithType.getLeft()) { + if (in == null) { + throw new FileNotFoundException("Cover art not found"); + } response.setContentType(imageInputStreamWithType.getRight()); IOUtils.copy(in, response.getOutputStream()); } diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java index 5693360aa..9c9167efd 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtCreateService.java @@ -303,6 +303,7 @@ public BufferedImage createVideoImage(VideoCoverArtRequest request, int size) { * audio file, * the embedded album art is returned. */ + @Nullable private InputStream getImageInputStream(CoverArt art) throws IOException { return getImageInputStreamWithType(art.getFullPath()).getLeft(); } @@ -313,21 +314,21 @@ private InputStream getImageInputStream(CoverArt art) throws IOException { * the embedded album art is returned. In addition returns the mime type */ public Pair getImageInputStreamWithType(Path file) throws IOException { - if (JaudiotaggerParser.isImageAvailable(file)) { - LOG.trace("Using Jaudio Tagger for reading artwork from {}", file); - try { - LOG.trace("Reading artwork from file {}", file); + try { + if (JaudiotaggerParser.isImageAvailable(file)) { + LOG.trace("Using Jaudio Tagger for reading artwork from {}", file); Artwork artwork = JaudiotaggerParser.getArtwork(file); return Pair.of(new ByteArrayInputStream(artwork.getBinaryData()), artwork.getMimeType()); - } catch (Exception e) { - LOG.debug("Could not read artwork from file {}", file); - throw new RuntimeException(e); + } else { + LOG.trace("Reading artwork from file {}", file); + return Pair.of( + new BufferedInputStream(Files.newInputStream(file)), + StringUtil.getMimeType(FilenameUtils.getExtension(file.toString())) + ); } - } else { - return Pair.of( - new BufferedInputStream(Files.newInputStream(file)), - StringUtil.getMimeType(FilenameUtils.getExtension(file.toString())) - ); + } catch (Exception e) { + LOG.debug("Could not read artwork from file {}", file); + return Pair.of(null, null); } } From 4368ee597293572f98b36b64746145da2ce9bc82 Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Fri, 10 May 2024 15:00:22 +0000 Subject: [PATCH 06/10] kagemomiji/airsonic-advanced#432 add delete functions to persistIfNeeded method and check existing function --- .../player/domain/dto/CoverArtRequest.java | 3 +- .../player/service/CoverArtService.java | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/domain/dto/CoverArtRequest.java b/airsonic-main/src/main/java/org/airsonic/player/domain/dto/CoverArtRequest.java index ccd8f9605..3293c458c 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/domain/dto/CoverArtRequest.java +++ b/airsonic-main/src/main/java/org/airsonic/player/domain/dto/CoverArtRequest.java @@ -3,6 +3,7 @@ import org.airsonic.player.domain.CoverArt; import org.airsonic.player.util.FileUtil; +import java.nio.file.Files; import java.time.Instant; import java.util.Optional; import java.util.function.Supplier; @@ -29,7 +30,7 @@ public String getKey() { } public Instant lastModified() { - return Optional.ofNullable(coverArt).map(c -> FileUtil.lastModified(c.getFullPath())) + return Optional.ofNullable(coverArt).filter(c -> Files.exists(c.getFullPath())).map(c -> FileUtil.lastModified(c.getFullPath())) .orElseGet(lastModifiedGenerator); } diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java index cbfc5440e..feb23cb6b 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java @@ -22,6 +22,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import jakarta.annotation.Nullable; + import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; @@ -66,6 +68,9 @@ public void persistIfNeeded(MediaFile mediaFile) { upsert(mediaFileArt); } mediaFile.setArt(null); + } else { + // If the media file has no cover art, remove any existing cover art. + delete(EntityType.MEDIA_FILE, mediaFile.getId()); } } @@ -84,6 +89,9 @@ public void persistIfNeeded(Album album) { upsert(albumArt); } album.setArt(null); + } else { + // If the album has no cover art, remove any existing cover art. + delete(EntityType.ALBUM, album.getId()); } } @@ -102,6 +110,9 @@ public void persistIfNeeded(Artist artist) { upsert(artistArt); } artist.setArt(null); + } else { + // If the artist has no cover art, remove any existing cover art. + delete(EntityType.ARTIST, artist.getId()); } } @@ -135,24 +146,17 @@ public CoverArt getMediaFileArt(@Param("id") int id) { return art; } + @Nullable public Path getMediaFileArtPath(int id) { CoverArt art = getMediaFileArt(id); return getFullPath(art); } - public Path getFullPath(CoverArt art) { + @Nullable + public Path getFullPath(@Nullable CoverArt art) { if (art != null && !CoverArt.NULL_ART.equals(art)) { - if (art.getFolder() == null) { - // null folder ids mean absolute paths - return art.getRelativePath(); - } else { - MusicFolder folder = art.getFolder(); - if (folder != null) { - return art.getFullPath(folder.getPath()); - } - } + return art.getFullPath(); } - return null; } @@ -169,7 +173,7 @@ public void expunge() { .filter(art -> (art.getEntityType() == EntityType.ALBUM && art.getAlbum() == null) || (art.getEntityType() == EntityType.ARTIST && art.getArtist() == null) || - (art.getEntityType() == EntityType.MEDIA_FILE && art.getMediaFile() == null)) + (art.getEntityType() == EntityType.MEDIA_FILE && (art.getMediaFile() == null || !art.getMediaFile().isPresent()))) .collect(Collectors.toList()); coverArtRepository.deleteAll(expungeCoverArts); } From a47b04f5b0178798489981cbf6a2214102b5195b Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Fri, 10 May 2024 15:20:53 +0000 Subject: [PATCH 07/10] kagemomiji/airsonic-advanced#432 Modified tests for exupunge coverart --- .../player/service/CoverArtServiceTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java b/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java index 47561c433..97c7cbf9c 100644 --- a/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java +++ b/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java @@ -145,11 +145,12 @@ void testExpunge() { new CoverArt(1, EntityType.ALBUM, "path/to/image.jpg", null, false), new CoverArt(2, EntityType.ARTIST, "path/to/image.jpg", null, false), new CoverArt(3, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false), - new CoverArt(4, EntityType.ALBUM, "path/to/image.jpg", null, false), - new CoverArt(5, EntityType.ARTIST, "path/to/image.jpg", null, false), - new CoverArt(6, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false) + new CoverArt(4, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false), + new CoverArt(5, EntityType.ALBUM, "path/to/image.jpg", null, false), + new CoverArt(6, EntityType.ARTIST, "path/to/image.jpg", null, false), + new CoverArt(7, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false) ).map(art -> { - if (art.getEntityId() < 4) { + if (art.getEntityId() < 5) { switch (art.getEntityType()) { case ALBUM: art.setAlbum(new Album()); @@ -158,7 +159,9 @@ void testExpunge() { art.setArtist(new Artist()); break; case MEDIA_FILE: - art.setMediaFile(new MediaFile()); + MediaFile mediaFile = new MediaFile(); + mediaFile.setPresent(art.getEntityId() == 3); // only media file with id 3 has a media file present + art.setMediaFile(mediaFile); break; case NONE: break; @@ -171,7 +174,7 @@ void testExpunge() { coverArtService.expunge(); verify(coverArtRepository, times(1)).deleteAll(coverArtListCaptor.capture()); List deletedCoverArt = coverArtListCaptor.getValue(); - assertEquals(3, deletedCoverArt.size()); + assertEquals(4, deletedCoverArt.size()); assertTrue(deletedCoverArt.stream().allMatch(art -> art.getEntityId() > 3)); } From 56514eec7d36a2cb25f37a0082886086c94bef70 Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Sat, 11 May 2024 14:51:19 +0000 Subject: [PATCH 08/10] kagemomiji/airsonic-advanced#432 Remove unnecessary cover art deletion code --- .../org/airsonic/player/service/CoverArtService.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java index feb23cb6b..8f38ba0ce 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java @@ -68,9 +68,6 @@ public void persistIfNeeded(MediaFile mediaFile) { upsert(mediaFileArt); } mediaFile.setArt(null); - } else { - // If the media file has no cover art, remove any existing cover art. - delete(EntityType.MEDIA_FILE, mediaFile.getId()); } } @@ -89,9 +86,6 @@ public void persistIfNeeded(Album album) { upsert(albumArt); } album.setArt(null); - } else { - // If the album has no cover art, remove any existing cover art. - delete(EntityType.ALBUM, album.getId()); } } @@ -110,9 +104,6 @@ public void persistIfNeeded(Artist artist) { upsert(artistArt); } artist.setArt(null); - } else { - // If the artist has no cover art, remove any existing cover art. - delete(EntityType.ARTIST, artist.getId()); } } From ff4b9370e863fe5764ada4d5ad6422f64984b63d Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Tue, 14 May 2024 13:38:45 +0000 Subject: [PATCH 09/10] kagemomiji/airsonic-advanced#432 Add an implementation to retrieve mediafiles while ignoring the cache when performing playqueue save --- .../player/service/MediaFileService.java | 16 +++++++++++++--- .../player/service/PlayQueueService.java | 6 ++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java b/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java index cb322a518..cd851e02c 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/MediaFileService.java @@ -61,7 +61,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; @@ -213,9 +212,20 @@ public MediaFile getMediaFile(Path relativePath, MusicFolder folder, Double star * @param id The media file id. * @return mediafile for the given id. */ - public MediaFile getMediaFile(@Param("id") Integer id) { + public MediaFile getMediaFile(Integer id) { + return getMediaFile(id, false); + } + + /** + * Returns the media file for checking last modified. + * + * @param id The media file id. + * @param ignoreCache Whether to ignore the cache + * @return mediafile for the given id. + */ + public MediaFile getMediaFile(Integer id, boolean ignoreCache) { if (Objects.isNull(id)) return null; - MediaFile result = mediaFileCache.getMediaFileById(id); + MediaFile result = ignoreCache ? null : mediaFileCache.getMediaFileById(id); if (result == null) { result = mediaFileRepository.findById(id).map(mediaFile -> checkLastModified(mediaFile, settingsService.isFastCacheEnabled())).orElse(null); mediaFileCache.putMediaFileById(id, result); diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/PlayQueueService.java b/airsonic-main/src/main/java/org/airsonic/player/service/PlayQueueService.java index dd09daa66..0585b02f0 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/PlayQueueService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/PlayQueueService.java @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Function; @@ -148,9 +149,10 @@ public int savePlayQueue(Player player, int index, long offset) { @Transactional public int savePlayQueue(String username, List mediaFileIds, Integer currentFileId, Long position, String changedBy) { SavedPlayQueue savedPlayQueue = savedPlayQueueRepository.findByUsername(username).orElse(new SavedPlayQueue(username)); - List mediaFiles = mediaFileIds.stream().map(mediaFileService::getMediaFile).collect(Collectors.toList()); + + List mediaFiles = mediaFileIds.stream().map(mid -> mediaFileService.getMediaFile(mid, true)).filter(Objects::nonNull).collect(Collectors.toList()); savedPlayQueue.setMediaFiles(mediaFiles); - savedPlayQueue.setCurrentMediaFile(mediaFileService.getMediaFile(currentFileId)); + savedPlayQueue.setCurrentMediaFile(mediaFileService.getMediaFile(currentFileId, true)); savedPlayQueue.setPositionMillis(position); savedPlayQueue.setChanged(Instant.now()); savedPlayQueue.setChangedBy(changedBy); From f38306f7992196bdaadfcc8f0ccefb85956940bc Mon Sep 17 00:00:00 2001 From: "Y.Tory" <5343692+kagemomiji@users.noreply.github.com> Date: Tue, 14 May 2024 14:48:43 +0000 Subject: [PATCH 10/10] kagemomiji/airsonic-advanced#432 fix expunge errors --- .../MusicFolderSettingsController.java | 4 ++-- .../org/airsonic/player/domain/CoverArt.java | 6 ++--- .../player/service/CoverArtService.java | 16 +++++++++---- .../player/service/CoverArtServiceTest.java | 24 ++++++++++++------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java b/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java index 367096458..e6563ddfa 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java +++ b/airsonic-main/src/main/java/org/airsonic/player/controller/MusicFolderSettingsController.java @@ -135,14 +135,14 @@ private void expunge() { } LOG.debug("Cleaning database..."); + LOG.debug("Deleting non-present cover art..."); + coverArtService.expunge(); LOG.debug("Deleting non-present artists..."); artistService.expunge(); LOG.debug("Deleting non-present albums..."); albumService.expunge(); LOG.debug("Deleting non-present media files..."); mediaFileService.expunge(); - LOG.debug("Deleting non-present cover art..."); - coverArtService.expunge(); LOG.debug("Deleting non-present media folders..."); mediaFolderService.expunge(); LOG.debug("Refreshing playlist stats..."); diff --git a/airsonic-main/src/main/java/org/airsonic/player/domain/CoverArt.java b/airsonic-main/src/main/java/org/airsonic/player/domain/CoverArt.java index 14e41f95f..fa44c551d 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/domain/CoverArt.java +++ b/airsonic-main/src/main/java/org/airsonic/player/domain/CoverArt.java @@ -49,15 +49,15 @@ public class CoverArt { @Column(name = "updated") private Instant updated = created; - @OneToOne(fetch = FetchType.LAZY) + @OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "entity_id", insertable = false, updatable = false) private Artist artist; - @OneToOne(fetch = FetchType.LAZY) + @OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "entity_id", insertable = false, updatable = false) private Album album; - @OneToOne(fetch = FetchType.LAZY) + @OneToOne(fetch = FetchType.EAGER) @JoinColumn(name = "entity_id", insertable = false, updatable = false) private MediaFile mediaFile; diff --git a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java index 8f38ba0ce..42148856d 100644 --- a/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java +++ b/airsonic-main/src/main/java/org/airsonic/player/service/CoverArtService.java @@ -161,10 +161,18 @@ public void delete(EntityType type, Integer id) { public void expunge() { coverArtCache.clear(); List expungeCoverArts = coverArtRepository.findAll().stream() - .filter(art -> - (art.getEntityType() == EntityType.ALBUM && art.getAlbum() == null) || - (art.getEntityType() == EntityType.ARTIST && art.getArtist() == null) || - (art.getEntityType() == EntityType.MEDIA_FILE && (art.getMediaFile() == null || !art.getMediaFile().isPresent()))) + .filter(art -> { + switch (art.getEntityType()) { + case ALBUM: + return art.getAlbum() == null || !art.getAlbum().isPresent(); + case ARTIST: + return art.getArtist() == null || !art.getArtist().isPresent(); + case MEDIA_FILE: + return art.getMediaFile() == null || !art.getMediaFile().isPresent(); + default: + return false; + } + }) .collect(Collectors.toList()); coverArtRepository.deleteAll(expungeCoverArts); } diff --git a/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java b/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java index 97c7cbf9c..b9eaf5d97 100644 --- a/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java +++ b/airsonic-main/src/test/java/org/airsonic/player/service/CoverArtServiceTest.java @@ -145,22 +145,28 @@ void testExpunge() { new CoverArt(1, EntityType.ALBUM, "path/to/image.jpg", null, false), new CoverArt(2, EntityType.ARTIST, "path/to/image.jpg", null, false), new CoverArt(3, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false), - new CoverArt(4, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false), - new CoverArt(5, EntityType.ALBUM, "path/to/image.jpg", null, false), - new CoverArt(6, EntityType.ARTIST, "path/to/image.jpg", null, false), - new CoverArt(7, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false) + new CoverArt(4, EntityType.ALBUM, "path/to/image.jpg", null, false), + new CoverArt(5, EntityType.ARTIST, "path/to/image.jpg", null, false), + new CoverArt(6, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false), + new CoverArt(7, EntityType.ALBUM, "path/to/image.jpg", null, false), + new CoverArt(8, EntityType.ARTIST, "path/to/image.jpg", null, false), + new CoverArt(9, EntityType.MEDIA_FILE, "path/to/image.jpg", null, false) ).map(art -> { - if (art.getEntityId() < 5) { + if (art.getEntityId() < 7) { switch (art.getEntityType()) { case ALBUM: - art.setAlbum(new Album()); + Album album = new Album(); + album.setPresent(art.getEntityId() <= 3); // only album with id 3 has an album present + art.setAlbum(album); break; case ARTIST: - art.setArtist(new Artist()); + Artist artist = new Artist(); + artist.setPresent(art.getEntityId() <= 3); // only artist with id 3 has an artist present + art.setArtist(artist); break; case MEDIA_FILE: MediaFile mediaFile = new MediaFile(); - mediaFile.setPresent(art.getEntityId() == 3); // only media file with id 3 has a media file present + mediaFile.setPresent(art.getEntityId() <= 3); // only media file with id 3 has a media file present art.setMediaFile(mediaFile); break; case NONE: @@ -174,7 +180,7 @@ void testExpunge() { coverArtService.expunge(); verify(coverArtRepository, times(1)).deleteAll(coverArtListCaptor.capture()); List deletedCoverArt = coverArtListCaptor.getValue(); - assertEquals(4, deletedCoverArt.size()); + assertEquals(6, deletedCoverArt.size()); assertTrue(deletedCoverArt.stream().allMatch(art -> art.getEntityId() > 3)); }