From 72851f072128134f4ef8c103146aa7596735183d Mon Sep 17 00:00:00 2001 From: Zak Peirce Date: Tue, 20 Dec 2016 11:41:02 -0800 Subject: [PATCH] Back ported last.fm coverart service from Subsonic 6.0_beta1 None of this code is mine, I just integrated it. --- libresonic-main/pom.xml | 10 +- .../player/ajax/CoverArtService.java | 14 +- .../controller/ChangeCoverArtController.java | 5 +- .../libresonic/player/domain/AlbumNotes.java | 69 +++++ .../player/domain/LastFmCoverArt.java | 50 +++ .../service/LastFmExpirationPolicy.java | 49 +++ .../player/service/LastFmService.java | 101 ++++++ .../player/i18n/ResourceBundle_en.properties | 3 +- .../WEB-INF/applicationContext-service.xml | 1 + .../src/main/webapp/WEB-INF/dwr.xml | 4 +- .../webapp/WEB-INF/jsp/changeCoverArt.jsp | 146 +++------ .../src/main/webapp/WEB-INF/jsp/coverArt.jsp | 287 +++++++++--------- .../src/main/webapp/icons/lastfm.gif | Bin 0 -> 1763 bytes 13 files changed, 473 insertions(+), 266 deletions(-) create mode 100644 libresonic-main/src/main/java/org/libresonic/player/domain/AlbumNotes.java create mode 100644 libresonic-main/src/main/java/org/libresonic/player/domain/LastFmCoverArt.java create mode 100644 libresonic-main/src/main/java/org/libresonic/player/service/LastFmExpirationPolicy.java create mode 100644 libresonic-main/src/main/webapp/icons/lastfm.gif diff --git a/libresonic-main/pom.xml b/libresonic-main/pom.xml index 910b1119..9bbb392a 100644 --- a/libresonic-main/pom.xml +++ b/libresonic-main/pom.xml @@ -123,14 +123,8 @@ com.google.guava - guava-base - r03 - - - - com.google.guava - guava-collections - r03 + guava + 18.0 diff --git a/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java b/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java index 360c668d..c626250b 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java +++ b/libresonic-main/src/main/java/org/libresonic/player/ajax/CoverArtService.java @@ -23,6 +23,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; +import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.http.client.config.RequestConfig; @@ -32,7 +33,9 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.libresonic.player.Logger; +import org.libresonic.player.domain.LastFmCoverArt; import org.libresonic.player.domain.MediaFile; +import org.libresonic.player.service.LastFmService; import org.libresonic.player.service.MediaFileService; import org.libresonic.player.service.SecurityService; import org.libresonic.player.util.StringUtil; @@ -50,6 +53,11 @@ public class CoverArtService { private SecurityService securityService; private MediaFileService mediaFileService; + private LastFmService lastFmService; + + public List searchCoverArt(String artist, String album) { + return lastFmService.searchCoverArt(artist, album); + } /** * Downloads and saves the cover art at the given URL. @@ -162,4 +170,8 @@ public class CoverArtService { public void setMediaFileService(MediaFileService mediaFileService) { this.mediaFileService = mediaFileService; } -} \ No newline at end of file + + public void setLastFmService(LastFmService lastFmService) { + this.lastFmService = lastFmService; + } +} diff --git a/libresonic-main/src/main/java/org/libresonic/player/controller/ChangeCoverArtController.java b/libresonic-main/src/main/java/org/libresonic/player/controller/ChangeCoverArtController.java index 9e977ea2..9047564a 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/controller/ChangeCoverArtController.java +++ b/libresonic-main/src/main/java/org/libresonic/player/controller/ChangeCoverArtController.java @@ -25,6 +25,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang.StringUtils; import org.springframework.web.bind.ServletRequestUtils; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.ParameterizableViewController; @@ -49,10 +50,10 @@ public class ChangeCoverArtController extends ParameterizableViewController { String album = request.getParameter("album"); MediaFile dir = mediaFileService.getMediaFile(id); - if (artist == null) { + if (StringUtils.isBlank(artist)) { artist = dir.getArtist(); } - if (album == null) { + if (StringUtils.isBlank(album)) { album = dir.getAlbumName(); } diff --git a/libresonic-main/src/main/java/org/libresonic/player/domain/AlbumNotes.java b/libresonic-main/src/main/java/org/libresonic/player/domain/AlbumNotes.java new file mode 100644 index 00000000..d3d48237 --- /dev/null +++ b/libresonic-main/src/main/java/org/libresonic/player/domain/AlbumNotes.java @@ -0,0 +1,69 @@ +/* + * This file is part of Libresonic. + * + * Libresonic 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. + * + * Libresonic 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 Libresonic. If not, see . + * + * Copyright 2016 (C) Libresonic Authors + * Based upon Subsonic, Copyright 2009 (C) Sindre Mehus + */ + +package org.libresonic.player.domain; + +/** + * @author Sindre Mehus + * @version $Id$ + */ +public class AlbumNotes { + + private final String notes; + private final String musicBrainzId; + private final String lastFmUrl; + private final String smallImageUrl; + private final String mediumImageUrl; + private final String largeImageUrl; + + public AlbumNotes(String notes, String musicBrainzId, String lastFmUrl, String smallImageUrl, + String mediumImageUrl, String largeImageUrl) { + this.notes = notes; + this.musicBrainzId = musicBrainzId; + this.lastFmUrl = lastFmUrl; + this.smallImageUrl = smallImageUrl; + this.mediumImageUrl = mediumImageUrl; + this.largeImageUrl = largeImageUrl; + } + + public String getNotes() { + return notes; + } + + public String getMusicBrainzId() { + return musicBrainzId; + } + + public String getLastFmUrl() { + return lastFmUrl; + } + + public String getSmallImageUrl() { + return smallImageUrl; + } + + public String getMediumImageUrl() { + return mediumImageUrl; + } + + public String getLargeImageUrl() { + return largeImageUrl; + } +} diff --git a/libresonic-main/src/main/java/org/libresonic/player/domain/LastFmCoverArt.java b/libresonic-main/src/main/java/org/libresonic/player/domain/LastFmCoverArt.java new file mode 100644 index 00000000..d328113a --- /dev/null +++ b/libresonic-main/src/main/java/org/libresonic/player/domain/LastFmCoverArt.java @@ -0,0 +1,50 @@ +/* + * This file is part of Libresonic. + * + * Libresonic 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. + * + * Libresonic 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 Libresonic. If not, see . + * + * Copyright 2014 (C) Sindre Mehus + */ + +package org.libresonic.player.domain; + +/** + * @author Sindre Mehus + * @version $Id$ + */ +public class LastFmCoverArt { + + private final String imageUrl; + private final String artist; + private final String album; + + public LastFmCoverArt(String imageUrl, String artist, String album) { + this.imageUrl = imageUrl; + this.artist = artist; + this.album = album; + } + + public String getImageUrl() { + return imageUrl; + } + + public String getArtist() { + return artist; + } + + public String getAlbum() { + return album; + } +} + diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/LastFmExpirationPolicy.java b/libresonic-main/src/main/java/org/libresonic/player/service/LastFmExpirationPolicy.java new file mode 100644 index 00000000..e5a378a5 --- /dev/null +++ b/libresonic-main/src/main/java/org/libresonic/player/service/LastFmExpirationPolicy.java @@ -0,0 +1,49 @@ +/** + * This file is part of Libresonic. + * + * Libresonic 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. + * + * Libresonic 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 Libresonic. If not, see . + * + * Copyright 2014 (C) Sindre Mehus + */ + +package org.libresonic.player.service; + +import java.util.LinkedHashMap; +import java.util.Map; + +import de.umass.lastfm.cache.ExpirationPolicy; + +/** + * Artist and album info is cached permanently. Everything else is cached one year. + * + * @author Sindre Mehus + * @version $Id$ + */ +public class LastFmExpirationPolicy implements ExpirationPolicy { + + private final static long ONE_YEAR = 12 * 30 * 24 * 3600 * 1000L; + + private final Map methodToExpirationTime = new LinkedHashMap() {{ + put("artist.getInfo", Long.MAX_VALUE); // Cache forever + put("album.getInfo", Long.MAX_VALUE); // Cache forever + put("album.search", -1L); // Don't cache + }}; + + @Override + public long getExpirationTime(String method, Map params) { + Long expirationTime = methodToExpirationTime.get(method); + return expirationTime == null ? ONE_YEAR : expirationTime; + } +} + diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/LastFmService.java b/libresonic-main/src/main/java/org/libresonic/player/service/LastFmService.java index fcb75ce6..037c8d7e 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/service/LastFmService.java +++ b/libresonic-main/src/main/java/org/libresonic/player/service/LastFmService.java @@ -22,6 +22,7 @@ package org.libresonic.player.service; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -30,13 +31,23 @@ import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Lists; + +import de.umass.lastfm.Album; import de.umass.lastfm.Artist; import de.umass.lastfm.Caller; import de.umass.lastfm.ImageSize; import de.umass.lastfm.Track; +import de.umass.lastfm.cache.Cache; + import org.libresonic.player.Logger; import org.libresonic.player.dao.ArtistDao; import org.libresonic.player.dao.MediaFileDao; +import org.libresonic.player.domain.LastFmCoverArt; +import org.libresonic.player.domain.AlbumNotes; import org.libresonic.player.domain.ArtistBio; import org.libresonic.player.domain.MediaFile; import org.libresonic.player.domain.MusicFolder; @@ -273,6 +284,96 @@ public class LastFmService { } } + /** + * Returns album notes and images. + * + * @param mediaFile The media file (song or album). + * @return Album notes. + */ + public AlbumNotes getAlbumNotes(MediaFile mediaFile) { + return getAlbumNotes(getCanonicalArtistName(getArtistName(mediaFile)), mediaFile.getAlbumName()); + } + + /** + * Returns album notes and images. + * + * @param album The album. + * @return Album notes. + */ + public AlbumNotes getAlbumNotes(org.libresonic.player.domain.Album album) { + return getAlbumNotes(getCanonicalArtistName(album.getArtist()), album.getName()); + } + + /** + * Returns album notes and images. + * + * @param artist The artist name. + * @param album The album name. + * @return Album notes. + */ + private AlbumNotes getAlbumNotes(String artist, String album) { + if (artist == null || album == null) { + return null; + } + try { + Album info = Album.getInfo(artist, album, LAST_FM_KEY); + if (info == null) { + return null; + } + return new AlbumNotes(processWikiText(info.getWikiSummary()), + info.getMbid(), + info.getUrl(), + info.getImageURL(ImageSize.MEDIUM), + info.getImageURL(ImageSize.LARGE), + info.getImageURL(ImageSize.MEGA)); + } catch (Throwable x) { + LOG.warn("Failed to find album notes for " + artist + " - " + album, x); + return null; + } + } + + public List searchCoverArt(String artist, String album) { + if (artist == null && album == null) { + return Collections.emptyList(); + } + try { + StringBuilder query = new StringBuilder(); + if (artist != null) { + query.append(artist).append(" "); + } + if (album != null) { + query.append(album); + } + + Collection matches = Album.search(query.toString(), LAST_FM_KEY); + return FluentIterable.from(matches) + .transform(new Function() { + @Override + public LastFmCoverArt apply(Album album) { + return convert(album); + } + }) + .filter(Predicates.notNull()) + .toList(); + } catch (Throwable x) { + LOG.warn("Failed to search for cover art for " + artist + " - " + album, x); + return Collections.emptyList(); + } + } + + private LastFmCoverArt convert(Album album) { + String imageUrl = null; + for (ImageSize imageSize : Lists.reverse(Arrays.asList(ImageSize.values()))) { + imageUrl = StringUtils.trimToNull(album.getImageURL(imageSize)); + if (imageUrl != null) { + break; + } + } + + return imageUrl == null ? null : new LastFmCoverArt(imageUrl, album.getArtist(), album.getName()); + } + + private ArtistBio getArtistBio(String artistName) { try { if (artistName == null) { diff --git a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties index b23e08b9..512346ff 100644 --- a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties +++ b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties @@ -666,11 +666,12 @@ changecoverart.title = Change cover art changecoverart.address = Or enter image address changecoverart.artist = Artist changecoverart.album = Album -changecoverart.search = Google Image Search +changecoverart.search = Search Last.fm changecoverart.wait = Please wait... changecoverart.success = Image was successfully downloaded. changecoverart.error = Failed to download image. changecoverart.noimagesfound = No images found. +changecoverart.courtesy = Images courtesy of Last.fm # changeCoverArtConfirm.jsp changeCoverArtConfirm.failed = Failed to change cover art:
"{0}" diff --git a/libresonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml b/libresonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml index daa22901..45e6f641 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml +++ b/libresonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml @@ -274,6 +274,7 @@ + diff --git a/libresonic-main/src/main/webapp/WEB-INF/dwr.xml b/libresonic-main/src/main/webapp/WEB-INF/dwr.xml index 2ac93368..548027f0 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/dwr.xml +++ b/libresonic-main/src/main/webapp/WEB-INF/dwr.xml @@ -53,6 +53,8 @@ + + @@ -63,4 +65,4 @@ - \ No newline at end of file + diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp index 724ec653..6665cad1 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/changeCoverArt.jsp @@ -6,13 +6,14 @@ - - +

- + +
" size="35" type="text" value="${model.artist}" onclick="select()"/>" size="35" type="text" value="${model.album}" onclick="select()"/> "/>
- @@ -178,32 +117,21 @@ -
- -
-
- -
-
- -
-
- -
-
- -
-
- +
+
+
+
+ "> +
-
-
- -
-
-
+ - \ No newline at end of file + + diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp index 8d9eee77..0f860ab6 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/coverArt.jsp @@ -1,144 +1,143 @@ -<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %> -<%@ include file="include.jsp" %> - -<%-- -PARAMETERS - albumId: ID of album. - playlistId: ID of playlist. - podcastChannelId: ID of podcast channel - coverArtSize: Height and width of cover art. - caption1: Caption line 1 - caption2: Caption line 2 - caption3: Caption line 3 - captionCount: Number of caption lines to display (default 0) - showLink: Whether to make the cover art image link to the album page. - showZoom: Whether to display a link for zooming the cover art. - showChange: Whether to display a link for changing the cover art. - appearAfter: Fade in after this many milliseconds, or nil if no fading in should happen. - hideOverflow: Hide cover art overflow when height is greater than width ---%> - - - - - - - - - - - - - - - -
-
overflow:hidden;" title="${param.caption1}" id="${divId}"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- " id="${playId}" - style="position: relative; top: 8px; left: 8px; z-index: 2; display:none" > -
- - - - - - -
- - - - - -
${param.caption2} 
-
- -
${param.caption3} 
-
-
- - -
- - - - - - - - - | - - - - - -
-
- - - +<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="iso-8859-1" %> +<%@ include file="include.jsp" %> + +<%-- +PARAMETERS + albumId: ID of album. + playlistId: ID of playlist. + podcastChannelId: ID of podcast channel + coverArtSize: Height and width of cover art. + caption1: Caption line 1 + caption2: Caption line 2 + caption3: Caption line 3 + captionCount: Number of caption lines to display (default 0) + showLink: Whether to make the cover art image link to the album page. + showZoom: Whether to display a link for zooming the cover art. + showChange: Whether to display a link for changing the cover art. + appearAfter: Fade in after this many milliseconds, or nil if no fading in should happen. +--%> + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ " id="${playId}" + style="position: relative; top: 8px; left: 8px; z-index: 2; display:none" > +
+ + + + + + +
+ + + + + +
${param.caption2} 
+
+ +
${param.caption3} 
+
+
+ + +
+ + + + + + + + + | + + + + + +
+
+ + + diff --git a/libresonic-main/src/main/webapp/icons/lastfm.gif b/libresonic-main/src/main/webapp/icons/lastfm.gif new file mode 100644 index 0000000000000000000000000000000000000000..b244a75f10b99123b9dec7dbed5e17db9937b269 GIT binary patch literal 1763 zcmeH``#X~h0DwOk%|UJ{k6b2~B!yH)$~O0OQ60W=WJPMVN|%Qm7Df&knN#CAbor`P za)~aKoV2aEY;-!Bx`~&ANIPcHzPw(@*q2VD&-&2u*4KNP@TCKLn z3_cVZ&}(TdL?OG4V1vQ_Jr+L=V*I*I^oPRH!o9l${@w#Sw+{Gt2zzXd)|3ayoAAOXu{%ipk$^FRN6_*LB0#?SGXMal5&r$069_IVz6z}IW|WQ+tCzob&mb+P##E^~i%^?SK4j*m@K zABle6uXtH4&CeOTc2QnlIuai{K_pDxDPG|9EsjY>@rhzEa_;LF0m^5A-zP7-BRQ8K z4Dw%|RgGReBgE{U{rF)d?wC0G&_u?sgJI~g>sMu@-{hqwqt`B}=`CX^muIS;O+oiX zQHOZ$&XO}&RnxnngJFuA7bB;RPoyVLRg@{}YsQk3M3LdryzJpb?C6yXQ}@efDxVJR z3sk*t6bA3<_uex7^pToD8w}nn2=GzVR`nyj#&4v~b9=|rlBdc`1$%Zb@Oi4{Ce^#Q z!Y~Z*KmS?4;X6zR03g8qU+do|0Js)FZ?+1nhv+$Q3%A&gdIwh>Q^$Sx!d^XYC0Ti& zzlnpMv=<`+3v9w)Q(1Rx%_?JAub+07c_y&$g&%b$Rv@p*gseBuzdl6e48@|};%w~q znp&P!AC zZ@raAt1m&)>bu;>5uYE_TIWm&GFmd;UrY}ro7k5ZB0vA=f{X1pHqpcznR#_+%uPPM z`Fehx2XzK#$H*m#>_hn4=^m%lGFhWfCHAWXpQoun)}An=7A|f_jc1N^#|31~Prl$0 zGi}viXa+v9Bsb$KtkQ0^X~;ruy4f0d7Tk}jWPlsH=sIRtvl9J$0HTMk-cZU%ul5VF zdbDO;LH*-37I6#M0-F@Xs+4UQ9K9Gdpx_z@oTodiMYSlS9M;KQ^~niB6jd*Ukf=*O z7VmqSj6ZRXXoW2zQcJLj=X7AWXPcSeL8yrd#!?OFj@Sdkw6)m8jc3b~Rw0-VvR=O! z2M;L>&Xyk{=(*|@?y6#d?w)K0=(dB6+k`E7)k<)e4~INC6?tA-S%{H+1AobKtD<5{ zV(8WN46VWvUYD(~G^%ge*A7xp@sosbKqWJIH-27T{ibb^Yf^7+2)t~_NS4Dm(E>&d zXJr7>z==~vnS=XL;dVqPCB=xdPRVT)HtyDG=JBgu7Uw>QIHrQ zN!^)bnMugarbl%sC^YQTlb2zep_5^sfFISDKk4csygqI5_gCsUm0e&oxgf5nz+2=( zn2T?X5n$y!KkNxOO(-ai>k;VLP543dcA85jC6MK(dlJKPki>3hSqE)xaGvdk=mvN% zIuqQFgW6-f?qzAfO+mH!8D6-{%$R6As*!`&{ku_r2O;?;Cm~{MvOZy+cAvN&Uqsef z^I%~@PvpoHi;R%!JvkXjF@yv{6w6$r8yl1qaeii$uc%m8>?1P2dnrx2Gj>GB;MpC| zAOq*1M@*@CDv)jv$U!y34%7C+Ce6w9AEb7vP`Y&AS`&hz_3Q04WwQxlSki+8coU-OS1lMZ1S~NzF?N_@U
">