Add support for ListenBrainz Audio Scrobbler Service
- Add textbox in user settings page for ListenBrainz token
- Add changes to db
- Add db colume to store MusicBrainz Recording ID
- Add db colume to store ListenBrainz token
- Add test for reading id
- Add tag on testing file
- Add localization entry
Signed-off-by: Shen-Ta Hsieh <ibmibmibm.tw@gmail.com>
Signed-off-by: Andrew DeMaria <lostonamountain@gmail.com>
This commit is contained in:
@@ -53,9 +53,11 @@ public class PersonalSettingsCommand {
|
||||
private boolean songNotificationEnabled;
|
||||
private boolean queueFollowingSongs;
|
||||
private boolean lastFmEnabled;
|
||||
private boolean listenBrainzEnabled;
|
||||
private int paginationSize;
|
||||
private String lastFmUsername;
|
||||
private String lastFmPassword;
|
||||
private String listenBrainzToken;
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
@@ -233,6 +235,14 @@ public class PersonalSettingsCommand {
|
||||
this.lastFmEnabled = lastFmEnabled;
|
||||
}
|
||||
|
||||
public boolean isListenBrainzEnabled() {
|
||||
return listenBrainzEnabled;
|
||||
}
|
||||
|
||||
public void setListenBrainzEnabled(boolean listenBrainzEnabled) {
|
||||
this.listenBrainzEnabled = listenBrainzEnabled;
|
||||
}
|
||||
|
||||
public String getLastFmUsername() {
|
||||
return lastFmUsername;
|
||||
}
|
||||
@@ -249,6 +259,14 @@ public class PersonalSettingsCommand {
|
||||
this.lastFmPassword = lastFmPassword;
|
||||
}
|
||||
|
||||
public String getListenBrainzToken() {
|
||||
return listenBrainzToken;
|
||||
}
|
||||
|
||||
public void setListenBrainzToken(String listenBrainzToken) {
|
||||
this.listenBrainzToken = listenBrainzToken;
|
||||
}
|
||||
|
||||
public boolean isQueueFollowingSongs() {
|
||||
return queueFollowingSongs;
|
||||
}
|
||||
|
||||
+4
@@ -82,6 +82,8 @@ public class PersonalSettingsController {
|
||||
command.setLastFmEnabled(userSettings.isLastFmEnabled());
|
||||
command.setLastFmUsername(userSettings.getLastFmUsername());
|
||||
command.setLastFmPassword(userSettings.getLastFmPassword());
|
||||
command.setListenBrainzEnabled(userSettings.isListenBrainzEnabled());
|
||||
command.setListenBrainzToken(userSettings.getListenBrainzToken());
|
||||
command.setPaginationSize(userSettings.getPaginationSize());
|
||||
|
||||
Locale currentLocale = userSettings.getLocale();
|
||||
@@ -148,6 +150,8 @@ public class PersonalSettingsController {
|
||||
settings.setKeyboardShortcutsEnabled(command.isKeyboardShortcutsEnabled());
|
||||
settings.setLastFmEnabled(command.isLastFmEnabled());
|
||||
settings.setLastFmUsername(command.getLastFmUsername());
|
||||
settings.setListenBrainzEnabled(command.isListenBrainzEnabled());
|
||||
settings.setListenBrainzToken(command.getListenBrainzToken());
|
||||
settings.setSystemAvatarId(getSystemAvatarId(command));
|
||||
settings.setAvatarScheme(getAvatarScheme(command));
|
||||
settings.setPaginationSize(command.getPaginationSize());
|
||||
|
||||
@@ -46,7 +46,7 @@ public class MediaFileDao extends AbstractDao {
|
||||
private static final String INSERT_COLUMNS = "path, folder, type, format, title, album, artist, album_artist, disc_number, " +
|
||||
"track_number, year, genre, bit_rate, variable_bit_rate, duration_seconds, file_size, width, height, cover_art_path, " +
|
||||
"parent_path, play_count, last_played, comment, created, changed, last_scanned, children_last_updated, present, " +
|
||||
"version, mb_release_id";
|
||||
"version, mb_release_id, mb_recording_id";
|
||||
|
||||
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
|
||||
private static final String GENRE_COLUMNS = "name, song_count, album_count";
|
||||
@@ -162,7 +162,8 @@ public class MediaFileDao extends AbstractDao {
|
||||
"children_last_updated=?," +
|
||||
"present=?, " +
|
||||
"version=?, " +
|
||||
"mb_release_id=? " +
|
||||
"mb_release_id=?, " +
|
||||
"mb_recording_id=? " +
|
||||
"where path=?";
|
||||
|
||||
LOG.trace("Updating media file {}", Util.debugObject(file));
|
||||
@@ -173,7 +174,7 @@ public class MediaFileDao extends AbstractDao {
|
||||
file.isVariableBitRate(), file.getDurationSeconds(), file.getFileSize(), file.getWidth(), file.getHeight(),
|
||||
file.getCoverArtPath(), file.getParentPath(), file.getPlayCount(), file.getLastPlayed(), file.getComment(),
|
||||
file.getChanged(), file.getLastScanned(), file.getChildrenLastUpdated(), file.isPresent(), VERSION,
|
||||
file.getMusicBrainzReleaseId(), file.getPath());
|
||||
file.getMusicBrainzReleaseId(), file.getMusicBrainzRecordingId(), file.getPath());
|
||||
|
||||
if (n == 0) {
|
||||
|
||||
@@ -191,7 +192,7 @@ public class MediaFileDao extends AbstractDao {
|
||||
file.isVariableBitRate(), file.getDurationSeconds(), file.getFileSize(), file.getWidth(), file.getHeight(),
|
||||
file.getCoverArtPath(), file.getParentPath(), file.getPlayCount(), file.getLastPlayed(), file.getComment(),
|
||||
file.getCreated(), file.getChanged(), file.getLastScanned(),
|
||||
file.getChildrenLastUpdated(), file.isPresent(), VERSION, file.getMusicBrainzReleaseId());
|
||||
file.getChildrenLastUpdated(), file.isPresent(), VERSION, file.getMusicBrainzReleaseId(), file.getMusicBrainzRecordingId());
|
||||
}
|
||||
|
||||
int id = queryForInt("select id from media_file where path=?", null, file.getPath());
|
||||
@@ -721,7 +722,8 @@ public class MediaFileDao extends AbstractDao {
|
||||
rs.getTimestamp(28),
|
||||
rs.getBoolean(29),
|
||||
rs.getInt(30),
|
||||
rs.getString(31));
|
||||
rs.getString(31),
|
||||
rs.getString(32));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ public class UserDao extends AbstractDao {
|
||||
"main_year, main_bit_rate, main_duration, main_format, main_file_size, " +
|
||||
"playlist_track_number, playlist_artist, playlist_album, playlist_genre, " +
|
||||
"playlist_year, playlist_bit_rate, playlist_duration, playlist_format, playlist_file_size, " +
|
||||
"last_fm_enabled, last_fm_username, last_fm_password, transcode_scheme, show_now_playing, selected_music_folder_id, " +
|
||||
"last_fm_enabled, last_fm_username, last_fm_password, listenbrainz_enabled, listenbrainz_token, " +
|
||||
"transcode_scheme, show_now_playing, selected_music_folder_id, " +
|
||||
"party_mode_enabled, now_playing_allowed, avatar_scheme, system_avatar_id, changed, show_artist_info, auto_hide_play_queue, " +
|
||||
"view_as_list, default_album_list, queue_following_songs, show_side_bar, list_reload_delay, " +
|
||||
"keyboard_shortcuts_enabled, pagination_size";
|
||||
@@ -219,6 +220,7 @@ public class UserDao extends AbstractDao {
|
||||
playlist.isGenreVisible(), playlist.isYearVisible(), playlist.isBitRateVisible(), playlist.isDurationVisible(),
|
||||
playlist.isFormatVisible(), playlist.isFileSizeVisible(),
|
||||
settings.isLastFmEnabled(), settings.getLastFmUsername(), encrypt(settings.getLastFmPassword()),
|
||||
settings.isListenBrainzEnabled(), settings.getListenBrainzToken(),
|
||||
settings.getTranscodeScheme().name(), settings.isShowNowPlayingEnabled(),
|
||||
settings.getSelectedMusicFolderId(), settings.isPartyModeEnabled(), settings.isNowPlayingAllowed(),
|
||||
settings.getAvatarScheme().name(), settings.getSystemAvatarId(), settings.getChanged(),
|
||||
@@ -370,6 +372,9 @@ public class UserDao extends AbstractDao {
|
||||
settings.setLastFmUsername(rs.getString(col++));
|
||||
settings.setLastFmPassword(decrypt(rs.getString(col++)));
|
||||
|
||||
settings.setListenBrainzEnabled(rs.getBoolean(col++));
|
||||
settings.setListenBrainzToken(rs.getString(col++));
|
||||
|
||||
settings.setTranscodeScheme(TranscodeScheme.valueOf(rs.getString(col++)));
|
||||
settings.setShowNowPlayingEnabled(rs.getBoolean(col++));
|
||||
settings.setSelectedMusicFolderId(rs.getInt(col++));
|
||||
|
||||
@@ -69,12 +69,13 @@ public class MediaFile {
|
||||
private boolean present;
|
||||
private int version;
|
||||
private String musicBrainzReleaseId;
|
||||
private String musicBrainzRecordingId;
|
||||
|
||||
public MediaFile(int id, String path, String folder, MediaType mediaType, String format, String title,
|
||||
String albumName, String artist, String albumArtist, Integer discNumber, Integer trackNumber, Integer year, String genre, Integer bitRate,
|
||||
boolean variableBitRate, Integer durationSeconds, Long fileSize, Integer width, Integer height, String coverArtPath,
|
||||
String parentPath, int playCount, Date lastPlayed, String comment, Date created, Date changed, Date lastScanned,
|
||||
Date childrenLastUpdated, boolean present, int version, String musicBrainzReleaseId) {
|
||||
Date childrenLastUpdated, boolean present, int version, String musicBrainzReleaseId, String musicBrainzRecordingId) {
|
||||
this.id = id;
|
||||
this.path = path;
|
||||
this.folder = folder;
|
||||
@@ -106,6 +107,7 @@ public class MediaFile {
|
||||
this.present = present;
|
||||
this.version = version;
|
||||
this.musicBrainzReleaseId = musicBrainzReleaseId;
|
||||
this.musicBrainzRecordingId = musicBrainzRecordingId;
|
||||
}
|
||||
|
||||
public MediaFile() {
|
||||
@@ -393,6 +395,14 @@ public class MediaFile {
|
||||
this.musicBrainzReleaseId = musicBrainzReleaseId;
|
||||
}
|
||||
|
||||
public String getMusicBrainzRecordingId() {
|
||||
return musicBrainzRecordingId;
|
||||
}
|
||||
|
||||
public void setMusicBrainzRecordingId(String musicBrainzRecordingId) {
|
||||
this.musicBrainzRecordingId = musicBrainzRecordingId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns when the children was last updated in the database.
|
||||
*/
|
||||
|
||||
@@ -46,8 +46,10 @@ public class UserSettings {
|
||||
private Visibility mainVisibility = new Visibility();
|
||||
private Visibility playlistVisibility = new Visibility();
|
||||
private boolean lastFmEnabled;
|
||||
private boolean listenBrainzEnabled;
|
||||
private String lastFmUsername;
|
||||
private String lastFmPassword;
|
||||
private String listenBrainzToken;
|
||||
private TranscodeScheme transcodeScheme = TranscodeScheme.OFF;
|
||||
private int selectedMusicFolderId = -1;
|
||||
private boolean partyModeEnabled;
|
||||
@@ -149,6 +151,14 @@ public class UserSettings {
|
||||
this.lastFmEnabled = lastFmEnabled;
|
||||
}
|
||||
|
||||
public boolean isListenBrainzEnabled() {
|
||||
return listenBrainzEnabled;
|
||||
}
|
||||
|
||||
public void setListenBrainzEnabled(boolean listenBrainzEnabled) {
|
||||
this.listenBrainzEnabled = listenBrainzEnabled;
|
||||
}
|
||||
|
||||
public String getLastFmUsername() {
|
||||
return lastFmUsername;
|
||||
}
|
||||
@@ -165,6 +175,14 @@ public class UserSettings {
|
||||
this.lastFmPassword = lastFmPassword;
|
||||
}
|
||||
|
||||
public String getListenBrainzToken() {
|
||||
return listenBrainzToken;
|
||||
}
|
||||
|
||||
public void setListenBrainzToken(String listenBrainzToken) {
|
||||
this.listenBrainzToken = listenBrainzToken;
|
||||
}
|
||||
|
||||
public TranscodeScheme getTranscodeScheme() {
|
||||
return transcodeScheme;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.airsonic.player.service;
|
||||
import org.airsonic.player.domain.MediaFile;
|
||||
import org.airsonic.player.domain.UserSettings;
|
||||
import org.airsonic.player.service.scrobbler.LastFMScrobbler;
|
||||
import org.airsonic.player.service.scrobbler.ListenBrainzScrobbler;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -34,6 +35,7 @@ import java.util.Date;
|
||||
public class AudioScrobblerService {
|
||||
|
||||
private LastFMScrobbler lastFMScrobbler;
|
||||
private ListenBrainzScrobbler listenBrainzScrobbler;
|
||||
@Autowired
|
||||
private SettingsService settingsService;
|
||||
|
||||
@@ -58,6 +60,13 @@ public class AudioScrobblerService {
|
||||
}
|
||||
lastFMScrobbler.register(mediaFile, userSettings.getLastFmUsername(), userSettings.getLastFmPassword(), submission, time);
|
||||
}
|
||||
|
||||
if (userSettings.isListenBrainzEnabled() && userSettings.getListenBrainzToken() != null) {
|
||||
if (listenBrainzScrobbler == null) {
|
||||
listenBrainzScrobbler = new ListenBrainzScrobbler();
|
||||
}
|
||||
listenBrainzScrobbler.register(mediaFile, userSettings.getListenBrainzToken(), submission, time);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
|
||||
@@ -508,6 +508,7 @@ public class MediaFileService {
|
||||
mediaFile.setHeight(metaData.getHeight());
|
||||
mediaFile.setWidth(metaData.getWidth());
|
||||
mediaFile.setMusicBrainzReleaseId(metaData.getMusicBrainzReleaseId());
|
||||
mediaFile.setMusicBrainzRecordingId(metaData.getMusicBrainzRecordingId());
|
||||
}
|
||||
String format = StringUtils.trimToNull(StringUtils.lowerCase(FilenameUtils.getExtension(mediaFile.getPath())));
|
||||
mediaFile.setFormat(format);
|
||||
|
||||
@@ -1121,8 +1121,10 @@ public class SettingsService {
|
||||
settings.setQueueFollowingSongs(true);
|
||||
settings.setDefaultAlbumList(AlbumListType.RANDOM);
|
||||
settings.setLastFmEnabled(false);
|
||||
settings.setListenBrainzEnabled(false);
|
||||
settings.setLastFmUsername(null);
|
||||
settings.setLastFmPassword(null);
|
||||
settings.setListenBrainzToken(null);
|
||||
settings.setChanged(new Date());
|
||||
settings.setPaginationSize(40);
|
||||
|
||||
|
||||
+1
@@ -97,6 +97,7 @@ public class JaudiotaggerParser extends MetaDataParser {
|
||||
metaData.setDiscNumber(parseInteger(getTagField(tag, FieldKey.DISC_NO)));
|
||||
metaData.setTrackNumber(parseTrackNumber(getTagField(tag, FieldKey.TRACK)));
|
||||
metaData.setMusicBrainzReleaseId(getTagField(tag, FieldKey.MUSICBRAINZ_RELEASEID));
|
||||
metaData.setMusicBrainzRecordingId(getTagField(tag, FieldKey.MUSICBRAINZ_TRACK_ID));
|
||||
|
||||
metaData.setArtist(getTagField(tag, FieldKey.ARTIST));
|
||||
metaData.setAlbumArtist(getTagField(tag, FieldKey.ALBUM_ARTIST));
|
||||
|
||||
@@ -39,6 +39,7 @@ public class MetaData {
|
||||
private Integer width;
|
||||
private Integer height;
|
||||
private String musicBrainzReleaseId;
|
||||
private String musicBrainzRecordingId;
|
||||
|
||||
public Integer getDiscNumber() {
|
||||
return discNumber;
|
||||
@@ -151,4 +152,12 @@ public class MetaData {
|
||||
public void setMusicBrainzReleaseId(String musicBrainzReleaseId) {
|
||||
this.musicBrainzReleaseId = musicBrainzReleaseId;
|
||||
}
|
||||
|
||||
public String getMusicBrainzRecordingId() {
|
||||
return musicBrainzRecordingId;
|
||||
}
|
||||
|
||||
public void setMusicBrainzRecordingId(String musicBrainzRecordingId) {
|
||||
this.musicBrainzRecordingId = musicBrainzRecordingId;
|
||||
}
|
||||
}
|
||||
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
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.service.scrobbler;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.airsonic.player.domain.MediaFile;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Provides services for "audioscrobbling" at listenbrainz.org.
|
||||
* <br/>
|
||||
* See https://listenbrainz.readthedocs.io/
|
||||
*/
|
||||
public class ListenBrainzScrobbler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ListenBrainzScrobbler.class);
|
||||
private static final int MAX_PENDING_REGISTRATION = 2000;
|
||||
|
||||
private RegistrationThread thread;
|
||||
private final LinkedBlockingQueue<RegistrationData> queue = new LinkedBlockingQueue<RegistrationData>();
|
||||
private final RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(15000)
|
||||
.setSocketTimeout(15000)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Registers the given media file at listenbrainz.org. This method returns immediately, the actual registration is done
|
||||
* by a separate thread.
|
||||
*
|
||||
* @param mediaFile The media file to register.
|
||||
* @param token The token to authentication user on ListenBrainz.
|
||||
* @param submission Whether this is a submission or a now playing notification.
|
||||
* @param time Event time, or {@code null} to use current time.
|
||||
*/
|
||||
public synchronized void register(MediaFile mediaFile, String token, boolean submission, Date time) {
|
||||
if (thread == null) {
|
||||
thread = new RegistrationThread();
|
||||
thread.start();
|
||||
}
|
||||
|
||||
if (queue.size() >= MAX_PENDING_REGISTRATION) {
|
||||
LOG.warn("ListenBrainz scrobbler queue is full. Ignoring '" + mediaFile.getTitle() + "'");
|
||||
return;
|
||||
}
|
||||
|
||||
RegistrationData registrationData = createRegistrationData(mediaFile, token, submission, time);
|
||||
if (registrationData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
queue.put(registrationData);
|
||||
} catch (InterruptedException x) {
|
||||
LOG.warn("Interrupted while queuing ListenBrainz scrobble: " + x.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private RegistrationData createRegistrationData(MediaFile mediaFile, String token, boolean submission, Date time) {
|
||||
RegistrationData reg = new RegistrationData();
|
||||
reg.token = token;
|
||||
reg.artist = mediaFile.getArtist();
|
||||
reg.album = mediaFile.getAlbumName();
|
||||
reg.title = mediaFile.getTitle();
|
||||
reg.musicBrainzReleaseId = mediaFile.getMusicBrainzReleaseId();
|
||||
reg.musicBrainzRecordingId = mediaFile.getMusicBrainzRecordingId();
|
||||
reg.trackNumber = mediaFile.getTrackNumber();
|
||||
reg.duration = mediaFile.getDurationSeconds() == null ? 0 : mediaFile.getDurationSeconds();
|
||||
reg.time = time == null ? new Date() : time;
|
||||
reg.submission = submission;
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scrobbles the given song data at listenbrainz.org, using the protocol defined at https://listenbrainz.readthedocs.io/en/latest/dev/api.html.
|
||||
*
|
||||
* @param registrationData Registration data for the song.
|
||||
*/
|
||||
private void scrobble(RegistrationData registrationData) throws ClientProtocolException, IOException {
|
||||
if (registrationData == null || registrationData.token == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!submit(registrationData)) {
|
||||
LOG.warn("Failed to scrobble song '" + registrationData.title + "' at ListenBrainz.");
|
||||
} else {
|
||||
LOG.info("Successfully registered " +
|
||||
(registrationData.submission ? "submission" : "now playing") +
|
||||
" for song '" + registrationData.title + "'" +
|
||||
" at ListenBrainz: " + registrationData.time);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns if submission succeeds.
|
||||
*/
|
||||
private boolean submit(RegistrationData registrationData) throws ClientProtocolException, IOException {
|
||||
Map<String, Object> additional_info = new HashMap<String, Object>();
|
||||
additional_info.computeIfAbsent("release_mbid", k -> registrationData.musicBrainzReleaseId);
|
||||
additional_info.computeIfAbsent("recording_mbid", k -> registrationData.musicBrainzRecordingId);
|
||||
additional_info.computeIfAbsent("tracknumber", k -> registrationData.trackNumber);
|
||||
|
||||
Map<String, Object> track_metadata = new HashMap<String, Object>();
|
||||
if (additional_info.size() > 0) {
|
||||
track_metadata.put("additional_info", additional_info);
|
||||
}
|
||||
track_metadata.computeIfAbsent("artist_name", k -> registrationData.artist);
|
||||
track_metadata.computeIfAbsent("track_name", k -> registrationData.title);
|
||||
track_metadata.computeIfAbsent("release_name", k -> registrationData.album);
|
||||
|
||||
Map<String, Object> payload = new HashMap<String, Object>();
|
||||
if (track_metadata.size() > 0) {
|
||||
payload.put("track_metadata", track_metadata);
|
||||
}
|
||||
|
||||
Map<String, Object> content = new HashMap<String, Object>();
|
||||
|
||||
if (registrationData.submission) {
|
||||
payload.put("listened_at", Long.valueOf(registrationData.time.getTime() / 1000L));
|
||||
content.put("listen_type", "single");
|
||||
} else {
|
||||
content.put("listen_type", "playing_now");
|
||||
}
|
||||
|
||||
List<Map<String, Object>> payloads = new ArrayList<Map<String, Object>>();
|
||||
payloads.add(payload);
|
||||
content.put("payload", payloads);
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String json = mapper.writeValueAsString(content);
|
||||
|
||||
executeJsonPostRequest("https://api.listenbrainz.org/1/submit-listens", registrationData.token, json);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean executeJsonPostRequest(String url, String token, String json) throws ClientProtocolException, IOException {
|
||||
HttpPost request = new HttpPost(url);
|
||||
request.setEntity(new StringEntity(json, "UTF-8"));
|
||||
request.setHeader("Authorization", "token " + token);
|
||||
request.setHeader("Content-type", "application/json; charset=utf-8");
|
||||
|
||||
executeRequest(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void executeRequest(HttpUriRequest request) throws ClientProtocolException, IOException {
|
||||
CloseableHttpClient client = HttpClients.createDefault();
|
||||
client.execute(request);
|
||||
}
|
||||
|
||||
private class RegistrationThread extends Thread {
|
||||
private RegistrationThread() {
|
||||
super("ListenBrainzScrobbler Registration");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
RegistrationData registrationData = null;
|
||||
try {
|
||||
registrationData = queue.take();
|
||||
scrobble(registrationData);
|
||||
} catch (ClientProtocolException x) {
|
||||
} catch (IOException x) {
|
||||
handleNetworkError(registrationData, x.toString());
|
||||
} catch (Exception x) {
|
||||
LOG.warn("Error in ListenBrainz registration: " + x.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNetworkError(RegistrationData registrationData, String errorMessage) {
|
||||
try {
|
||||
queue.put(registrationData);
|
||||
LOG.info("ListenBrainz registration for '" + registrationData.title +
|
||||
"' encountered network error: " + errorMessage + ". Will try again later. In queue: " + queue.size());
|
||||
} catch (InterruptedException x) {
|
||||
LOG.error("Failed to reschedule ListenBrainz registration for '" + registrationData.title + "': " + x.toString());
|
||||
}
|
||||
try {
|
||||
sleep(60L * 1000L); // Wait 60 seconds.
|
||||
} catch (InterruptedException x) {
|
||||
LOG.error("Failed to sleep after ListenBrainz registration failure for '" + registrationData.title + "': " + x.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RegistrationData {
|
||||
private String token;
|
||||
private String artist;
|
||||
private String album;
|
||||
private String title;
|
||||
private String musicBrainzReleaseId;
|
||||
private String musicBrainzRecordingId;
|
||||
private Integer trackNumber;
|
||||
private int duration;
|
||||
private Date time;
|
||||
public boolean submission;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<databaseChangeLog
|
||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet id="add-media-file-mb-recording-id" author="BestSteve">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<columnExists tableName="media_file" columnName="mb_recording_id" />
|
||||
</not>
|
||||
</preConditions>
|
||||
<addColumn tableName="media_file">
|
||||
<column name="mb_recording_id" type="${varchar_type}">
|
||||
<constraints nullable="true" />
|
||||
</column>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,7 @@
|
||||
<databaseChangeLog
|
||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<include file="add-media-file-mb-recording-id.xml" relativeToChangelogFile="true"/>
|
||||
<include file="support-listenbrainz.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
@@ -0,0 +1,18 @@
|
||||
<databaseChangeLog
|
||||
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet id="support-listenbrainz" author="BestSteve">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<columnExists tableName="user_settings" columnName="listenbrainz_enabled" />
|
||||
</not>
|
||||
</preConditions>
|
||||
<addColumn tableName="user_settings">
|
||||
<column name="listenbrainz_enabled" type="boolean" defaultValueBoolean="false">
|
||||
<constraints nullable="false" />
|
||||
</column>
|
||||
<column name="listenbrainz_token" type="${varchar_type}" />
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@@ -12,4 +12,5 @@
|
||||
<include file="6.3/changelog.xml" relativeToChangelogFile="true"/>
|
||||
<include file="6.4/changelog.xml" relativeToChangelogFile="true"/>
|
||||
<include file="10.2/changelog.xml" relativeToChangelogFile="true"/>
|
||||
<include file="10.6/changelog.xml" relativeToChangelogFile="true"/>
|
||||
</databaseChangeLog>
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u0439 \u0442\u043E\u0432\u0430, \u043A\u043E\u0435\u0442\u043E \u0441\u043B\u0443\u0448\u0430\u043C \u0432 <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B
|
||||
personalsettings.lastfmpassword=Last.fm \u043F\u0430\u0440\u043E\u043B\u0430
|
||||
personalsettings.listenbrainzenabled=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0430\u0439 \u0442\u043E\u0432\u0430, \u043A\u043E\u0435\u0442\u043E \u0441\u043B\u0443\u0448\u0430\u043C \u0432 <a href="http://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\u041B\u0438\u0447\u043D\u0430 \u0441\u043D\u0438\u043C\u043A\u0430
|
||||
personalsettings.avatar.none=\u0411\u0435\u0437 \u0441\u043D\u0438\u043C\u043A\u0430
|
||||
personalsettings.avatar.custom=\u041C\u043E\u044F \u0441\u043D\u0438\u043C\u043A\u0430
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registrar el que estic reproduint a <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Nom d'usuari de Last.fm
|
||||
personalsettings.lastfmpassword=Contrasenya de Last.fm
|
||||
personalsettings.listenbrainzenabled=Registrar el que estic reproduint a <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Imatge personal
|
||||
personalsettings.avatar.none=Sense imatge
|
||||
personalsettings.avatar.custom=Imatge personalitzada
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Informovat <a href="http://last.fm/" target="_blank">Last.fm</a> o tom, co p\u0159ehr\u00E1v\u00E1m
|
||||
personalsettings.lastfmusername=U\u017Eivatel Last.fm
|
||||
personalsettings.lastfmpassword=Heslo Last.fm
|
||||
personalsettings.listenbrainzenabled=Informovat <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a> o tom, co p\u0159ehr\u00E1v\u00E1m
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Osobn\u00ED obr\u00E1zek
|
||||
personalsettings.avatar.none=Bez obr\u00E1zku
|
||||
personalsettings.avatar.custom=Vlastn\u00ED obr\u00E1zek
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registrer hvad jeg spiller p\u00E5 <a href="http://last.fm/" target="_blank"> Last.fm </a>
|
||||
personalsettings.lastfmusername=Last.fm brugernavn
|
||||
personalsettings.lastfmpassword=Last.fm adgangskode
|
||||
personalsettings.listenbrainzenabled=Registrer hvad jeg spiller p\u00E5 <a href="https://listenbrainz.org/" target="_blank"> ListenBrainz </a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personligt image
|
||||
personalsettings.avatar.none=Intet image
|
||||
personalsettings.avatar.custom=Tilpassede image
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registriere was ich h\u00F6re bei <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm Benutzername
|
||||
personalsettings.lastfmpassword=Last.fm Passwort
|
||||
personalsettings.listenbrainzenabled=Registriere was ich h\u00F6re bei <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Pers\u00F6nliches Bild
|
||||
personalsettings.avatar.none=Kein Bild
|
||||
personalsettings.avatar.custom=Eigenes Bild
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=\u039A\u03B1\u03C4\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03C4\u03BF \u03C4\u03AF \u03B1\u03BA\u03BF\u03CD\u03C9 \u03C3\u03C4\u03BF <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7
|
||||
personalsettings.lastfmpassword=Last.fm \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2
|
||||
personalsettings.listenbrainzenabled=\u039A\u03B1\u03C4\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03C4\u03BF \u03C4\u03AF \u03B1\u03BA\u03BF\u03CD\u03C9 \u03C3\u03C4\u03BF <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\u03A0\u03C1\u03BF\u03C3\u03C9\u03C0\u03B9\u03BA\u03CC \u03B5\u03B9\u03BA\u03BF\u03BD\u03AF\u03B4\u03B9\u03BF
|
||||
personalsettings.avatar.none=\u039A\u03B1\u03BD\u03AD\u03BD\u03B1 \u03B5\u03B9\u03BA\u03BF\u03BD\u03AF\u03B4\u03B9\u03BF
|
||||
personalsettings.avatar.custom=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03BF \u03B5\u03B9\u03BA\u03BF\u03BD\u03AF\u03B4\u03B9\u03BF
|
||||
|
||||
@@ -366,6 +366,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Register what I'm playing at <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm username
|
||||
personalsettings.lastfmpassword=Last.fm password
|
||||
personalsettings.listenbrainzenabled=Register what I'm playing at <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personal image
|
||||
personalsettings.avatar.none=No image
|
||||
personalsettings.avatar.custom=Custom image
|
||||
|
||||
+2
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Register what I'm playing at <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm username
|
||||
personalsettings.lastfmpassword=Last.fm password
|
||||
personalsettings.listenbrainzenabled=Register what I'm playing at <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personal image
|
||||
personalsettings.avatar.none=No image
|
||||
personalsettings.avatar.custom=Custom image
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=N\u00FAmero de \u00E1lbumes/carpetas relacionado
|
||||
personalsettings.lastfmenabled=Registrar lo que estoy reproduciendo en <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Nombre de usuario de Last.fm
|
||||
personalsettings.lastfmpassword=Contrase\u00F1a de Last.fm
|
||||
personalsettings.listenbrainzenabled=Registrar lo que estoy reproduciendo en <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Imagen personal
|
||||
personalsettings.avatar.none=Sin imagen
|
||||
personalsettings.avatar.custom=Imagen personalizada
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registreeri minu kuulatavate lugude loend portaali <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm\u00b4i kasutajanimi
|
||||
personalsettings.lastfmpassword=Last.fm\u00b4i parool
|
||||
personalsettings.listenbrainzenabled=Registreeri minu kuulatavate lugude loend portaali <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Isiklik pilt
|
||||
personalsettings.avatar.none=Pilt puudub
|
||||
personalsettings.avatar.custom=Kohandatud pilt
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Rekister\u00F6i mit\u00E4 kuuntelen <a href="http://last.fm/" target="_blank">Last.fm-palveluun</a>
|
||||
personalsettings.lastfmusername=Last.fm k\u00E4ytt\u00E4j\u00E4tunnus
|
||||
personalsettings.lastfmpassword=Last.fm salasana
|
||||
personalsettings.listenbrainzenabled=Rekister\u00F6i mit\u00E4 kuuntelen <a href="https://listenbrainz.org/" target="_blank">ListenBrainz-palveluun</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Henkil\u00F6kohtainen kuva
|
||||
personalsettings.avatar.none=Ei kuvaa
|
||||
personalsettings.avatar.custom=Valinnainen kuva
|
||||
|
||||
@@ -366,6 +366,8 @@ personalsettings.paginationsize=Nombre d'albums/r\u00e9pertoires associ\u00e9s \
|
||||
personalsettings.lastfmenabled=Enregistrer ce que j'\u00e9coute sur <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Identifiant Last.fm
|
||||
personalsettings.lastfmpassword=Mot de passe Last.fm
|
||||
personalsettings.listenbrainzenabled=Enregistrer ce que j'\u00e9coute sur <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Image personnelle
|
||||
personalsettings.avatar.none=Aucune image
|
||||
personalsettings.avatar.custom=Image personnalis\u00e9e
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Skr\u00E1 \u00C9g Er A\u00F0 Spila \u00C1 <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm Notandanafn
|
||||
personalsettings.lastfmpassword=Last.fm Lykilor\u00F0
|
||||
personalsettings.listenbrainzenabled=Skr\u00E1 \u00C9g Er A\u00F0 Spila \u00C1 <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Einka Mynd
|
||||
personalsettings.avatar.none=Engin Mynd
|
||||
personalsettings.avatar.custom=Valin Mynd
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registra quello che sto ascoltando su <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Nome utente Last.fm
|
||||
personalsettings.lastfmpassword=Password Last.fm
|
||||
personalsettings.listenbrainzenabled=Registra quello che sto ascoltando su <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Immagine personale
|
||||
personalsettings.avatar.none=Nessuna immagine
|
||||
personalsettings.avatar.custom=Immagine personalizzata
|
||||
|
||||
+2
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=\u518D\u751F\u4E2D\u306E\u66F2\u3092 <a href="http://last.fm/" target="_blank">Last.fm</a> \u306B\u767B\u9332
|
||||
personalsettings.lastfmusername=Last.fm \u30E6\u30FC\u30B6\u540D
|
||||
personalsettings.lastfmpassword=Last.fm \u30D1\u30B9\u30EF\u30FC\u30C9
|
||||
personalsettings.listenbrainzenabled=\u518D\u751F\u4E2D\u306E\u66F2\u3092 <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a> \u306B\u767B\u9332
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\u30A2\u30A4\u30B3\u30F3
|
||||
personalsettings.avatar.none=\u30A2\u30A4\u30B3\u30F3\u306A\u3057
|
||||
personalsettings.avatar.custom=\u30AB\u30B9\u30BF\u30E0
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=\uB0B4\uAC00 \uAC10\uC0C1\uD558\uB294 \uB178\uB798\uB97C <a href="http://last.fm/" target="_blank">Last.fm</a> \uC5D0 \uB4F1\uB85D\uD558\uAE30
|
||||
personalsettings.lastfmusername=Last.fm \uC0AC\uC6A9\uC790 \uC774\uB984
|
||||
personalsettings.lastfmpassword=Last.fm \uC554\uD638
|
||||
personalsettings.listenbrainzenabled=\uB0B4\uAC00 \uAC10\uC0C1\uD558\uB294 \uB178\uB798\uB97C <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a> \uC5D0 \uB4F1\uB85D\uD558\uAE30
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\uAC1C\uC778 \uC774\uBBF8\uC9C0
|
||||
personalsettings.avatar.none=\uC774\uBBF8\uC9C0 \uC5C6\uC74C
|
||||
personalsettings.avatar.custom=\uC0AC\uC6A9\uC790 \uC774\uBBF8\uC9C0
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Register what I'm playing at <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm username
|
||||
personalsettings.lastfmpassword=Last.fm password
|
||||
personalsettings.listenbrainzenabled=Register what I'm playing at <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personal image
|
||||
personalsettings.avatar.none=No image
|
||||
personalsettings.avatar.custom=Custom image
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Aantal te tonen gerelateerde albums/mappen (0=al
|
||||
personalsettings.lastfmenabled=Registreer wat ik afspeel op <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm-gebruikersnaam
|
||||
personalsettings.lastfmpassword=Last.fm-wachtwoord
|
||||
personalsettings.listenbrainzenabled=Registreer wat ik afspeel op <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Persoonlijke afbeelding
|
||||
personalsettings.avatar.none=Geen afbeelding
|
||||
personalsettings.avatar.custom=Aangepaste afbeelding
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registrer kva eg spelar hos <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Brukarnamn for Last.fm
|
||||
personalsettings.lastfmpassword=Passord for Last.fm
|
||||
personalsettings.listenbrainzenabled=Registrer kva eg spelar hos <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personleg bilete
|
||||
personalsettings.avatar.none=Ikkje bilete
|
||||
personalsettings.avatar.custom=Eigendefinert bilete
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registrer hva jeg spiller hos <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Brukernavn for Last.fm
|
||||
personalsettings.lastfmpassword=Passord for Last.fm
|
||||
personalsettings.listenbrainzenabled=Registrer hva jeg spiller hos <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personlig bilde
|
||||
personalsettings.avatar.none=Ingen bilde
|
||||
personalsettings.avatar.custom=Egendefinert bilde
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Rejestuj co odtwarzam na <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=U\u017Cytkownik Last.fm
|
||||
personalsettings.lastfmpassword=Has\u0142o Last.fm
|
||||
personalsettings.listenbrainzenabled=Rejestuj co odtwarzam na <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=M\u00F3j obrazek
|
||||
personalsettings.avatar.none=Bez obrazka
|
||||
personalsettings.avatar.custom=W\u0142asny obrazek
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registar o que estou a ouvir no <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Utilizador Last.fm
|
||||
personalsettings.lastfmpassword=Senha Last.fm
|
||||
personalsettings.listenbrainzenabled=Registar o que estou a ouvir no <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Imagem pessoal
|
||||
personalsettings.avatar.none=Sem imagem
|
||||
personalsettings.avatar.custom=Imagem personalizada
|
||||
|
||||
+2
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Register what I'm playing at <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm username
|
||||
personalsettings.lastfmpassword=Last.fm password
|
||||
personalsettings.listenbrainzenabled=Register what I'm playing at <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personal image
|
||||
personalsettings.avatar.none=No image
|
||||
personalsettings.avatar.custom=Custom image
|
||||
|
||||
+2
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Register what I'm playing at <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm username
|
||||
personalsettings.lastfmpassword=Last.fm password
|
||||
personalsettings.listenbrainzenabled=Register what I'm playing at <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personal image
|
||||
personalsettings.avatar.none=No image
|
||||
personalsettings.avatar.custom=Custom image
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0447\u0442\u043E \u044F \u0441\u043B\u0443\u0448\u0430\u044E \u043D\u0430 <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm \u043B\u043E\u0433\u0438\u043D
|
||||
personalsettings.lastfmpassword=Last.fm \u043F\u0430\u0440\u043E\u043B\u044C
|
||||
personalsettings.listenbrainzenabled=\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0447\u0442\u043E \u044F \u0441\u043B\u0443\u0448\u0430\u044E \u043D\u0430 <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\u041F\u0435\u0440\u0441\u043E\u043D\u0430\u043B\u044C\u043D\u043E\u0435 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435
|
||||
personalsettings.avatar.none=\u041D\u0435\u0442 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F
|
||||
personalsettings.avatar.custom=\u0421\u0432\u043E\u0451 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Zabele\u017Ei, kaj poslu\u0161am, na <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm uporabni\u0161ko ime
|
||||
personalsettings.lastfmpassword=Last.fm geslo
|
||||
personalsettings.listenbrainzenabled=Zabele\u017Ei, kaj poslu\u0161am, na <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Osebna slika
|
||||
personalsettings.avatar.none=Brez slike
|
||||
personalsettings.avatar.custom=Slika po meri
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Registrera vad jag spelar hos <a href="http://last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Last.fm anv\u00E4ndare
|
||||
personalsettings.lastfmpassword=Last.fm l\u00F6senord
|
||||
personalsettings.listenbrainzenabled=Registrera vad jag spelar hos <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personlig bild
|
||||
personalsettings.avatar.none=Ingen bild
|
||||
personalsettings.avatar.custom=Egen bild
|
||||
|
||||
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=Register what I'm playing at <a href="https://www.last.fm/" target="_blank">Last.fm</a>
|
||||
personalsettings.lastfmusername=Ім'я користувача Last.fm
|
||||
personalsettings.lastfmpassword=Last.fm пароль
|
||||
personalsettings.listenbrainzenabled=Register what I'm playing at <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=Personal image
|
||||
personalsettings.avatar.none=Немає зображення
|
||||
personalsettings.avatar.custom=Custom image
|
||||
|
||||
+2
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of related albums/directories to show ini
|
||||
personalsettings.lastfmenabled=\u767B\u5F55 <a href="http://last.fm/" target="_blank">Last.fm</a>\u7684\u5E10\u53F7
|
||||
personalsettings.lastfmusername=Last.fm \u5E10\u53F7
|
||||
personalsettings.lastfmpassword=Last.fm \u5BC6\u7801
|
||||
personalsettings.listenbrainzenabled=\u767B\u5F55 <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a>\u7684\u5E10\u53F7
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\u4E2A\u6027\u5316\u5934\u50CF
|
||||
personalsettings.avatar.none=\u65E0\u5934\u50CF
|
||||
personalsettings.avatar.custom=\u81EA\u5B9A\u4E49
|
||||
|
||||
+2
@@ -365,6 +365,8 @@ personalsettings.paginationsize=Number of initial related albums/directories to
|
||||
personalsettings.lastfmenabled=\u5728 <a href="http://last.fm/" target="_blank">Last.fm</a> \u767B\u9304\u6211\u7684\u64AD\u653E\u52D5\u614B
|
||||
personalsettings.lastfmusername=Last.fm \u5E33\u865F
|
||||
personalsettings.lastfmpassword=Last.fm \u5BC6\u78BC
|
||||
personalsettings.listenbrainzenabled=\u5728 <a href="https://listenbrainz.org/" target="_blank">ListenBrainz</a> \u767B\u9304\u6211\u7684\u64AD\u653E\u52D5\u614B
|
||||
personalsettings.listenbrainztoken=ListenBrainz User Token
|
||||
personalsettings.avatar.title=\u500B\u4EBA\u5716\u793A
|
||||
personalsettings.avatar.none=\u4E0D\u7528\u5716\u793A
|
||||
personalsettings.avatar.custom=\u81EA\u8A02\u5F71\u50CF
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
<script type="text/javascript" src="<c:url value='/script/utils.js'/>"></script>
|
||||
|
||||
<script type="text/javascript" language="javascript">
|
||||
function enableLastFmFields() {
|
||||
function enableFields() {
|
||||
$("#lastFm").is(":checked") ? $("#lastFmTable").show() : $("#lastFmTable").hide();
|
||||
$("#listenBrainz").is(":checked") ? $("#listenBrainzTable").show() : $("#listenBrainzTable").hide();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body class="mainframe bgcolor1" onload="enableLastFmFields()">
|
||||
<body class="mainframe bgcolor1" onload="enableFields()">
|
||||
<script type="text/javascript" src="<c:url value='/script/wz_tooltip.js'/>"></script>
|
||||
<script type="text/javascript" src="<c:url value='/script/tip_balloon.js'/>"></script>
|
||||
|
||||
@@ -172,9 +173,13 @@
|
||||
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td><form:checkbox path="lastFmEnabled" id="lastFm" cssClass="checkbox" onclick="enableLastFmFields()"/></td>
|
||||
<td><form:checkbox path="lastFmEnabled" id="lastFm" cssClass="checkbox" onclick="enableFields()"/></td>
|
||||
<td><label for="lastFm"><fmt:message key="personalsettings.lastfmenabled"/></label></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><form:checkbox path="listenBrainzEnabled" id="listenBrainz" cssClass="checkbox" onclick="enableFields()"/></td>
|
||||
<td><label for="listenBrainz"><fmt:message key="personalsettings.listenbrainzenabled"/></label></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="indent">
|
||||
@@ -202,6 +207,13 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table id="listenBrainzTable" style="padding-left:2em">
|
||||
<tr>
|
||||
<td><fmt:message key="personalsettings.listenbrainztoken"/></td>
|
||||
<td><form:input path="listenBrainzToken" size="36"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p style="padding-top:1em;padding-bottom:1em">
|
||||
<input type="submit" value="<fmt:message key='common.save'/>" style="margin-right:0.3em"/>
|
||||
<a href='nowPlaying.view'><input type="button" value="<fmt:message key='common.cancel'/>"></a>
|
||||
|
||||
@@ -187,6 +187,8 @@ public class UserDaoTestCase extends DaoTestCaseBean2 {
|
||||
assertFalse("Error in getUserSettings().", userSettings.isLastFmEnabled());
|
||||
assertNull("Error in getUserSettings().", userSettings.getLastFmUsername());
|
||||
assertNull("Error in getUserSettings().", userSettings.getLastFmPassword());
|
||||
assertFalse("Error in getUserSettings().", userSettings.isListenBrainzEnabled());
|
||||
assertNull("Error in getUserSettings().", userSettings.getListenBrainzToken());
|
||||
assertSame("Error in getUserSettings().", TranscodeScheme.OFF, userSettings.getTranscodeScheme());
|
||||
assertFalse("Error in getUserSettings().", userSettings.isShowNowPlayingEnabled());
|
||||
assertEquals("Error in getUserSettings().", -1, userSettings.getSelectedMusicFolderId());
|
||||
@@ -208,6 +210,8 @@ public class UserDaoTestCase extends DaoTestCaseBean2 {
|
||||
settings.setLastFmEnabled(true);
|
||||
settings.setLastFmUsername("last_user");
|
||||
settings.setLastFmPassword("last_pass");
|
||||
settings.setListenBrainzEnabled(true);
|
||||
settings.setListenBrainzToken("01234567-89ab-cdef-0123-456789abcdef");
|
||||
settings.setTranscodeScheme(TranscodeScheme.MAX_192);
|
||||
settings.setShowNowPlayingEnabled(false);
|
||||
settings.setSelectedMusicFolderId(3);
|
||||
@@ -233,6 +237,8 @@ public class UserDaoTestCase extends DaoTestCaseBean2 {
|
||||
assertEquals("Error in getUserSettings().", true, userSettings.isLastFmEnabled());
|
||||
assertEquals("Error in getUserSettings().", "last_user", userSettings.getLastFmUsername());
|
||||
assertEquals("Error in getUserSettings().", "last_pass", userSettings.getLastFmPassword());
|
||||
assertEquals("Error in getUserSettings().", true, userSettings.isListenBrainzEnabled());
|
||||
assertEquals("Error in getUserSettings().", "01234567-89ab-cdef-0123-456789abcdef", userSettings.getListenBrainzToken());
|
||||
assertSame("Error in getUserSettings().", TranscodeScheme.MAX_192, userSettings.getTranscodeScheme());
|
||||
assertFalse("Error in getUserSettings().", userSettings.isShowNowPlayingEnabled());
|
||||
assertEquals("Error in getUserSettings().", 3, userSettings.getSelectedMusicFolderId());
|
||||
@@ -267,4 +273,4 @@ public class UserDaoTestCase extends DaoTestCaseBean2 {
|
||||
assertEquals("Wrong jukebox role.", expected.isJukeboxRole(), actual.isJukeboxRole());
|
||||
assertEquals("Wrong settings role.", expected.isSettingsRole(), actual.isSettingsRole());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -218,7 +218,7 @@ public class MediaScannerServiceTestCase {
|
||||
Assert.assertEquals("0820752d-1043-4572-ab36-2df3b5cc15fa", album.getMusicBrainzReleaseId());
|
||||
Assert.assertEquals(musicFolderFile.toPath().resolve("TestAlbum").toString(), album.getPath());
|
||||
|
||||
// Test that the music file is correctly imported, along with its MusicBrainz release ID
|
||||
// Test that the music file is correctly imported, along with its MusicBrainz release ID and recording ID
|
||||
List<MediaFile> albumFiles = mediaFileDao.getChildrenOf(allAlbums.get(0).getPath());
|
||||
Assert.assertEquals(1, albumFiles.size());
|
||||
MediaFile file = albumFiles.get(0);
|
||||
@@ -232,5 +232,6 @@ public class MediaScannerServiceTestCase {
|
||||
Assert.assertEquals(album.getPath(), file.getParentPath());
|
||||
Assert.assertEquals(new File(album.getPath()).toPath().resolve("01 - Aria.flac").toString(), file.getPath());
|
||||
Assert.assertEquals("0820752d-1043-4572-ab36-2df3b5cc15fa", file.getMusicBrainzReleaseId());
|
||||
Assert.assertEquals("831586f4-56f9-4785-ac91-447ae20af633", file.getMusicBrainzRecordingId());
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user