Internal help: Warn for potential db corruption issues

master
François-Xavier Thomas 4 years ago committed by jvoisin
parent 768968d075
commit 55f9a41673
  1. 9
      airsonic-main/src/main/java/org/airsonic/player/controller/InternalHelpController.java
  2. 61
      airsonic-main/src/main/java/org/airsonic/player/dao/MediaFileDao.java
  3. 1
      airsonic-main/src/main/resources/org/airsonic/player/i18n/ResourceBundle_en.properties
  4. 59
      airsonic-main/src/main/webapp/WEB-INF/jsp/internalhelp.jsp

@ -20,6 +20,7 @@
package org.airsonic.player.controller;
import org.airsonic.player.dao.DaoHelper;
import org.airsonic.player.dao.MediaFileDao;
import org.airsonic.player.dao.MusicFolderDao;
import org.airsonic.player.domain.MediaLibraryStatistics;
import org.airsonic.player.domain.MusicFolder;
@ -162,6 +163,8 @@ public class InternalHelpController {
@Autowired
private MusicFolderDao musicFolderDao;
@Autowired
private MediaFileDao mediaFileDao;
@Autowired
private TranscodingService transcodingService;
@GetMapping
@ -311,6 +314,12 @@ public class InternalHelpController {
map.put("dbMediaFileDistinctArtistCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(DISTINCT artist) FROM MEDIA_FILE WHERE present"), Long.class));
map.put("dbMediaFileDistinctAlbumArtistCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(DISTINCT album_artist) FROM MEDIA_FILE WHERE present"), Long.class));
map.put("dbMediaFilesInNonPresentMusicFoldersCount", mediaFileDao.getFilesInNonPresentMusicFoldersCount(Arrays.asList(settingsService.getPodcastFolder())));
map.put("dbMediaFilesInNonPresentMusicFoldersSample", mediaFileDao.getFilesInNonPresentMusicFolders(10, Arrays.asList(settingsService.getPodcastFolder())));
map.put("dbMediaFilesWithMusicFolderMismatchCount", mediaFileDao.getFilesWithMusicFolderMismatchCount());
map.put("dbMediaFilesWithMusicFolderMismatchSample", mediaFileDao.getFilesWithMusicFolderMismatch(10));
map.put("dbTableCount", dbTableCount);
}

