/* This file is part of Madsonic. Madsonic 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. Madsonic 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 Madsonic. If not, see <http://www.gnu.org/licenses/>. Copyright 2013 (C) Madevil */ package net.sourceforge.subsonic.service; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.Collection; import java.util.List; import java.util.Locale; import net.sourceforge.subsonic.Logger; import net.sourceforge.subsonic.dao.LastFMArtistDao; import net.sourceforge.subsonic.dao.LastFMArtistSimilarDao; import net.sourceforge.subsonic.domain.Artist; import net.sourceforge.subsonic.domain.LastFMArtist; import net.sourceforge.subsonic.domain.LastFMArtistSimilar; import net.sourceforge.subsonic.domain.MediaFile; import net.sourceforge.subsonic.lastfm.Album; import net.sourceforge.subsonic.lastfm.Image; import net.sourceforge.subsonic.lastfm.ImageSize; import net.sourceforge.subsonic.lastfm.PaginatedResult; import net.sourceforge.subsonic.util.StringUtil; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringEscapeUtils; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.HttpConnectionParams; import org.jfree.util.Log; public class LastFMService { private static final Logger LOG = Logger.getLogger(LastFMService.class); private SecurityService securityService; private SettingsService settingsService; private MediaFileService mediaFileService; private LastFMArtistDao lastFMArtistDao; // Use here your own Last.FM API key private static String api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; private static LastFMArtistSimilarDao lastFMArtistSimilarDao; public LastFMArtist getArtist(String artistname) { try { if (artistname != null || artistname.length() > 0 ){ String tmpArtistName = net.sourceforge.subsonic.lastfm.Artist.getCorrection(artistname, api_key).getName(); if (artistname != tmpArtistName) { LOG.debug("## ArtistAutoCorrect: " + artistname + " -> " + tmpArtistName); } return lastFMArtistDao.getArtist(tmpArtistName); } } catch (Exception x) { // Ignore Exception return null; } return null; } public List<String> getSimilarArtist(String ArtistName){ return lastFMArtistSimilarDao.getSimilarArtist(ArtistName); } public void CleanupArtist(){ lastFMArtistDao.CleanupArtist(); } public void getArtistImages(List<Artist> artistList) { LOG.info("## ArtistCount: " + artistList.size()); MediaFile mediaFileArtist; for (Artist artist : artistList) { try{ int id = mediaFileService.getIDfromArtistname(artist.getName()) == null ? -1 : mediaFileService.getIDfromArtistname(artist.getName()); mediaFileArtist = mediaFileService.getMediaFile(id); if (mediaFileArtist == null){ continue; } LOG.debug("## Scan for Artist: " + artist.getName() ); getArtistImage(mediaFileArtist, api_key); } catch (NullPointerException ex) { System.out.println("## ERROR: " + artist.getName()); } } LOG.info("## LastFM Scan Finished"); } public void getArtistBio(List<Artist> artistList) { LOG.info("## ArtistCount: " + artistList.size()); MediaFile mediaFileArtist; //LastFMArtist for (Artist artist : artistList) { try{ int id = mediaFileService.getIDfromArtistname(artist.getName()) == null ? -1 : mediaFileService.getIDfromArtistname(artist.getName()); mediaFileArtist = mediaFileService.getMediaFile(id); if (mediaFileArtist == null){ continue; } LOG.debug("## Scan for ArtistBio: " + artist.getName() ); // getArtistBio(mediaFileArtist, api_key); // mediaFileService.createOrUpdateMediaFile(mediaFileArtist); } catch (NullPointerException ex) { System.out.println("## ERROR: " + artist.getName()); } } LOG.info("## LastFM Scan Finished"); } public String stripNonValidXMLCharacters(String in) { StringBuffer out = new StringBuffer(); // Used to hold the output. char current; // Used to reference the current character. if (in == null || ("".equals(in))) return ""; // vacancy test. for (int i = 0; i < in.length(); i++) { current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen. if ((current == 0x9) || (current == 0xA) || (current == 0xB) || (current == 0xD) || (current == 0x1f) || ((current >= 0x20) && (current <= 0xD7FF)) || ((current >= 0xE000) && (current <= 0xFFFD)) || ((current >= 0x10000) && (current <= 0x10FFFF))) out.append(current); } return out.toString(); } //----------------- Import ------------------ public void getArtistInfo(List<Artist> artistList) { LOG.debug("## ArtistCount: " + artistList.size()); Locale LastFMLocale = new Locale(settingsService.getLocale().toString()) ; LOG.debug("## LastFM Locale: " + LastFMLocale.toString()); for (Artist artist : artistList) { try{ if (artist.getArtistFolder() != null) { LastFMArtist lastFMartist = new LastFMArtist(); net.sourceforge.subsonic.lastfm.Artist tmpArtist = null; //String escapedArtist = stripNonValidXMLCharacters(StringEscapeUtils.escapeXml(artist.getArtistFolder()) ); String stripedArtist = stripNonValidXMLCharacters( artist.getArtistFolder() ); String RequestedArtist = (net.sourceforge.subsonic.lastfm.Artist.getCorrection(stripedArtist, api_key)).getName(); //todo:error try { tmpArtist = net.sourceforge.subsonic.lastfm.Artist.getInfo(RequestedArtist, LastFMLocale, null, api_key); } catch (Exception e) { Log.error("## FATAL Error! Artist Fetch! " + tmpArtist.getName()); } lastFMartist.setArtistname(tmpArtist.getName()); lastFMartist.setMbid(tmpArtist.getMbid()); lastFMartist.setUrl(tmpArtist.getUrl()); lastFMartist.setSince(tmpArtist.getSince()); lastFMartist.setPlayCount(tmpArtist.getPlaycount()); Collection<Album> TopAlbum = net.sourceforge.subsonic.lastfm.Artist.getTopAlbums(RequestedArtist, api_key, 3); String CollAlbum = null; for (Album album : TopAlbum) { if (album != null) { if (CollAlbum == null) { CollAlbum = album.getName(); }else { CollAlbum = CollAlbum + "|" + album.getName(); } } } lastFMartist.setTopalbum(CollAlbum); Collection<String> GenreTags = tmpArtist.getTags(); String CollTag = null; for(String TopTag : GenreTags) { if (TopTag != null) { if (CollTag == null) { CollTag = TopTag; }else { CollTag = CollTag + "|" + TopTag; } } } lastFMartist.setToptag(CollTag); for(String TopTag : GenreTags) { if (TopTag != null) { lastFMartist.setGenre(TopTag); break; } } // String[] sep = CollTag.split("\\|"); // List list = Arrays.asList(sep); String tmpSum = tmpArtist.getWikiSummary(); tmpSum = StringUtil.removeMarkup(tmpSum); // String tmpText = tmpArtist.getWikiText(); // tmpText = StringUtil.removeMarkup(tmpText); // lastFMartist.setBio(tmpText); lastFMartist.setSummary(tmpSum); // Collection<Tag> TopTags = net.sourceforge.subsonic.lastfm.Artist.getTopTags(tmpArtist.getName(), api_key, 1); Collection<net.sourceforge.subsonic.lastfm.Artist> Similar = net.sourceforge.subsonic.lastfm.Artist.getSimilar(tmpArtist.getName(), 6, api_key); for (net.sourceforge.subsonic.lastfm.Artist x : Similar) { LastFMArtistSimilar s = new LastFMArtistSimilar(); s.setArtistName(tmpArtist.getName()); s.setArtistMbid(tmpArtist.getMbid()); s.setSimilarName(x.getName()); s.setSimilarMbid(x.getMbid()); lastFMArtistSimilarDao.createOrUpdateLastFMArtistSimilar(s); } // /** // * new Artist image importer workaround // */ // if (tmpArtist != null) { // lastFMartist.setCoverart1(tmpArtist.getImageURL(ImageSize.EXTRALARGE)); // } // deprecated // PaginatedResult <Image> artistImage = net.sourceforge.subsonic.lastfm.Artist.getImages(RequestedArtist, 1, 5, api_key); // Collection <Image> Imgs = artistImage.getPageResults(); // // int counter = 0; // for (Image Img : Imgs) // { switch(counter) // { case 0: lastFMartist.setCoverart1(Img.getImageURL(ImageSize.LARGESQUARE));break; // case 1: lastFMartist.setCoverart2(Img.getImageURL(ImageSize.LARGESQUARE));break; // case 2: lastFMartist.setCoverart3(Img.getImageURL(ImageSize.LARGESQUARE));break; // case 3: lastFMartist.setCoverart4(Img.getImageURL(ImageSize.LARGESQUARE));break; // case 4: lastFMartist.setCoverart5(Img.getImageURL(ImageSize.LARGESQUARE));break; // } // counter++; // } if (lastFMartist.getArtistname() != null) { LOG.info("## LastFM ArtistInfo Update: " + lastFMartist.getArtistname()); lastFMArtistDao.createOrUpdateLastFMArtist(lastFMartist); } } } catch (NullPointerException ex) { System.out.println("## ERROR: " + artist.getName()); } } LOG.info("## LastFM ArtistScan Finished"); } public void getArtistInfo(LastFMArtist lastFMartist, String api_key){ try { /// LastFM API net.sourceforge.subsonic.lastfm.Artist Artist = net.sourceforge.subsonic.lastfm.Artist.getCorrection(lastFMartist.getArtistname(), api_key); lastFMartist.setArtistname(Artist.getName()); lastFMartist.setMbid(Artist.getMbid()); } catch (Exception x) { LOG.warn("## Failed to Update ArtistCover: " + lastFMartist.getArtistname(), x); } } /** * Artist image importer */ public void getArtistImage(MediaFile mediaFileArtist, String api_key){ if (mediaFileArtist.getCoverArtPath() == null || mediaFileArtist == null) { try { /// LastFM API String artistName = net.sourceforge.subsonic.lastfm.Artist.getCorrection(mediaFileArtist.getArtist(), api_key).getName(); net.sourceforge.subsonic.lastfm.Artist artist = net.sourceforge.subsonic.lastfm.Artist.getInfo(artistName, api_key); setCoverArtImage (mediaFileArtist.getId(), artist.getImageURL(ImageSize.MEGA), true); LOG.info("## Update ArtistCover: " + mediaFileArtist.getArtist()); } catch (Exception x) { LOG.warn("## Failed to Update ArtistCover: " + mediaFileArtist.getArtist(), x); } } } public void getArtistBio(LastFMArtist lastFMartist, String api_key){ try { /// LastFM API String artist = net.sourceforge.subsonic.lastfm.Artist.getCorrection(lastFMartist.getArtistname(), api_key).getName(); String summary = getSummary(artist, api_key); lastFMartist.setSummary(summary); System.out.println("summary: "); System.out.println(summary); } catch (Exception x) { LOG.warn("## Failed to Update ArtistCover: " + lastFMartist.getArtistname(), x); } } public static String getInfo(String artistName, String apiKey){ // net.sourceforge.subsonic.lastfm.Artist ArtistTest1 = net.sourceforge.subsonic.lastfm.Artist.getInfo(net.sourceforge.subsonic.lastfm.Artist.getCorrection("AC-DC", apiKey).getName(), apiKey); net.sourceforge.subsonic.lastfm.Artist temp = net.sourceforge.subsonic.lastfm.Artist.getInfo(artistName, apiKey); return temp.getWikiSummary(); //.getWikiSummary(); //getWikiText(); //Also .getWikiSummary(), .getWikiLastChanged(), etc... } public static String getSummary(String artistName, String apiKey){ net.sourceforge.subsonic.lastfm.Artist temp = net.sourceforge.subsonic.lastfm.Artist.getInfo(artistName, apiKey); return temp.getWikiSummary(); //.getWikiSummary(); //getWikiText(); //Also .getWikiSummary(), .getWikiLastChanged(), etc... } public String setCoverArtImage(int id, String url, boolean isArtist) { try { MediaFile mediaFile = mediaFileService.getMediaFile(id); if (mediaFile.isAlbum() || mediaFile.isAlbumSet() ){ isArtist = false; } saveCoverArt(mediaFile.getPath(), url , isArtist); return null; } catch (Exception x) { LOG.warn("Failed to save cover art for media " + id, x); return x.toString(); } } private void saveCoverArt(String path, String url, boolean isArtist) throws Exception { InputStream input = null; HttpClient client = new DefaultHttpClient(); try { HttpConnectionParams.setConnectionTimeout(client.getParams(), 20 * 1000); // 20 seconds HttpConnectionParams.setSoTimeout(client.getParams(), 20 * 1000); // 20 seconds HttpGet method = new HttpGet(url); org.apache.http.HttpResponse response = client.execute(method); input = response.getEntity().getContent(); // Attempt to resolve proper suffix. String suffix = "jpg"; if (url.toLowerCase().endsWith(".gif")) { suffix = "gif"; } else if (url.toLowerCase().endsWith(".png")) { suffix = "png"; } String coverName = "cover."; if (isArtist == true) { coverName = "artist."; } // Check permissions. File newCoverFile = new File(path, coverName + suffix); if (!securityService.isWriteAllowed(newCoverFile)) { throw new Exception("Permission denied: " + StringUtil.toHtml(newCoverFile.getPath())); } // If file exists, create a backup. backup(newCoverFile, new File(path, coverName + "backup." + suffix)); // Write file. IOUtils.copy(input, new FileOutputStream(newCoverFile)); MediaFile mediaFile = mediaFileService.getMediaFile(path); // Rename existing cover file if new cover file is not the preferred. try { File coverFile = mediaFileService.getCoverArt(mediaFile); if (coverFile != null) { if (!newCoverFile.equals(coverFile)) { coverFile.renameTo(new File(coverFile.getCanonicalPath() + ".old")); LOG.info("Renamed old image file " + coverFile); } } } catch (Exception x) { LOG.warn("Failed to rename existing cover file.", x); } mediaFileService.refreshMediaFile(mediaFile); } finally { IOUtils.closeQuietly(input); client.getConnectionManager().shutdown(); } } private void backup(File newCoverFile, File backup) { if (newCoverFile.exists()) { if (backup.exists()) { backup.delete(); } if (newCoverFile.renameTo(backup)) { LOG.info("Backed up old image file to " + backup); } else { LOG.warn("Failed to create image file backup " + backup); } } } public void setSecurityService(SecurityService securityService) { this.securityService = securityService; } public void setSettingsService(SettingsService settingsService) { this.settingsService = settingsService; } public void setMediaFileService(MediaFileService mediaFileService) { this.mediaFileService = mediaFileService; } public void setLastFMArtistDao(LastFMArtistDao lastFMArtistDao) { this.lastFMArtistDao = lastFMArtistDao; } public void setLastFMArtistSimilarDao(LastFMArtistSimilarDao lastFMArtistSimilarDao) { this.lastFMArtistSimilarDao = lastFMArtistSimilarDao; } }