Add "internal help" page with diagnostic information
This commit is contained in:
committed by
jvoisin
parent
cf44bf1743
commit
7a27e08106
+301
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
This file is part of Airsonic.
|
||||
|
||||
Airsonic is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Airsonic is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Airsonic. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2016 (C) Airsonic Authors
|
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
|
||||
*/
|
||||
package org.airsonic.player.controller;
|
||||
|
||||
import org.airsonic.player.dao.DaoHelper;
|
||||
import org.airsonic.player.dao.MusicFolderDao;
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
import org.airsonic.player.service.SecurityService;
|
||||
import org.airsonic.player.service.SettingsService;
|
||||
import org.airsonic.player.service.VersionService;
|
||||
import org.airsonic.player.service.search.AnalyzerFactory;
|
||||
import org.airsonic.player.service.search.IndexManager;
|
||||
import org.airsonic.player.service.search.IndexType;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.input.ReversedLinesFileReader;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Controller for the help page.
|
||||
*
|
||||
* @author Sindre Mehus
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping("/internalhelp")
|
||||
public class InternalHelpController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InternalHelpController.class);
|
||||
|
||||
private static final int LOG_LINES_TO_SHOW = 50;
|
||||
|
||||
public class MusicFolderStatistics {
|
||||
private String name;
|
||||
private String freeFilesystemSizeBytes;
|
||||
private String totalFilesystemSizeBytes;
|
||||
private boolean readable;
|
||||
private boolean writable;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getFreeFilesystemSizeBytes() {
|
||||
return freeFilesystemSizeBytes;
|
||||
}
|
||||
|
||||
public boolean isReadable() {
|
||||
return readable;
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return writable;
|
||||
}
|
||||
|
||||
public String getTotalFilesystemSizeBytes() {
|
||||
return totalFilesystemSizeBytes;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setFreeFilesystemSizeBytes(String freeFilesystemSizeBytes) {
|
||||
this.freeFilesystemSizeBytes = freeFilesystemSizeBytes;
|
||||
}
|
||||
|
||||
public void setReadable(boolean readable) {
|
||||
this.readable = readable;
|
||||
}
|
||||
|
||||
public void setWritable(boolean writable) {
|
||||
this.writable = writable;
|
||||
}
|
||||
|
||||
public void setTotalFilesystemSizeBytes(String totalFilesystemSizeBytes) {
|
||||
this.totalFilesystemSizeBytes = totalFilesystemSizeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private VersionService versionService;
|
||||
@Autowired
|
||||
private SettingsService settingsService;
|
||||
@Autowired
|
||||
private SecurityService securityService;
|
||||
@Autowired
|
||||
private IndexManager indexManager;
|
||||
@Autowired
|
||||
private DaoHelper daoHelper;
|
||||
@Autowired
|
||||
private AnalyzerFactory analyzerFactory;
|
||||
@Autowired
|
||||
private MusicFolderDao musicFolderDao;
|
||||
|
||||
@GetMapping
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
|
||||
if (versionService.isNewFinalVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestFinalVersion());
|
||||
} else if (versionService.isNewBetaVersionAvailable()) {
|
||||
map.put("newVersionAvailable", true);
|
||||
map.put("latestVersion", versionService.getLatestBetaVersion());
|
||||
}
|
||||
|
||||
long totalMemory = Runtime.getRuntime().totalMemory();
|
||||
long freeMemory = Runtime.getRuntime().freeMemory();
|
||||
|
||||
String serverInfo = request.getSession().getServletContext().getServerInfo() +
|
||||
", java " + System.getProperty("java.version") +
|
||||
", " + System.getProperty("os.name");
|
||||
|
||||
// Airsonic scan statistics
|
||||
map.put("statAlbumCount", indexManager.getStatistics().getAlbumCount());
|
||||
map.put("statArtistCount", indexManager.getStatistics().getArtistCount());
|
||||
map.put("statSongCount", indexManager.getStatistics().getSongCount());
|
||||
map.put("statLastScanDate", indexManager.getStatistics().getScanDate());
|
||||
map.put("statTotalDurationSeconds", indexManager.getStatistics().getTotalDurationInSeconds());
|
||||
map.put("statTotalLengthBytes", FileUtils.byteCountToDisplaySize(indexManager.getStatistics().getTotalLengthInBytes()));
|
||||
|
||||
// Lucene index statistics
|
||||
try (IndexReader reader = indexManager.getSearcher(IndexType.SONG).getIndexReader()) {
|
||||
map.put("indexSongCount", reader.numDocs());
|
||||
map.put("indexSongDeletedCount", reader.numDeletedDocs());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
try (IndexReader reader = indexManager.getSearcher(IndexType.ALBUM).getIndexReader()) {
|
||||
map.put("indexAlbumCount", reader.numDocs());
|
||||
map.put("indexAlbumDeletedCount", reader.numDeletedDocs());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
try (IndexReader reader = indexManager.getSearcher(IndexType.ARTIST).getIndexReader()) {
|
||||
map.put("indexArtistCount", reader.numDocs());
|
||||
map.put("indexArtistDeletedCount", reader.numDeletedDocs());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
try (IndexReader reader = indexManager.getSearcher(IndexType.ALBUM_ID3).getIndexReader()) {
|
||||
map.put("indexAlbumId3Count", reader.numDocs());
|
||||
map.put("indexAlbumId3DeletedCount", reader.numDeletedDocs());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
try (IndexReader reader = indexManager.getSearcher(IndexType.ARTIST_ID3).getIndexReader()) {
|
||||
map.put("indexArtistId3Count", reader.numDocs());
|
||||
map.put("indexArtistId3DeletedCount", reader.numDeletedDocs());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
try (Analyzer analyzer = analyzerFactory.getAnalyzer()) {
|
||||
map.put("indexLuceneVersion", analyzer.getVersion().toString());
|
||||
} catch (IOException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
|
||||
// Database statistics
|
||||
try (Connection conn = daoHelper.getDataSource().getConnection()) {
|
||||
map.put("dbDriverName", conn.getMetaData().getDriverName());
|
||||
map.put("dbDriverVersion", conn.getMetaData().getDriverVersion());
|
||||
} catch (SQLException e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
File dbDirectory = new File(settingsService.getAirsonicHome(), "db");
|
||||
map.put("dbDirectorySizeBytes", dbDirectory.exists() ? FileUtils.sizeOfDirectory(dbDirectory) : 0);
|
||||
map.put("dbDirectorySize", FileUtils.byteCountToDisplaySize((long) map.get("dbDirectorySizeBytes")));
|
||||
File dbLogFile = new File(dbDirectory, "airsonic.log");
|
||||
map.put("dbLogSizeBytes", dbLogFile.exists() ? dbLogFile.length() : 0);
|
||||
map.put("dbLogSize", FileUtils.byteCountToDisplaySize((long) map.get("dbLogSizeBytes")));
|
||||
SortedMap<String, Long> dbTableCount = new TreeMap<>();
|
||||
try {
|
||||
for (String tableName : daoHelper.getJdbcTemplate().queryForList("SELECT table_name FROM INFORMATION_SCHEMA.SYSTEM_TABLES WHERE table_schem = 'PUBLIC'", String.class)) {
|
||||
try {
|
||||
Long tableCount = daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM %s", tableName), Long.class);
|
||||
dbTableCount.put(tableName, tableCount);
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Unable to gather information", e);
|
||||
}
|
||||
|
||||
map.put("dbMediaFileMusicNonPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE NOT present AND type = 'MUSIC'"), Long.class));
|
||||
map.put("dbMediaFilePodcastNonPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE NOT present AND type = 'PODCAST'"), Long.class));
|
||||
map.put("dbMediaFileDirectoryNonPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE NOT present AND type = 'DIRECTORY'"), Long.class));
|
||||
map.put("dbMediaFileAlbumNonPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE NOT present AND type = 'ALBUM'"), Long.class));
|
||||
|
||||
map.put("dbMediaFileMusicPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE present AND type = 'MUSIC'"), Long.class));
|
||||
map.put("dbMediaFilePodcastPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE present AND type = 'PODCAST'"), Long.class));
|
||||
map.put("dbMediaFileDirectoryPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE present AND type = 'DIRECTORY'"), Long.class));
|
||||
map.put("dbMediaFileAlbumPresentCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(*) FROM MEDIA_FILE WHERE present AND type = 'ALBUM'"), Long.class));
|
||||
|
||||
map.put("dbMediaFileDistinctAlbumCount", daoHelper.getJdbcTemplate().queryForObject(String.format("SELECT count(DISTINCT album) FROM MEDIA_FILE WHERE present"), Long.class));
|
||||
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("dbTableCount", dbTableCount);
|
||||
|
||||
// Filesystem statistics
|
||||
map.put("fsHomeDirectorySizeBytes", FileUtils.sizeOfDirectory(settingsService.getAirsonicHome()));
|
||||
map.put("fsHomeDirectorySize", FileUtils.byteCountToDisplaySize((long)map.get("fsHomeDirectorySizeBytes")));
|
||||
map.put("fsHomeTotalSpaceBytes", settingsService.getAirsonicHome().getTotalSpace());
|
||||
map.put("fsHomeTotalSpace", FileUtils.byteCountToDisplaySize((long)map.get("fsHomeTotalSpaceBytes")));
|
||||
map.put("fsHomeUsableSpaceBytes", settingsService.getAirsonicHome().getUsableSpace());
|
||||
map.put("fsHomeUsableSpace", FileUtils.byteCountToDisplaySize((long)map.get("fsHomeUsableSpaceBytes")));
|
||||
SortedMap<String, MusicFolderStatistics> fsMusicFolderStatistics = new TreeMap<>();
|
||||
for (MusicFolder folder: musicFolderDao.getAllMusicFolders()) {
|
||||
MusicFolderStatistics stat = new MusicFolderStatistics();
|
||||
stat.setName(folder.getName());
|
||||
stat.setFreeFilesystemSizeBytes(FileUtils.byteCountToDisplaySize(folder.getPath().getUsableSpace()));
|
||||
stat.setTotalFilesystemSizeBytes(FileUtils.byteCountToDisplaySize(folder.getPath().getTotalSpace()));
|
||||
stat.setReadable(Files.isReadable(folder.getPath().toPath()));
|
||||
stat.setWritable(Files.isWritable(folder.getPath().toPath()));
|
||||
fsMusicFolderStatistics.put(folder.getName(), stat);
|
||||
}
|
||||
map.put("fsMusicFolderStatistics", fsMusicFolderStatistics);
|
||||
|
||||
// OS information
|
||||
map.put("localeDefault", Locale.getDefault());
|
||||
map.put("localeUserLanguage", System.getProperty("user.language"));
|
||||
map.put("localeUserCountry", System.getProperty("user.country"));
|
||||
map.put("localeFileEncoding", System.getProperty("file.encoding"));
|
||||
map.put("localeSunJnuEncoding", System.getProperty("sun.jnu.encoding"));
|
||||
map.put("localeSunIoUnicodeEncoding", System.getProperty("sun.io.unicode.encoding"));
|
||||
map.put("localeLang", System.getenv("LANG"));
|
||||
map.put("localeLcAll", System.getenv("LC_ALL"));
|
||||
map.put("localeDefaultCharset", Charset.defaultCharset());
|
||||
|
||||
map.put("user", securityService.getCurrentUser(request));
|
||||
map.put("brand", settingsService.getBrand());
|
||||
map.put("localVersion", versionService.getLocalVersion());
|
||||
map.put("buildDate", versionService.getLocalBuildDate());
|
||||
map.put("buildNumber", versionService.getLocalBuildNumber());
|
||||
map.put("serverInfo", serverInfo);
|
||||
map.put("usedMemory", totalMemory - freeMemory);
|
||||
map.put("totalMemory", totalMemory);
|
||||
File logFile = SettingsService.getLogFile();
|
||||
List<String> latestLogEntries = getLatestLogEntries(logFile);
|
||||
map.put("logEntries", latestLogEntries);
|
||||
map.put("logFile", logFile);
|
||||
|
||||
return new ModelAndView("internalhelp","model",map);
|
||||
}
|
||||
|
||||
private static List<String> getLatestLogEntries(File logFile) {
|
||||
List<String> lines = new ArrayList<>(LOG_LINES_TO_SHOW);
|
||||
try (ReversedLinesFileReader reader = new ReversedLinesFileReader(logFile, Charset.defaultCharset())) {
|
||||
String current;
|
||||
while ((current = reader.readLine()) != null) {
|
||||
if (lines.size() >= LOG_LINES_TO_SHOW) {
|
||||
break;
|
||||
}
|
||||
lines.add(0, current);
|
||||
}
|
||||
return lines;
|
||||
} catch (IOException e) {
|
||||
LOG.warn("Could not open log file " + logFile, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public class GlobalSecurityConfig extends GlobalAuthenticationConfigurerAdapter
|
||||
.antMatchers("/personalSettings*", "/passwordSettings*",
|
||||
"/playerSettings*", "/shareSettings*", "/passwordSettings*")
|
||||
.hasRole("SETTINGS")
|
||||
.antMatchers("/generalSettings*", "/advancedSettings*", "/userSettings*",
|
||||
.antMatchers("/generalSettings*", "/advancedSettings*", "/userSettings*", "/internalhelp*",
|
||||
"/musicFolderSettings*", "/databaseSettings*", "/transcodeSettings*", "/rest/startScan*")
|
||||
.hasRole("ADMIN")
|
||||
.antMatchers("/deletePlaylist*", "/savePlaylist*")
|
||||
|
||||
+16
@@ -262,6 +262,22 @@ upload.empty=No files to upload.
|
||||
upload.failed=Uploading failed with the following error:<br><b>"{0}"</b>
|
||||
upload.unzipped=Unzipped {0}
|
||||
|
||||
internalhelp.title=About {0} Internals
|
||||
internalhelp.details=Internal details
|
||||
internalhelp.statistics=Statistics
|
||||
internalhelp.database=Database
|
||||
internalhelp.index=Search Index
|
||||
internalhelp.filesystem=Filesystem
|
||||
internalhelp.musicfolders=Music Folders
|
||||
internalhelp.locale=Locale
|
||||
internalhelp.defaultlocale=Default locale
|
||||
internalhelp.defaultcharset=Default charset encoding
|
||||
internalhelp.folderisreadable=Folder "{0}": Read access
|
||||
internalhelp.folderiswritable=Folder "{0}": Write access
|
||||
internalhelp.folderfsusage=Folder "{0}": Free space on filesystem
|
||||
internalhelp.fsusage=Airsonic Home: Free space on filesystem
|
||||
internalhelp.fshomesize=Airsonic Home: Directory size
|
||||
|
||||
help.title=About {0}
|
||||
help.upgrade=New version available. Download {0} {1} <a href="#" onclick="window.open(''https://airsonic.github.io/docs/update/'')">here</a>.
|
||||
help.version.title=Version
|
||||
|
||||
@@ -62,3 +62,5 @@ userImage = icons/default_dark/user.svg
|
||||
viewAsGridImage = icons/default_dark/view_as_grid.png
|
||||
viewAsListImage = icons/default_dark/view_as_list.png
|
||||
volumeImage = icons/default_dark/volume.png
|
||||
alertImage = icons/default_dark/alert.svg
|
||||
checkImage = icons/default_dark/check.svg
|
||||
|
||||
@@ -62,3 +62,5 @@ userImage = icons/default_light/user.svg
|
||||
viewAsGridImage = icons/default_light/view_as_grid.svg
|
||||
viewAsListImage = icons/default_light/view_as_list.svg
|
||||
volumeImage = icons/default_light/volume.svg
|
||||
alertImage = icons/default_light/alert.svg
|
||||
checkImage = icons/default_light/check.svg
|
||||
|
||||
@@ -64,5 +64,6 @@
|
||||
<p><fmt:message key="help.logfile"><fmt:param value="${model.logFile}"/></fmt:message> </p>
|
||||
|
||||
<div class="forward"><a href="help.view?"><fmt:message key="common.refresh"/></a></div>
|
||||
<div class="forward"><a href="internalhelp.view?"><fmt:message key="common.more"/></a></div>
|
||||
|
||||
</body></html>
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
<!DOCTYPE html>
|
||||
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1"%>
|
||||
|
||||
<html><head>
|
||||
<%@ include file="head.jsp" %>
|
||||
<script type="text/javascript" src="<c:url value='/script/utils.js'/>"></script>
|
||||
</head>
|
||||
<body class="mainframe bgcolor1">
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${empty model.buildDate}">
|
||||
<fmt:message key="common.unknown" var="buildDateString"/>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<fmt:formatDate value="${model.buildDate}" dateStyle="long" var="buildDateString"/>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<c:choose>
|
||||
<c:when test="${empty model.localVersion}">
|
||||
<fmt:message key="common.unknown" var="versionString"/>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<c:set var="versionString" value="${model.localVersion}"/>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
|
||||
<h1>
|
||||
<img src="<spring:theme code='helpImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="internalhelp.title"><fmt:param value="${model.brand}"/></fmt:message></span>
|
||||
</h1>
|
||||
|
||||
<c:if test="${model.newVersionAvailable}">
|
||||
<p class="warning"><fmt:message key="help.upgrade"><fmt:param value="${model.brand}"/><fmt:param value="${model.latestVersion}"/></fmt:message></p>
|
||||
</c:if>
|
||||
|
||||
<table width="75%" class="ruleTable indent">
|
||||
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="help.version.title"/></td><td class="ruleTableCell">${versionString} – ${buildDateString}</td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="help.server.title"/></td><td class="ruleTableCell">${model.serverInfo} (<sub:formatBytes bytes="${model.usedMemory}"/> / <sub:formatBytes bytes="${model.totalMemory}"/>)</td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="help.license.title"/></td><td class="ruleTableCell">
|
||||
<a href="http://www.gnu.org/copyleft/gpl.html" target="_blank"><img style="float:right;margin-left: 10px" alt="GPL 3.0" src="<c:url value='/icons/default_light/gpl.png'/>"></a>
|
||||
<fmt:message key="help.license.text"><fmt:param value="${model.brand}"/></fmt:message></td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="help.homepage.title"/></td><td class="ruleTableCell"><a target="_blank" href="https://airsonic.github.io/" rel="noopener nofererrer">Airsonic website</a></td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="help.forum.title"/></td><td class="ruleTableCell"><a target="_blank" href="https://www.reddit.com/r/airsonic" rel="noopener noreferrer">Airsonic on Reddit</a></td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="help.contact.title"/></td><td class="ruleTableCell"><fmt:message key="help.contact.text"><fmt:param value="${model.brand}"/></fmt:message></td></tr>
|
||||
</table>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h2>
|
||||
<img src="<spring:theme code='logImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="internalhelp.statistics"/></span>
|
||||
</h2>
|
||||
|
||||
<table width="75%" class="ruleTable indent">
|
||||
<tr><td class="ruleTableHeader">statAlbumCount</td><td class="ruleTableCell">${model.statAlbumCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">statArtistCount</td><td class="ruleTableCell">${model.statArtistCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">statSongCount</td><td class="ruleTableCell">${model.statSongCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">statLastScanDate</td><td class="ruleTableCell">${model.statLastScanDate}</td></tr>
|
||||
<tr><td class="ruleTableHeader">statTotalDurationSeconds</td><td class="ruleTableCell">${model.statTotalDurationSeconds}</td></tr>
|
||||
<tr><td class="ruleTableHeader">statTotalLengthBytes</td><td class="ruleTableCell">${model.statTotalLengthBytes}</td></tr>
|
||||
</table>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h2>
|
||||
<img src="<spring:theme code='logImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="internalhelp.index"/></span>
|
||||
</h2>
|
||||
|
||||
<table width="75%" class="ruleTable indent">
|
||||
<tr><td class="ruleTableHeader">indexLuceneVersion</td><td class="ruleTableCell">${model.indexLuceneVersion}</td></tr>
|
||||
|
||||
|
||||
<tr><td class="ruleTableHeader">indexSongDeletedCount</td><td class="ruleTableCell">${model.indexSongDeletedCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexAlbumDeletedCount</td><td class="ruleTableCell">${model.indexAlbumDeletedCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexArtistDeletedCount</td><td class="ruleTableCell">${model.indexArtistDeletedCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexAlbumId3DeletedCount</td><td class="ruleTableCell">${model.indexAlbumId3DeletedCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexArtistId3DeletedCount</td><td class="ruleTableCell">${model.indexArtistId3DeletedCount}</td></tr>
|
||||
|
||||
<tr><td class="ruleTableHeader">indexSongCount</td><td class="ruleTableCell">${model.indexSongCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexAlbumCount</td><td class="ruleTableCell">${model.indexAlbumCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexArtistCount</td><td class="ruleTableCell">${model.indexArtistCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexAlbumId3Count</td><td class="ruleTableCell">${model.indexAlbumId3Count}</td></tr>
|
||||
<tr><td class="ruleTableHeader">indexArtistId3Count</td><td class="ruleTableCell">${model.indexArtistId3Count}</td></tr>
|
||||
</table>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h2>
|
||||
<img src="<spring:theme code='logImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="internalhelp.database"/></span>
|
||||
</h2>
|
||||
|
||||
<table width="75%" class="ruleTable indent">
|
||||
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<c:choose>
|
||||
<c:when test="${model.dbLogSizeBytes < 268435456}">
|
||||
<img src="<spring:theme code='checkImage'/>" alt="OK">
|
||||
The database log file (db/airsonic.log) appears healthy.
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
The database log file (db/airsonic.log) is large (greater than 256M). Run a scan to clean it up.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="ruleTableHeader">dbDriverName</td><td class="ruleTableCell">${model.dbDriverName}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbDriverVersion</td><td class="ruleTableCell">${model.dbDriverVersion}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbDirectorySize</td><td class="ruleTableCell">${model.dbDirectorySize}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbLogSize</td><td class="ruleTableCell">${model.dbLogSize}</td></tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<c:choose>
|
||||
<c:when test="${model.dbMediaFileAlbumNonPresentCount + model.dbMediaFileDirectoryNonPresentCount + model.dbMediaFileMusicNonPresentCount + model.dbMediaFilePodcastNonPresentCount == 0}">
|
||||
<img src="<spring:theme code='checkImage'/>" alt="OK">
|
||||
The database does not contain non-present items.
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
The database contains non-present items. Run "clean-up database" to clean them up.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><td class="ruleTableHeader">dbMediaFileMusicNonPresentCount</td><td class="ruleTableCell">${model.dbMediaFileMusicNonPresentCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFilePodcastNonPresentCount</td><td class="ruleTableCell">${model.dbMediaFilePodcastNonPresentCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFileDirectoryNonPresentCount</td><td class="ruleTableCell">${model.dbMediaFileDirectoryNonPresentCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFileAlbumNonPresentCount</td><td class="ruleTableCell">${model.dbMediaFileAlbumNonPresentCount}</td></tr>
|
||||
|
||||
<tr><td class="ruleTableHeader">dbMediaFileMusicPresentCount</td><td class="ruleTableCell">${model.dbMediaFileMusicPresentCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFilePodcastPresentCount</td><td class="ruleTableCell">${model.dbMediaFilePodcastPresentCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFileDirectoryPresentCount</td><td class="ruleTableCell">${model.dbMediaFileDirectoryPresentCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFileAlbumPresentCount</td><td class="ruleTableCell">${model.dbMediaFileAlbumPresentCount}</td></tr>
|
||||
|
||||
<tr><td class="ruleTableHeader">dbMediaFileDistinctAlbumCount</td><td class="ruleTableCell">${model.dbMediaFileDistinctAlbumCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFileDistinctArtistCount</td><td class="ruleTableCell">${model.dbMediaFileDistinctArtistCount}</td></tr>
|
||||
<tr><td class="ruleTableHeader">dbMediaFileDistinctAlbumArtistCount</td><td class="ruleTableCell">${model.dbMediaFileDistinctAlbumArtistCount}</td></tr>
|
||||
|
||||
<c:forEach var="tableCount" items="${model.dbTableCount}">
|
||||
<tr><td class="ruleTableHeader">${tableCount.key} count</td><td class="ruleTableCell">${tableCount.value}</td></tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h2>
|
||||
<img src="<spring:theme code='logImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="internalhelp.filesystem"/></span>
|
||||
</h2>
|
||||
|
||||
<table width="75%" class="ruleTable indent">
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.fsusage"/></td><td class="ruleTableCell">${model.fsHomeUsableSpace} / ${model.fsHomeTotalSpace}</td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.fshomesize"/></td><td class="ruleTableCell">${model.fsHomeDirectorySize}</td></tr>
|
||||
<c:forEach var="musicFolderStatistics" items="${model.fsMusicFolderStatistics}">
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<c:choose>
|
||||
<c:when test="${musicFolderStatistics.value.readable}">
|
||||
<img src="<spring:theme code='checkImage'/>" alt="OK">
|
||||
Airsonic appears to have the correct permissions for music folder "${musicFolderStatistics.key}".
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
Airsonic does not have the correct permissions for music folder "${musicFolderStatistics.key}".
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.folderisreadable"><fmt:param value="${musicFolderStatistics.key}"/></fmt:message></td><td class="ruleTableCell">${musicFolderStatistics.value.readable}</td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.folderiswritable"><fmt:param value="${musicFolderStatistics.key}"/></fmt:message></td><td class="ruleTableCell">${musicFolderStatistics.value.writable}</td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.folderfsusage"><fmt:param value="${musicFolderStatistics.key}"/></fmt:message></td><td class="ruleTableCell">${musicFolderStatistics.value.freeFilesystemSizeBytes} / ${musicFolderStatistics.value.totalFilesystemSizeBytes}</td></tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h2>
|
||||
<img src="<spring:theme code='logImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="internalhelp.locale"/></span>
|
||||
</h2>
|
||||
|
||||
<table width="75%" class="ruleTable indent">
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<c:choose>
|
||||
<c:when test="${fn:contains(model.localeDefaultCharset, 'UTF-8')}">
|
||||
<img src="<spring:theme code='checkImage'/>" alt="OK">
|
||||
Java default charset appears to have UTF-8 support.
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
Java default charset appears to have no UTF-8 support. International characters may be partially supported.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.defaultcharset"/></td><td class="ruleTableCell">${model.localeDefaultCharset}</td></tr>
|
||||
<tr><td class="ruleTableHeader"><fmt:message key="internalhelp.defaultlocale"/></td><td class="ruleTableCell">${model.localeDefault}</td></tr>
|
||||
<tr><td class="ruleTableHeader">user.language</td><td class="ruleTableCell">${model.localeUserLanguage}</td></tr>
|
||||
<tr><td class="ruleTableHeader">user.country</td><td class="ruleTableCell">${model.localeUserCountry}</td></tr>
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<c:choose>
|
||||
<c:when test="${fn:contains(model.localeFileEncoding, 'UTF-8')}">
|
||||
<img src="<spring:theme code='checkImage'/>" alt="OK">
|
||||
Java file encoding appears to have UTF-8 support.
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
Java file encoding appears to have no UTF-8 support. International characters may be partially supported.
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td class="ruleTableHeader">file.encoding</td><td class="ruleTableCell">${model.localeFileEncoding}</td></tr>
|
||||
<tr><td class="ruleTableHeader">sun.jnu.encoding</td><td class="ruleTableCell">${model.localeSunJnuEncoding}</td></tr>
|
||||
<tr><td class="ruleTableHeader">sun.io.unicode.encoding</td><td class="ruleTableCell">${model.localeSunIoUnicodeEncoding}</td></tr>
|
||||
|
||||
<c:if test="${not empty model.localeLang and not fn:contains(model.localeLang, 'UTF-8')}">
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
The LANG environment variable is defined but appears to disable UTF-8 support. International characters may be partially supported.
|
||||
</td>
|
||||
</tr>
|
||||
</c:if>
|
||||
<tr><td class="ruleTableHeader">LANG</td><td class="ruleTableCell">${model.localeLang}</td></tr>
|
||||
|
||||
<c:if test="${not empty model.localeLcAll and not fn:contains(model.localeLcAll, 'UTF-8')}">
|
||||
<tr>
|
||||
<td colspan="2" class="ruleTableCell">
|
||||
<img src="<spring:theme code='alertImage'/>" alt="Warning">
|
||||
The LC_ALL environment variable is defined but appears to disable UTF-8 support. International characters may be partially supported.
|
||||
</td>
|
||||
</tr>
|
||||
</c:if>
|
||||
<tr><td class="ruleTableHeader">LC_ALL</td><td class="ruleTableCell">${model.localeLcAll}</td></tr>
|
||||
</table>
|
||||
|
||||
<p></p>
|
||||
|
||||
<h2>
|
||||
<img src="<spring:theme code='logImage'/>" alt="">
|
||||
<span style="vertical-align: middle"><fmt:message key="help.log"/></span>
|
||||
</h2>
|
||||
|
||||
<table cellpadding="2" class="log indent">
|
||||
<c:forEach items="${model.logEntries}" var="entry">
|
||||
<tr>
|
||||
<td>${fn:escapeXml(entry)}</td>
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</table>
|
||||
|
||||
<p><fmt:message key="help.logfile"><fmt:param value="${model.logFile}"/></fmt:message> </p>
|
||||
|
||||
<div class="forward"><a href="internalhelp.view?"><fmt:message key="common.refresh"/></a></div>
|
||||
|
||||
</body></html>
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="orange" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-triangle"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
||||
|
After Width: | Height: | Size: 418 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 255 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="orange" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-alert-triangle"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
||||
|
After Width: | Height: | Size: 418 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||
|
After Width: | Height: | Size: 255 B |
Reference in New Issue
Block a user