Skip to content

Commit

Permalink
Merge pull request #636 from kagemomiji/issue624-support-utf8-bom-cue…
Browse files Browse the repository at this point in the history
…-sheet

#624 support UTF-8 with BOM CUE sheets
  • Loading branch information
kagemomiji authored Nov 21, 2024
2 parents de021d4 + 7cacdcf commit b640a84
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/trivy_scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v4

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.24.0
uses: aquasecurity/trivy-action@0.28.0
with:
scan-type: 'fs'
format: 'sarif'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ public class MediaFileService {

private final Map<Integer, Pair<Integer, Instant>> lastPlayed = new ConcurrentHashMap<>();

private boolean hasBOM(byte[] bom, int bytesRead) {
return bytesRead == 3 && bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF;
}

public MediaFile getMediaFile(String pathName) {
return getMediaFile(Paths.get(pathName));
}
Expand Down Expand Up @@ -1349,13 +1353,27 @@ private CueSheet getCueSheet(@Nonnull Path cueFile) {
if (cm != null && cm.getConfidence() > THRESHOLD) {
cs = Charset.forName(cm.getName());
}
LOG.debug("Detected charset for cuesheet file {}: Charset detected as {}", cueFile, cs);
bis.mark(3);

// check for BOM
byte[] bom = new byte[3];
int bytesRead = bis.read(bom, 0, 3);
if (!hasBOM(bom, bytesRead)) {
bis.reset();
}
cueSheet = CueParser.parse(bis, cs);
} catch (IOException e) {
LOG.warn("Defaulting to UTF-8 for cuesheet {}", cueFile);
}
cueSheet = CueParser.parse(cueFile, cs);
if (cueSheet.getMessages().stream().filter(m -> m.toString().toLowerCase().contains("warning")).findFirst().isPresent()) {
LOG.warn("Error parsing cuesheet {}", cueFile);
return null;
if (cueSheet != null) {
if (cueSheet.getMessages().stream().filter(m -> m.toString().toLowerCase().contains("warning"))
.map(m -> {
LOG.warn("Parsing {} at line {} : {}", cueFile, m.getLineNumber(), m.getMessage());
return m;
}).findFirst().isPresent()) {
cueSheet = null;
}
}
break;
case "flac":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,70 @@ public void testMusicCue() {
assertEquals(0.0d, track1.getStartPosition(), 0.0d);
}

@Test
public void testMusicCueWithBOM() {
LOG.info("start testMusicCueWithBOM");

// Add the "cue" folder to the database
Path musicFolderFile = MusicFolderTestData.resolveMusicCueWithBOMFolderPath();
MusicFolder musicFolder = new MusicFolder(musicFolderFile, "Cue", Type.MEDIA, true, Instant.now().truncatedTo(ChronoUnit.MICROS));
testFolders.add(musicFolder);
musicFolderRepository.saveAll(testFolders);
TestCaseUtils.execScan(mediaScannerService);

// Retrieve the "Cue" folder from the database to make
// sure that we don't accidentally operate on other folders
// from previous tests.
musicFolder = musicFolderRepository.findById(musicFolder.getId()).get();
List<MusicFolder> folders = new ArrayList<>();
folders.add(musicFolder);

// Test that the artist is correctly imported
List<Artist> allArtists = artistService.getAlphabeticalArtists(folders);
assertEquals(1, allArtists.size());
Artist artist = allArtists.get(0);
assertEquals("TestCueArtist", artist.getName());
assertEquals(1, artist.getAlbumCount());


// Test that the album is correctly imported
List<Album> allAlbums = albumService.getAlphabeticalAlbums(true, true, folders);
assertEquals(1, allAlbums.size());
Album album = allAlbums.get(0);
assertEquals("AirsonicTest", album.getName());
assertEquals("TestCueArtist", album.getArtist());
assertEquals(2, album.getSongCount());

// Test that the music file is correctly imported
List<MediaFile> albumFiles = mediaFileRepository.findByFolderAndParentPath(allAlbums.get(0).getFolder(), allAlbums.get(0).getPath(), Sort.by("startPosition"));
assertEquals(3, albumFiles.size());
MediaFile file = albumFiles.get(0);
assertEquals("airsonic-test", file.getTitle());
assertEquals("wav", file.getFormat());
assertNull(file.getAlbumName());
assertNull(file.getArtist());
assertNull(file.getAlbumArtist());
assertNull(file.getTrackNumber());
assertNull(file.getYear());
assertEquals(album.getPath(), file.getParentPath());
assertEquals(Paths.get(album.getPath()).resolve("airsonic-test.wav").toString(), file.getPath());
assertTrue(file.getIndexPath().contains("airsonic-test.cue"));
assertEquals(-1.0d, file.getStartPosition(), 0.0d);

MediaFile track1 = albumFiles.get(1);
assertEquals("Handel", track1.getTitle());
assertEquals("wav", track1.getFormat());
assertEquals(track1.getAlbumName(), "AirsonicTest");
assertEquals("Beecham", track1.getArtist());
assertEquals("TestCueArtist", track1.getAlbumArtist());
assertEquals(1L, (long)track1.getTrackNumber());
assertNull(track1.getYear());
assertEquals(album.getPath(), track1.getParentPath());
assertEquals(Paths.get(album.getPath()).resolve("airsonic-test.wav").toString(), track1.getPath());
assertNull(track1.getIndexPath());
assertEquals(0.0d, track1.getStartPosition(), 0.0d);
}

@Test
public void testMusicCueWithDisableCueIndexing() {
LOG.info("start testMusicCueWithDisableCueIndexing");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public static Path resolveMusicCueFolderPath() {
return resolveBaseMediaPath().resolve("cue");
}

public static Path resolveMusicCueWithBOMFolderPath() {
return resolveBaseMediaPath().resolve("cueBom");
}

public static Path resolveMusicDisableCueFolderPath() {
return resolveBaseMediaPath().resolve("disableCue");
}
Expand Down
13 changes: 13 additions & 0 deletions airsonic-main/src/test/resources/MEDIAS/cueBom/airsonic-test.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FILE "airsonic-test.wav" MOTOROLA
PERFORMER "TestCueArtist"
TITLE "AirsonicTest"
REM DATE 2023
REM GENRE "Classic"
TRACK 01 AUDIO
TITLE "Handel"
PERFORMER "Beecham"
INDEX 01 00:00:00
TRACK 02 AUDIO
TITLE "Jesu, Joy of Man's Desiring"
PERFORMER "Lipatti"
INDEX 01 04:01:31
Binary file not shown.
8 changes: 4 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<java.version>17</java.version>
<cxf.version>4.0.5</cxf.version>
<snakeyaml.version>2.3</snakeyaml.version>
<lucene.version>9.11.1</lucene.version>
<lucene.version>9.12.0</lucene.version>
<docker.container.repo>ghcr.io/kagemomiji/airsonic-advanced</docker.container.repo>
<docker.java.version>17.0.10_7</docker.java.version>
</properties>
Expand Down Expand Up @@ -111,7 +111,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.0-jre</version>
<version>33.3.1-jre</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
Expand Down Expand Up @@ -161,7 +161,7 @@
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<version>2.32.0</version>
<version>2.36.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
Expand Down Expand Up @@ -222,7 +222,7 @@
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.45.0</version>
<version>0.45.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down

0 comments on commit b640a84

Please sign in to comment.