@ -597,6 +597,67 @@ public class MediaFileDao extends AbstractDao {
return namedQuery(query, rowMapper, args);
}
/**
* Return a list of media file objects that don't belong to an existing music folder
* @param count maximum number of media file objects to return
* @param excludeFolders music folder paths excluded from the results
* @return a list of media files, sorted by id
*/
public List<MediaFile> getFilesInNonPresentMusicFolders(final int count, List<String> excludeFolders) {
Map<String, Object> args = new HashMap<>();
args.put("excludeFolders", excludeFolders);
args.put("count", count);
return namedQuery(
"SELECT " + prefix(QUERY_COLUMNS, "media_file") + " FROM media_file " +
"LEFT OUTER JOIN music_folder ON music_folder.path = media_file.folder " +
"WHERE music_folder.id IS NULL " +
"AND media_file.folder NOT IN (:excludeFolders) " +
"ORDER BY media_file.id LIMIT :count",
rowMapper, args);
}
/**
* Count the number of media files that don't belong to an existing music folder
* @param excludeFolders music folder paths excluded from the results
* @return a number of media file rows in the database
*/
public int getFilesInNonPresentMusicFoldersCount(List<String> excludeFolders) {
Map<String, Object> args = new HashMap<>();
args.put("excludeFolders", excludeFolders);
return namedQueryForInt(
"SELECT count(media_file.id) FROM media_file " +
"LEFT OUTER JOIN music_folder ON music_folder.path = media_file.folder " +
"WHERE music_folder.id IS NULL " +
"AND media_file.folder NOT IN (:excludeFolders) ",
0, args);
}
/**
* Return a list of media file objects whose path don't math their music folder
* @param count maximum number of media file objects to return
* @return a list of media files, sorted by id
*/
public List<MediaFile> getFilesWithMusicFolderMismatch(final int count) {
return query(
"SELECT " + prefix(QUERY_COLUMNS, "media_file") + " FROM media_file " +
"WHERE media_file.path != media_file.folder " +
"AND media_file.path NOT LIKE concat(media_file.folder, '/%') " +
"ORDER BY media_file.id LIMIT ?",
rowMapper, count);
}
/**
* Count the number of media files whose path don't math their music folder
* @return a number of media file rows in the database
*/
public int getFilesWithMusicFolderMismatchCount() {
return queryForInt(
"SELECT count(media_file.id) FROM media_file " +
"WHERE media_file.path != media_file.folder " +
"AND media_file.path NOT LIKE concat(media_file.folder, '/%')",
0);
}
public int getAlbumCount(final List<MusicFolder> musicFolders) {
if (musicFolders.isEmpty()) {
return 0;

@ -266,6 +266,7 @@ internalhelp.title=About {0} Internals
internalhelp.details=Internal details
internalhelp.statistics=Statistics
internalhelp.database=Database
internalhalp.databaseconsistency=Database Consistency
internalhelp.index=Search Index
internalhelp.filesystem=Filesystem
internalhelp.transcoding=Transcoding

@ -151,6 +151,65 @@
<p></p>
<h2>
<img src="<spring:theme code='logImage'/>" alt="">
<span style="vertical-align: middle"><fmt:message key="internalhalp.databaseconsistency"/></span>
</h2>
<table width="75%" class="ruleTable indent">
<tr>
<td colspan="3" class="ruleTableCell">
<c:choose>
<c:when test="${model.dbMediaFilesInNonPresentMusicFoldersCount == 0}">
<img src="<spring:theme code='checkImage'/>" alt="OK">
All media files in the database have a valid music folder.
</c:when>
<c:otherwise>
<img src="<spring:theme code='alertImage'/>" alt="Warning">
The media file database contains files whose music folder is no longer present. Examples are below.
</c:otherwise>
</c:choose>
</td>
</tr>
<tr><td class="ruleTableHeader">dbMediaFilesInNonPresentMusicFoldersCount</td><td class="ruleTableCell" colspan="2">${model.dbMediaFilesInNonPresentMusicFoldersCount}</td></tr>
<c:if test="${model.dbMediaFilesInNonPresentMusicFoldersCount > 0}">
<tr><td class="ruleTableHeader">ID</td><td class="ruleTableHeader">PATH</td><td class="ruleTableHeader">FOLDER</td></tr>
<c:forEach var="invalidFolderSample" items="${model.dbMediaFilesInNonPresentMusicFoldersSample}">
<tr><td class="ruleTableHeader">${invalidFolderSample.id}</td><td class="ruleTableCell">${invalidFolderSample.path}</td><td class="ruleTableCell">${invalidFolderSample.folder}</td></tr>
</c:forEach>
</c:if>
</table>
<p></p>
<table width="75%" class="ruleTable indent">
<tr>
<td colspan="3" class="ruleTableCell">
<c:choose>
<c:when test="${model.dbMediaFilesWithMusicFolderMismatchCount == 0}">
<img src="<spring:theme code='checkImage'/>" alt="OK">
All media files in the database match their music folder.
</c:when>
<c:otherwise>
<img src="<spring:theme code='alertImage'/>" alt="Warning">
The media file database contains files whose path does not match their music folder path. Examples are below.
</c:otherwise>
</c:choose>
</td>
</tr>
<tr><td class="ruleTableHeader">dbMediaFilesWithMusicFolderMismatchCount</td><td class="ruleTableCell" colspan="2">${model.dbMediaFilesWithMusicFolderMismatchCount}</td></tr>
<c:if test="${model.dbMediaFilesWithMusicFolderMismatchCount > 0}">
<tr><td class="ruleTableHeader">ID</td><td class="ruleTableHeader">PATH</td><td class="ruleTableHeader">FOLDER</td></tr>
<c:forEach var="mismatchSample" items="${model.dbMediaFilesWithMusicFolderMismatchSample}">
<tr><td class="ruleTableHeader">${mismatchSample.id}</td><td class="ruleTableCell">${mismatchSample.path}</td><td class="ruleTableCell">${mismatchSample.folder}</td></tr>
</c:forEach>
</c:if>
</table>
<p></p>
<h2>
<img src="<spring:theme code='logImage'/>" alt="">
<span style="vertical-align: middle"><fmt:message key="internalhelp.filesystem"/></span>

Loading…
Cancel
Save