001/* 002 * 003 * $Revision: 1333 $ 004 * $Date: 2020-05-08 14:01:10 -0700 (Fri, 08 May 2020) $ 005 * $URL: svn://fred.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/widgets/tunes/TunesHelper.java $ 006 * 007 * Written by Tom Gutwin - WebARTS Design. 008 * http://www.webarts.bc.ca 009 * Copyright (C) 2016-2019 WebARTS Design, North Vancouver Canada 010 * This program is free software; you can redistribute it and/or modify 011 * it under the terms of the GNU General Public License as published by 012 * the Free Software Foundation; either version 2 of the License, or 013 * (at your option) any later version. 014 * 015 * This program is distributed in the hope that it will be useful, 016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 018 * GNU General Public License for more details. 019 * 020 * You should have received a copy of the GNU General Public License 021 * along with this program; if not, write to the Free Software 022 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 023 */ 024 025package ca.bc.webarts.widgets.tunes; 026 027import ca.bc.webarts.widgets.Util; 028import ca.bc.webarts.tools.MusicBrainzRestRequester; 029 030import java.io.File; 031import java.text.SimpleDateFormat; 032import java.util.Date; 033import java.util.ArrayList; 034import java.util.Calendar; 035import java.util.Collections; 036import java.util.HashMap; 037import java.util.Iterator; 038import java.util.Locale; 039import java.util.StringTokenizer ; 040import java.util.Vector; 041import java.text.NumberFormat; 042 043import javax.json.*; 044import javax.json.stream.JsonGenerator; 045import javax.json.stream.JsonParser; 046 047import javax.servlet.http.HttpSession; 048 049//NEWlast.fm api using 2.0 API 050//import de.umass.lastfm.Authenticator; 051import de.umass.lastfm.Caller; 052import de.umass.lastfm.Event; 053//import de.umass.lastfm.; 054import de.umass.lastfm.Session; 055import de.umass.lastfm.Track; 056import de.umass.lastfm.User; 057import de.umass.lastfm.scrobble.ResponseStatus; 058import de.umass.lastfm.scrobble.Scrobbler; 059import de.umass.lastfm.scrobble.ScrobbleData; 060import de.umass.lastfm.scrobble.ScrobbleResult; 061import de.umass.lastfm.scrobble.Source; 062import de.umass.lastfm.PaginatedResult; 063 064/** 065 * A helper Bean for the WebARTS Tunes Web Application. 066 * 067 * @author TGutwin 068 */ 069public class TunesHelper 070{ 071 /** A holder for this clients System File Separator. */ 072 public final static String SYSTEM_FILE_SEPERATOR = java.io.File.separator; 073 074 /** A holder for this clients System line termination separator. */ 075 public final static String SYSTEM_LINE_SEPERATOR = 076 System.getProperty("line.separator"); 077 public final static String DEFAULT_COLUMN_DELIMITOR = "|"; 078 079 public final static String LASTFM_USER_AGENT = "jOggPlayer"; 080 public final static String LASTFM_API_KEY = "93df6c849563eef04d7d48db5950d0c7"; //"${lastfm.key}"; // jOggPlayer Key 081 public final static String LASTFM_API_KEY_SECRET = "aa622b7756440145b4405001305b9112"; //"${lastfm.secret}"; // jOggPlayer secret 082 /** DEFAULT Last.FM username to use: admin .**/ 083 protected static final String LASTFM_DEFAULT_USERNAME = "tgutwin"; //"${lastfm.user}"; 084 /** DEFAULT Last.FM password to use: admin .**/ 085 protected static final String LASTFM_DEFAULT_PASSWORD = "~19AcrobatiC19~~~"; //"${lastfm.pass}"; 086 087 private String columnDelimitor = DEFAULT_COLUMN_DELIMITOR; 088 private String webappRootPath_ = "/opt/jetty/baseDir/webapps"; 089 private static String webAppContext_ = "tunes"; 090 private String webappHomePath_ = webappRootPath_ + "/" + webAppContext_; 091 public static String webappHomeURLTLS_ = "https://fred.webarts.bc.ca:9443" + "/" + webAppContext_; 092 public static String webappHomeURL_ = "http://fred.webarts.bc.ca:81" + "/" + webAppContext_; 093 private static String tunesSubPath_ = "tunes"; 094 private static String imagesSubPath_ = "images"; 095 private String lastFmUserID_ = LASTFM_DEFAULT_USERNAME; 096 private String lastFmPassword_ = LASTFM_DEFAULT_PASSWORD; 097 098 private boolean lastFmIsInit_ = false; 099 private Caller lastFm_ = Caller.getInstance(); 100 private Session lastFmSession_ = null; 101 private User lastFmUser_ = null; 102 private PaginatedResult<Track> lastFmLovedTracks_ = null; 103 private Vector<String> lastFmLovedTrackNames_ = null; 104 private Vector<String> lastFmRecentSongs_ = null; 105 106 long randLong = Math.round(Math.random()*10000000); 107 String randStr = String.valueOf(randLong); 108 109 public Vector <Artist> artists_ = new Vector <Artist>(); 110 111 112 /** Constructor for the TunesHelper object. */ 113 public TunesHelper() 114 { 115 System.out.println("[ca.bc.webarts.widgets > TunesHelper()] " + 116 (new java.util.Date()).toString()); 117 }// -- Constructor 118 119 120 /** Constructor for the TunesHelper object to allow custom root path, but uses the default context = 'tunes'. */ 121 public TunesHelper(String webappRootPath, String tunesSubPath, String webappHomeURL) 122 { 123 System.out.println("[ca.bc.webarts.widgets > TunesHelper("+webappRootPath+","+tunesSubPath+","+webappHomeURL+")] " + 124 (new java.util.Date()).toString()); 125 126 tunesSubPath_ = tunesSubPath; 127 setWebappHomeURL(webappHomeURL); 128 setWebappRootPath(webappRootPath); 129 setWebappHomePath(); 130 }// -- Constructor 131 132 133 public static void main(String [] args) 134 { 135 final String methodName = "ca.bc.webarts.widgets.tunes.TunesHelper" + ": main()"; 136 137 String webappName = "tunes"; 138 String deployServerTLS = "https://fred.webarts.bc.ca:9443"; 139 String deployServer = "http://fred.webarts.bc.ca:81"; 140 String relativeWebappUrlStr = "/"+webappName; 141 String webAppHome = "/opt/jetty/baseDir/webapps"; 142 143 String tunesSubdir = "tunes"; 144 String tunesDir = webAppHome + "/" + webappName; 145 String webappUrlStr = deployServer + relativeWebappUrlStr; 146 String webappTLSUrlStr = deployServerTLS + relativeWebappUrlStr; 147 148 TunesHelper helper = new TunesHelper(webAppHome, tunesSubdir, webappUrlStr); 149 if (helper!=null) 150 { 151 helper.parseTunesDir(); 152 Vector<Artist> artistVect = helper.getArtists(); 153 //java.util.Collections(artistVect); // should already be sorted 154 String[] artNames=helper.getArtistNames(true); 155 String[] artNamesWithSpaces=helper.getArtistNames(false); 156 int numArts =helper.getNumberOfArtists(); 157 int numAlbums =helper.getNumberOfAlbums(); 158 int numSongs =helper.getNumberOfSongs(); 159 160 Util.writeStringToFile(helper.toJsonString(), "."+SYSTEM_FILE_SEPERATOR+"tunesList.json" ); 161 } 162 } 163 164 165 /** Parses the tunes dir and reads in all the artists_. **/ 166 public void parseTunesDir() 167 { 168 int numValidArtDirs = 0; 169 File[] artistDirFiles = (new File(getTunesDirPath())).listFiles(); 170 boolean useMusicBrainz = getTunesDirPath().contains("testTunes"); 171 useMusicBrainz = true; // now rely on the MB settings for useCacheOnly 172 java.util.Arrays.sort(artistDirFiles); 173 if(artistDirFiles!=null) 174 { 175 artists_ = new Vector <Artist>(); 176 try 177 { 178 for(File currFile : artistDirFiles) 179 if(currFile.isDirectory() 180 && currFile.canRead() ) 181 { 182 // check if the artist dir has ANY album dirs, then add 183 File[] subFiles = (new File(currFile.getAbsolutePath())).listFiles(); 184 boolean valid = false; 185 try 186 { 187 for(int i=0; !valid&&i<subFiles.length; i++) 188 { 189 if(subFiles[i].isDirectory() 190 && subFiles[i].canRead() 191 && subFiles[i].listFiles().length>0 192 ) valid= true; 193 } 194 if(valid) 195 { 196 //System.out.print("\n [ca.bc.webarts.widgets > TunesHelper.parseTunesDir() "); 197 //System.out.println(currFile.getAbsolutePath()); 198 Artist art = new Artist(currFile.getAbsolutePath(), useMusicBrainz, numValidArtDirs++); 199 artists_.add(art); 200 // now mark last.fm loved songs using lastFmLovedTracks_ 201 art.markLovedSongs(lastFmLovedTrackNames_); 202 } 203 } 204 catch ( Exception ex) 205 { 206 System.out.println(" *!What The Fudge (parseTunesDir()) : "+ex.getMessage()); 207 ex.printStackTrace(); 208 } 209 } 210 } 211 catch(Exception ex) { /* ignore error dirs */ } 212 artists_.sort(Artist.ArtistComparatorIgnoreCase ); 213 } 214 } 215 216 217 public Vector<String> getlastFmLovedTrackNames() { return lastFmLovedTrackNames_;} 218 219 220 /** 221 * Set Method for class field 'lastFmUserID_'. 222 * 223 * @param lastFmUserID is the value to set this class field to. 224 * 225 **/ 226 public void setLastFmUserID(String lastFmUserID) 227 { 228 this.lastFmUserID_ = lastFmUserID; 229 } // setLastFmUserID Method 230 231 232 /** 233 * Get Method for class field 'lastFmUserID_'. 234 * 235 * @return String - The value the class field 'lastFmUserID_'. 236 * 237 **/ 238 public String getLastFmUserID() 239 { 240 if(lastFmUserID_==null || "".equals(lastFmUserID_)) setLastFmUserID( LASTFM_DEFAULT_USERNAME); 241 return lastFmUserID_; 242 } // getLastFmUserID Method 243 244 245 /** 246 * Set Method for class field 'lastFmPassword_'. 247 * 248 * @param lastFmPassword is the value to set this class field to. 249 * 250 **/ 251 public void setLastFmPassword(String lastFmPassword) 252 { 253 this.lastFmPassword_ = lastFmPassword; 254 } // setLastFmPassword Method 255 256 257 /** 258 * Get Method for class field 'lastFmPassword_'. 259 * 260 * @return String - The value the class field 'lastFmPassword_'. 261 * 262 **/ 263 public String getLastFmPassword() 264 { 265 if(lastFmPassword_==null || "".equals(lastFmPassword_)) setLastFmPassword( LASTFM_DEFAULT_PASSWORD); 266 return this.lastFmPassword_; 267 } // getLastFmPassword Method 268 269 270 public boolean initLastFm(){ return initLastFm(false);} 271 public boolean initLastFm(boolean reInit) 272 { 273 if(reInit || !lastFmIsInit_ ) 274 { 275 System.out.println(" #last.fm initing... "); 276 lastFm_.setUserAgent(LASTFM_USER_AGENT); //tst 277 lastFm_.setDebugMode(false); 278 lastFmIsInit_ = true; 279 280 } 281 return lastFmIsInit_; 282 } 283 284 285 /** 286 * Session keys have an infinite lifetime by default. so store it as a cookie to be re-used. You are recommended to store the key securely. 287 * Users are able to revoke privileges for your application on their Last.fm settings screen, rendering session keys invalid. This method 288 * uses an existing user session. 289 * 290 **/ 291 public boolean initLastFmUserSession(){return initLastFmUserSession(false, getLastFmUserID(), getLastFmPassword());} 292 /** 293 * Session keys have an infinite lifetime by default. so store it as a cookie to be re-used. You are recommended to store the key securely. 294 * Users are able to revoke privileges for your application on their Last.fm settings screen, rendering session keys invalid. 295 * 296 * @param reInit true flags to re-inititalize a new session, false trys to restore an existing session 1st. 297 **/ 298 public boolean initLastFmUserSession(boolean reInit){return initLastFmUserSession(reInit, getLastFmUserID(), getLastFmPassword());} 299 /** 300 * Session keys have an infinite lifetime by default. so store it as a cookie to be re-used. You are recommended to store the key securely. 301 * Users are able to revoke privileges for your application on their Last.fm settings screen, rendering session keys invalid. 302 * 303 * @param reInit true flags to re-inititalize a new session, false trys to restore an existing session 1st. 304 * @param u is the userID to login with 305 * @param p is the password to login with 306 **/ 307 public boolean initLastFmUserSession(boolean reInit, String u, String p) 308 { 309 de.umass.lastfm.Result lastResult = null; 310 if(reInit || lastFmSession_==null || lastFmUser_==null) 311 { 312 System.out.println("\n !!!!!!" ); 313 System.out.println(" ]]"+(reInit?"RE-":"")+"init Last.FM session u="+ u+" p="+p); 314 initLastFm(); 315 lastFmSession_ = de.umass.lastfm.Authenticator.getMobileSession(u, p, LASTFM_API_KEY, LASTFM_API_KEY_SECRET); 316 if(lastFmSession_!=null) 317 { 318 System.out.println("]]init Last.FM session Created?: "+ lastFmSession_); 319 System.out.println("]]init Last.FM sessionKEY Created?: "+ retrieveLastFmSessionKey()+"\n"); 320 System.out.println("]]init Last.FM session Username = "+ retrieveLastFmSessionUsername()+"\n"); 321 //lastFmUser_ = User.getInfo(lastFmSession_); 322 lastFmUser_ = User.getInfo(u, LASTFM_API_KEY); 323 System.out.println("]]init Last.FM USER Created? "+(lastFmUser_!=null)); 324 if (lastFmUser_!=null) 325 { 326 getLastFmLovedTrackNamesForUser(u); 327 } 328 else 329 { 330 lastResult = lastFm_.getLastResult(); 331 if(lastResult.getStatus()==de.umass.lastfm.Result.Status.valueOf("FAILED")) 332 System.out.println("\n >!>!>! Last.FM ERROR USER NOT Created ("+lastResult.getErrorCode()+") "+lastResult.getErrorMessage()+"\n"); 333 } 334 } 335 else 336 { 337 lastResult = lastFm_.getLastResult(); 338 if(lastResult.getStatus()==de.umass.lastfm.Result.Status.valueOf("FAILED")) 339 System.out.println("\n >!>!>! Last.FM ERROR sessionKEY NOT Created ("+lastResult.getErrorCode()+") "+lastResult.getErrorMessage()+"\n"); 340 } 341 } 342 else System.out.println(" ]] USING Existing LastFM Session ("+retrieveLastFmSessionKey()+") & User="+retrieveLastFmSessionUsername()); 343 return (lastFmSession_!=null); //&&!retrieveLastFmSessionUsername().equals("")); 344 } 345 346 347 /** 348 * Restores an existing Session instance with the given session key. 349 **/ 350 public boolean restoreLastFmUserSession(String sessionKey) 351 { 352 boolean retVal = false; 353 de.umass.lastfm.Result lastResult = null; 354 if(sessionKey!=null && !sessionKey.equals("")) 355 { 356 initLastFm(); 357 lastFmSession_ = Session.createSession(LASTFM_API_KEY, LASTFM_API_KEY_SECRET, sessionKey); 358 System.out.println("\n]]restore Last.FM session Created "+ retrieveLastFmSessionKey() ); 359 System.out.println("\n]]restore Last.FM sessionUsername "+ retrieveLastFmSessionUsername() ); 360 if(lastFmSession_!=null) 361 { 362 lastFmUser_ = User.getInfo(lastFmSession_); 363 lastResult = lastFm_.getLastResult(); 364 if(lastResult.getStatus()==de.umass.lastfm.Result.Status.valueOf("FAILED")) 365 System.out.println("\n !>!>!> Last.FM ERROR USER NOT Created ("+lastResult.getErrorCode()+") "+lastResult.getErrorMessage()+"\n"); 366 if(lastFmUser_==null) 367 { 368 retVal = initLastFmUserSession(true, getLastFmUserID(), getLastFmPassword()); 369 //lastFmUser_ = User.getInfo(lastFmSession_); 370 lastFmUser_ = User.getInfo(lastFmSession_.getUsername(), LASTFM_API_KEY); 371 } 372 System.out.println(" ]]restore Last.FM USER Created "+(lastFmUser_!=null)); 373 374 if(lastFmUser_!=null) 375 { 376 System.out.println(" ]] LastFmUsername="+lastFmUser_.getName()+" LastFmUserID="+lastFmUser_.getId()); 377 System.out.println(" ]] LastFmSessionUsername="+retrieveLastFmSessionUsername()); 378 setLastFmUserID(lastFmUser_.getName()); 379 setLastFmPassword(lastFmPassword_); 380 } 381 System.out.println(" ]] TunesHelper. lastFmUserID_"+lastFmUserID_+" lastFmPassword_="+lastFmPassword_); 382 System.out.println(" ]] TunesHelper. lastFmUser_==null = "+(lastFmUser_==null)); 383 System.out.println(" ]] TunesHelper. retrieveLastFmSessionUsername()==null = "+retrieveLastFmSessionUsername()); 384 if(lastFmUser_==null || retrieveLastFmSessionUsername()==null || retrieveLastFmSessionUsername().equals("")) 385 retVal = initLastFmUserSession(true, getLastFmUserID(), getLastFmPassword()); 386 retVal = (lastFmUser_!=null&&!retrieveLastFmSessionUsername().equals("")); //(lastFm_.getLastResult()!=null?lastFm_.getLastResult().isSuccessful():false); 387 if (lastFmUser_!=null) 388 { 389 retrieveLastFmLovedTrackNames(); 390 } 391 } 392 else 393 { 394 lastResult = lastFm_.getLastResult(); 395 if(lastResult.getStatus()==de.umass.lastfm.Result.Status.valueOf("FAILED")) 396 System.out.println("\n !>!>!> Last.FM ERROR sessionKEY NOT Created ("+lastResult.getErrorCode()+") "+lastResult.getErrorMessage()+"\n"); 397 } 398 } 399 return retVal; 400 } 401 402 403 /** Queries this classes already defined LastFM user's Loved Tracks, and returns a Vector of their names AND assigns it to class var lastFmLovedTrackNames_. **/ 404 public Vector<String> retrieveLastFmLovedTrackNames() 405 { 406 return getLastFmLovedTrackNames(true); 407 } 408 /** Returns the class var lastFmLovedTrackNames_ directly without retrieving from this classes already defined LastFM user. **/ 409 public Vector<String> getLastFmLovedTrackNames() 410 { 411 return getLastFmLovedTrackNames(false); 412 } 413 /** Returns the class var lastFmLovedTrackNames_ with the option to retrieve them from this classes already defined LastFM user. 414 * If retrieved, it will also mark the class artists__ songs as Loved. 415 **/ 416 public Vector<String> getLastFmLovedTrackNames(boolean retrieve) 417 { 418 de.umass.lastfm.Result lastResult = null; 419 if (retrieve && lastFmUser_!=null) 420 { 421 System.out.println("]]getLastFmLovedTrackNames for Last.FM USER: "+retrieveLastFmSessionUsername()); 422 lastFmLovedTracks_ = lastFmUser_.getLovedTracks(getLastFmUserID(), LASTFM_API_KEY); 423 int numPages = 0; 424 if (lastFmLovedTracks_!=null) 425 { 426 numPages = lastFmLovedTracks_.getTotalPages(); 427 lastFmLovedTrackNames_ = new Vector<String>(); 428 for (int i=1; i<= numPages; i++) 429 { 430 lastFmLovedTracks_ = lastFmUser_.getLovedTracks(getLastFmUserID(), i, LASTFM_API_KEY); 431 for (Track currTrack : lastFmLovedTracks_) lastFmLovedTrackNames_.addElement(currTrack.getName().trim()); 432 for (Artist art : artists_) art.markLovedSongs( lastFmLovedTrackNames_ ); 433 } 434 //System.out.println(" ]] lastFmLovedTrackNames_ ("+ lastFmLovedTrackNames_.size() +") = \n" + lastFmLovedTrackNames_); 435 } 436 else System.out.println("\n >!>!>! Last.FM ERROR lastFmLovedTrackNames_ NOT Available \n"); 437 } 438 else 439 { 440 lastResult = lastFm_.getLastResult(); 441 if(lastResult.getStatus()==de.umass.lastfm.Result.Status.valueOf("FAILED")) 442 System.out.println("\n >!>!>! Last.FM ERROR USER NOT Created ("+lastResult.getErrorCode()+") "+lastResult.getErrorMessage()+"\n"); 443 } 444 445 return lastFmLovedTrackNames_; 446 } 447 448 449 /** retrieves loved trackNames for the specified LastFM user. **/ 450 public Vector<String> getLastFmLovedTrackNamesForUser(String lastFmUsername) 451 { 452 Vector<String> retVal = null; 453 de.umass.lastfm.Result lastResult = null; 454 if(lastFmUsername!=null && !"".equals(lastFmUsername)) 455 { 456 User user = User.getInfo(lastFmUsername, LASTFM_API_KEY); 457 System.out.println("]] Last.FM USER info retrieved? "+(user!=null)); 458 Vector<String> lovedTrackNames = null; 459 460 if (user!=null) 461 { 462 System.out.println("]]getLastFmLovedTrackNamesForUser("+lastFmUsername+") for Last.FM USER: "+retrieveLastFmSessionUsername()); 463 PaginatedResult<Track> lovedTracks = null; 464 lovedTracks = user.getLovedTracks(lastFmUsername, LASTFM_API_KEY); 465 int numPages = 0; 466 if (lovedTracks!=null) 467 { 468 numPages = lovedTracks.getTotalPages(); 469 lovedTrackNames = new Vector<String>(); 470 for (int i=1; i<= numPages; i++) 471 { 472 lovedTracks = user.getLovedTracks(lastFmUsername, i, LASTFM_API_KEY); 473 for (Track currTrack : lovedTracks) lovedTrackNames.addElement(currTrack.getName().trim()); 474 if (lastFmUsername.equalsIgnoreCase(getLastFmUserID())) for (Artist art : artists_) art.markLovedSongs( lovedTrackNames ); 475 } 476 //System.out.println(" ]] lovedTrackNames ("+ lovedTrackNames.size() +") = \n" + lovedTrackNames); 477 retVal = lovedTrackNames; 478 } 479 else System.out.println("\n >!>!>! Last.FM ERROR lovedTrackNames NOT Available \n"); 480 } 481 else 482 { 483 lastResult = lastFm_.getLastResult(); 484 if(lastResult.getStatus()==de.umass.lastfm.Result.Status.valueOf("FAILED")) 485 System.out.println("\n >!>!>! Last.FM ERROR USER NOT Created ("+lastResult.getErrorCode()+") "+lastResult.getErrorMessage()+"\n"); 486 } 487 } 488 return retVal; 489 } 490 491 492 public Session getLastFmSession() { return lastFmSession_; } 493 public String retrieveLastFmAPIKey() { return ( lastFmSession_!=null ? lastFmSession_.getApiKey():""); } 494 public String retrieveLastFmSessionKey() { return ( lastFmSession_!=null ? lastFmSession_.getKey():""); } 495 public String retrieveLastFmSessionUsername() { return ( lastFmSession_!=null ? (lastFmSession_.getUsername()==null ?LASTFM_DEFAULT_USERNAME:lastFmSession_.getUsername()):""); } 496 public User getLastFmUser() { return lastFmUser_; } 497 public User retrieveLastFmSessionUser() { 498 User u = User.getInfo(lastFmSession_); if(u!=null) lastFmUser_ = u; 499 System.out.println("]]init Last.FM USER Retrieved? "+(u!=null)); 500 return lastFmUser_;} 501 public String retrieveLastFmUserId() { return ( lastFmUser_!=null?lastFmUser_.getId():""); } 502 public String retrieveLastFmUserImageURL() { return ( lastFmUser_!=null?lastFmUser_.getImageURL():""); } 503 public String retrieveLastFmUserUrl() { return ( lastFmUser_!=null?lastFmUser_.getUrl():""); } 504 505 506 /* 507 public boolean nowPlaying(String artistName, String trackName) 508 { // OLD API 509 boolean retVal = false; 510 ScrobbleResult sr = Track.updateNowPlaying(artistName, trackName, getLastFmSession()); 511 retVal = (sr.isSuccessful() && !sr.isIgnored()); 512 System.out.println("nowPlayimg OK? " + retVal); 513 return retVal; 514 } 515 **/ 516 517 518 /* 519 public boolean scrobble(String artistName, String trackName) 520 {// OLD API 521 boolean retVal = false; 522 int now = (int) (System.currentTimeMillis() / 1000); 523 ScrobbleResult sr = Track.scrobble(artistName, trackName, now, getLastFmSession()); 524 retVal = (sr.isSuccessful() && !sr.isIgnored()); 525 System.out.println("scrobbled OK? " + retVal); 526 return retVal; 527 } 528 */ 529 530 531 /* calls the last.fm now playing method with a full set of Track data. */ 532 public void nowPlaying(String artist, String title, int playTime, int duration, String album, String albumArtist, int trackNum) 533 { 534 System.out.println("\nlastfmPlay with ScrobbleData Object"); 535 final String art = Util.capsToSpacesInString(artist).replace("_"," ").replace(" "," ").replace(" "," ").replace("54•40","54-40").trim(); 536 final String albArt = Util.capsToSpacesInString(albumArtist).replace("_"," ").replace(" "," ").replace(" "," ").replace("54•40","54-40").trim(); 537 final String alb = Util.capsToSpacesInString(album).replace("_"," ").replace(" "," ").replace(" "," ").replace("A & M","A&M").replace("A& M","A&M").replace("A &M","A&M").trim(); 538 final String trk = Util.capsToSpacesInString(title).replace("_"," ").replace(" "," ").replace(" "," ").trim(); 539 final ScrobbleData sd = new ScrobbleData(art, trk, playTime, duration, alb, albArt, "", trackNum, ""); 540 // Run in separate thread. 541 Thread lastfmThread = 542 new Thread() 543 { 544 public void run() 545 { 546 ScrobbleResult result = Track.updateNowPlaying(sd, getLastFmSession()); 547 System.out.println("lastfmPlay sent ("+albArt+", "+art+", "+alb+", "+trk+") : " + (result.isSuccessful() && !result.isIgnored())); 548 } 549 }; 550 551 lastfmThread.start(); 552 553 } 554 555 556 /* calls the last.fm now playing method with a basic set of Track data. */ 557 public void nowPlaying(String artist, String title) 558 { 559 System.out.println("\nlastfmPlay with basic artist/title"); 560 String art = Util.capsToSpacesInString(artist).replace("_"," ").replace(" "," ").replace(" "," ").replace("54•40","54-40").trim(); 561 String trk = Util.capsToSpacesInString(title).replace(" "," ").replace(" "," ").trim(); 562 // Run in separate thread. 563 /* 564 Thread lastfmThread = 565 new Thread() 566 { 567 public void run() 568 { 569 */ 570 ScrobbleResult result = Track.updateNowPlaying(art, trk, getLastFmSession()); 571 System.out.println("lastfmPlay sent ("+art+", "+trk+") : " + (result.isSuccessful() && !result.isIgnored())); 572 /* 573 } 574 }; 575 576 lastfmThread.start(); 577 */ 578 579 } 580 581 582 /** Queries Last.fm and retrieves the number of time this track has been played by the logged in user. 583 **/ 584 public int retrieveLastfmTrackPlayCount(Track trk) 585 { 586 int retVal = -1; 587 if(trk!=null) 588 { 589 // getInfo WITH the username present has to be called to load the playcounts 590 Track t = trk.getInfo(trk.getArtist(), trk.getName(), null, getLastFmUserID(), retrieveLastFmAPIKey() ); 591 if(t!=null) retVal = t.getUserPlaycount(); 592 if(t!=null && retVal==-1) retVal = t.getPlaycount(); 593 } 594 return retVal; 595 } 596 597 598 public void scrobble(String artist, String title){ scrobble(artist, "", title);} 599 public void scrobble(String artist, String album, String title){ scrobble(artist, "", album, title);} 600 public void scrobble(String artist, String albumArtist, String album, String title) 601 { 602 System.out.println("\nscrobble with basic artist/album/title"); 603 604 if(albumArtist==null || albumArtist.trim().equals("")) albumArtist = artist; 605 if(albumArtist.toLowerCase().contains("various") ) albumArtist = "Various Artists"; 606 String art = Util.capsToSpacesInString(artist).replace(" "," ").replace(" "," ").replace("54•40","54-40").replace("x &y","x&y").trim(); 607 String albArt = Util.capsToSpacesInString(albumArtist).replace(" "," ").replace(" "," ").replace("54•40","54-40").replace("x &y","x&y").trim(); 608 String alb = Util.capsToSpacesInString(album).replace(" "," ").replace(" "," ").replace("A & M","A&M").replace("A& M","A&M").replace("A &M","A&M").trim(); 609 String trk = Util.capsToSpacesInString(title).replace(" "," ").replace(" "," ").trim(); 610 // Run in separate thread. 611 /* 612 Thread lastfmThread = 613 new Thread() 614 { 615 public void run() 616 { 617 */ 618 int now = (int) (System.currentTimeMillis() / 1000); 619 de.umass.lastfm.scrobble.ScrobbleData sd = new de.umass.lastfm.scrobble.ScrobbleData(art, trk, now ); 620 621 if(alb!=null && !alb.trim().equals("")) 622 { 623 /* Why does thisone NOT work without an ignored msg????? some of the meta-data like -1 ?? */ 624 //sd = new de.umass.lastfm.scrobble.ScrobbleData(art, trk, now, 0, alb, art, "", -1, ""); 625 626 sd.setAlbum(alb); 627 if(!alb.equals(albArt)) sd.setAlbumArtist(albArt); 628 629 //sd.setAlbum(playList.getSongAlbum(sel)); 630 //sd.setTrackNumber(playList.getTrackNumber(sel)); 631 } 632 633 ScrobbleResult result = de.umass.lastfm.Track.scrobble(sd, getLastFmSession()); 634 635 if (result != null && result.isSuccessful() && !result.isIgnored()) System.out.println("Song Sumbitted ! " ); 636 else 637 { 638 /* 639 Filtered Requests 640 641 A scrobble or Now Playing request may be ignored if we detect bad meta data. This is not treated as an error condition, so if filtering takes place the response will have an "ok" status. The server will return an ignored message with an associated ignored code. This information is useful if the client wants to show information about why a track was not added to the user's profile. 642 643 In the case of batch scrobble requests, each scrobble is filtered separately. So if only one scrobble has bad meta data and is ignored other scrobbles in the request will still be accepted. 644 645 Possible ignored message codes: 646 647 0. None (the request passed all filters). 648 1. Filtered artist. 649 2. Filtered track. 650 3. Timestamp too far in the past. 651 4. Timestamp too far in the future. 652 5. Max daily scrobbles exceeded. 653 654 */ 655 if (result.isIgnored()) System.out.println("Sumbmit was Ignored --> (" +result.getIgnoredMessageCode()+")"+result.getIgnoredMessage()); 656 else 657 { 658 System.out.println("ERROR Message --> " +result.getErrorMessage()); 659 System.out.println(" Result Error Code: "+result.getErrorCode() ); 660 System.out.println(" Result.toString(): \n"+result); 661 System.out.println("\n\n Error Codes:"); 662 System.out.println(" 8 : Operation Failed - Server Error. Please try again later."); 663 System.out.println(" 6 : Invalid track name supplied"); 664 System.out.println(" 2 : Invalid service - This service does not exist"); 665 System.out.println(" 3 : Invalid Method - No method with that name in this package"); 666 System.out.println(" 4 : Authentication Failed - You do not have permissions to access the service"); 667 System.out.println(" 5 : Invalid format - This service doesn't exist in that format"); 668 System.out.println(" 6 : Invalid parameters - Your request is missing a required parameter"); 669 System.out.println(" 7 : Invalid resource specified"); 670 System.out.println(" 8 : Operation failed - Something else went wrong"); 671 System.out.println(" 9 : Invalid session key - Please re-authenticate"); 672 System.out.println(" 10 : Invalid API key - You must be granted a valid key by last.fm"); 673 System.out.println(" 11 : Service Offline - This service is temporarily offline. Try again later."); 674 System.out.println(" 13 : Invalid method signature supplied"); 675 System.out.println(" 16 : There was a temporary error processing your request. Please try again"); 676 System.out.println(" 26 : Suspended API key - Access for your account has been suspended, please contact Last.fm"); 677 System.out.println(" 29 : Rate limit exceeded - Your IP has made too many requests in a short period"); 678 } 679 } 680 /* 681 } 682 }; 683 684 lastfmThread.start(); 685 */ 686 } 687 688 689 /* calls the last.fm love track method with a basic set of Track data. */ 690 public String loveTrack(String artist, String title) 691 { 692 String retVal = ""; 693 if(artist!=null && !"".equals(artist) && !"null".equalsIgnoreCase(artist) && 694 title!=null && !"".equals(title) && !"null".equalsIgnoreCase(title) ) 695 { 696 String art = Util.capsToSpacesInString(artist).replace("_"," ").replace(" "," ").replace(" "," ").replace("54•40","54-40").trim(); 697 String trk = Util.capsToSpacesInString(title).replace(" "," ").replace(" "," ").trim(); 698 /* 699 // Run in separate thread. 700 Thread lastfmThread = 701 new Thread() 702 { 703 public void run() 704 { 705 */ 706 de.umass.lastfm.Result result = Track.love(art, trk, getLastFmSession()); 707 System.out.println("loveTrack sent ("+art+", "+trk+") : " + (result.isSuccessful())); 708 if (!result.isSuccessful()) 709 { 710 System.out.println(" Result Error Msg : "+result.getErrorMessage() ); 711 System.out.println(" Result Error Code: "+result.getErrorCode() ); 712 System.out.println(" Error Codes:"); 713 System.out.println(" 8 : Operation Failed - Server Error. Please try again later."); 714 System.out.println(" 6 : Invalid track name supplied"); 715 System.out.println(" 2 : Invalid service - This service does not exist"); 716 System.out.println(" 3 : Invalid Method - No method with that name in this package"); 717 System.out.println(" 4 : Authentication Failed - You do not have permissions to access the service"); 718 System.out.println(" 5 : Invalid format - This service doesn't exist in that format"); 719 System.out.println(" 6 : Invalid parameters - Your request is missing a required parameter"); 720 System.out.println(" 7 : Invalid resource specified"); 721 System.out.println(" 8 : Operation failed - Something else went wrong"); 722 System.out.println(" 9 : Invalid session key - Please re-authenticate"); 723 System.out.println(" 10 : Invalid API key - You must be granted a valid key by last.fm"); 724 System.out.println(" 11 : Service Offline - This service is temporarily offline. Try again later."); 725 System.out.println(" 13 : Invalid method signature supplied"); 726 System.out.println(" 16 : There was a temporary error processing your request. Please try again"); 727 System.out.println(" 26 : Suspended API key - Access for your account has been suspended, please contact Last.fm"); 728 System.out.println(" 29 : Rate limit exceeded - Your IP has made too many requests in a short period"); 729 } 730 /* 731 } 732 }; 733 734 lastfmThread.start(); 735 */ 736 retVal = result.getErrorMessage(); //"false"; 737 if (result.isSuccessful()) retVal = art+"/"+trk; 738 } 739 740 return retVal; 741 742 } 743 744 745 /** Returns a wrapper for the LastFM Album or null if not valid name. Use the Album.getUrl() if needed. **/ 746 public static de.umass.lastfm.Album retrieveLastFmAlbum(String art, String albumName) 747 { 748 // this call is not reliable 749 System.out.println("\n >> retrieveLastFmAlbum: " + art +"_" + albumName); 750 de.umass.lastfm.Album retVal = de.umass.lastfm.Album.getInfo(art, albumName , LASTFM_DEFAULT_USERNAME, LASTFM_API_KEY); 751 return retVal; 752 } 753 754 755 /** Returns a wrapper for the LastFM Album or null if not valid name. Use the Album.getUrl() if needed. **/ 756 public static String retrieveLastFmAlbumUrl(String art, String albumName) 757 { 758 String retVal = ""; 759 if(false) 760 { 761 de.umass.lastfm.Album al = retrieveLastFmAlbum( art, albumName); 762 if(al!=null) 763 { 764 System.out.println("\n >>>> retrieveLastFmAlbumUrl: " + art +"_" +albumName); 765 retVal = al.getUrl() ; 766 } 767 } 768 else 769 { 770 if(albumName.contains("+") || albumName.contains("<") ) System.out.println("\n >>>>>> albumName: " + albumName.trim()); 771 retVal = "https://www.last.fm/music/"+Util.capsToSpacesInString(art).trim().replace("+","%252B").replace("_"," ").replace(" ","+").replace("++","+")+ 772 "/"+albumName.trim().replace("+"," %252B").replace("_"," ").replace(" ","+").replace("++","+").replace("/","%2f").replace("'","%27").replace("#","%23") ; 773 } 774 return retVal; 775 } 776 777 778 public de.umass.lastfm.Track [] retrieveLastFmRecentSongs() { return retrieveLastFmRecentSongs(getLastFmUserID());} 779 public de.umass.lastfm.Track [] retrieveLastFmRecentSongs(String uid ) 780 { 781 de.umass.lastfm.Track [] retVal = null; 782 Vector <de.umass.lastfm.Track> t = new Vector<de.umass.lastfm.Track>(); 783 PaginatedResult <de.umass.lastfm.Track> pgResults = null; //User.getRecentTracks(uid , LASTFM_API_KEY); 784 int numPages = 5; //pgResults.getTotalPages(); 785 for (int i=1; i<= numPages; i++) 786 { 787 pgResults = User.getRecentTracks(uid, i, 10, LASTFM_API_KEY); 788 //System.out.println("\n >>>> RecentTracks: Page["+i+"] = "+pgResults); 789 int curTrk = 0; 790 for (Track currTrack : pgResults) 791 { 792 //System.out.println(" >>>> >> "+curTrk++); 793 t.addElement(currTrack); 794 } 795 } 796 retVal= new de.umass.lastfm.Track [t.size()]; 797 for (int i=0; i< t.size(); i++) retVal[i] = t.get(i); 798 799 return retVal; 800 } 801 802 803 public Vector <Artist> getArtists(){ return getArtists(false);} 804 public Vector <Artist> getArtists(boolean sorted) 805 { 806 Vector <Artist> retVal = artists_; 807 if(sorted) 808 { 809 } 810 811 return retVal; 812 } 813 814 815 public String[] getArtistNames(){return getArtistNames(false);} 816 817 818 public String[] getArtistNames(boolean removeSpaces) 819 { 820 String [] retVal = new String[artists_.size()]; 821 for(int i=0;i<artists_.size();i++){retVal[i]=artists_.get(i).artistName(removeSpaces);} 822 return retVal; 823 } 824 825 public int getNumberOfArtists() {return artists_.size(); } 826 827 828 public int getNumberOfAlbums() 829 { 830 int retVal = 0; 831 for(int i=0;i<artists_.size();i++){retVal+=artists_.get(i).getNumberOfAlbums();} 832 return retVal; 833 } 834 835 836 public int getNumberOfAlbums(String artist) 837 { 838 int retVal = 0; 839 // not implemented 840 //for(int i=0;i<artists_.size();i++){retVal+=artists_.get(i).getNumberOfAlbums();} 841 return retVal; 842 } 843 844 845 public int getNumberOfSongs() 846 { 847 int retVal = 0; 848 for(int i=0;i<artists_.size();i++){retVal+=artists_.get(i).getNumberOfSongs();} 849 return retVal; 850 } 851 852 853 /** 854 * Set Method for class field 'webAppContext_'. 855 * 856 * @param webAppContext is the value to set this class field to. 857 * 858 **/ 859 public void setWebAppContext(String webAppContext) 860 { 861 this.webAppContext_ = webAppContext; 862 setWebappHomePath(); 863 } // setWebAppContext Method 864 865 866 /** 867 * Get Method for class field 'webAppContext_'. 868 * 869 * @return String - The value the class field 'webAppContext_'. 870 * 871 **/ 872 public static String getWebAppContext() 873 { 874 return webAppContext_; 875 } // getWebAppContext Method 876 877 878 /** 879 * Set Method for class field 'webappHomeURL'. 880 * 881 * @param webappHomeURL is the value to set this class field to. 882 * 883 **/ 884 public void setWebappHomeURL(String webappHomeURL) 885 { 886 this.webappHomeURL_ = webappHomeURL; 887 } // setWebappHomeURL Method 888 889 890 /** 891 * Get Method for class field 'webappHomeURL_'. 892 * 893 * @return String - The value the class field 'webappHomeURL_'. 894 * 895 **/ 896 public static String getWebappHomeURL() 897 { 898 return webappHomeURL_; 899 } // getwebappHomeURL Method 900 901 902 /** 903 * Get the URL for the Images dir. 904 * 905 * @return String - A URL pointing to the images dir. 906 * 907 **/ 908 public static String getWebappImagesURL() 909 { 910 return webappHomeURL_+ "/" + imagesSubPath_; 911 } // getWebappImagesURL Method 912 913 914 /** 915 * Get the URL for the Tunes dir. 916 * 917 * @return String - A URL pointing to the images dir. 918 * 919 **/ 920 public static String getWebappTunesURL() 921 { 922 return webappHomeURL_+ "/" + tunesSubPath_; 923 } // getWebappTunesURL Method 924 925 926 /** 927 * Set Method for class field 'tunesSubPath_'. 928 * 929 * @param tunesSubPath is the value to set this class field to. 930 * 931 **/ 932 public static void setTunesSubPath(String tunesSubPath) 933 { 934 tunesSubPath_ = tunesSubPath; 935 } // setTunesSubPath Method 936 937 938 /** 939 * Get Method for class field 'tunesSubPath_'. 940 * 941 * @return String - The value the class field 'tunesSubPath_'. 942 * 943 **/ 944 public static String getTunesSubPath() 945 { 946 return tunesSubPath_; 947 } // getTunesSubPath Method 948 949 950 /** 951 * Set Method for class field 'webappRootPath_'. 952 * 953 * @param webappRootPath is the value to set this class field to. 954 * 955 **/ 956 public void setWebappRootPath(String webappRootPath) 957 { 958 this.webappRootPath_ = webappRootPath; 959 setWebappHomePath(); 960 } // setWebappRootPath Method 961 962 963 /** 964 * Get Method for class field 'webappRootPath_'. 965 * 966 * @return String - The value the class field 'webappRootPath'. 967 * 968 **/ 969 public String getWebappRootPath() 970 { 971 return webappRootPath_; 972 } // getWebappRootPath Method 973 974 975 /** 976 * Reset Method for class field 'webappHomePath_' to its root + context path. 977 * 978 * @param webappHomePath is the value to set this class field to. 979 * 980 **/ 981 public void setWebappHomePath() 982 { 983 this.webappHomePath_ = getWebappRootPath() + "/" + getWebAppContext(); 984 } // setWebappHomePath Method 985 986 987 /** 988 * DEPRECATED - Directly Set Method for class field 'webappHomePath_' which over-rides the root and contect settings (bad). 989 * 990 * @param webappHomePath is the value to set this class field to. 991 * 992 **/ 993 private void setWebappHomePath(String webappHomePath) 994 { 995 setWebappRootPath(webappHomePath.substring(0,webappHomePath.lastIndexOf("/")-1)); 996 setWebAppContext(webappHomePath.substring(webappHomePath.lastIndexOf("/")+1)); 997 //this.webappHomePath_ = webappHomePath; 998 } // setWebappHomePath Method 999 1000 1001 /** 1002 * Get Method for class field 'webappHomePath_'. 1003 * 1004 * @return String - The value the class field 'webappHomePath_'. 1005 * 1006 **/ 1007 public String getWebappHomePath() 1008 { 1009 return webappHomePath_; 1010 } // getWebappHomePath Method 1011 1012 1013 /** 1014 * Gets the full file path to the tunes dir. 1015 * 1016 * @return String - the full file path to the tunes dir. 1017 * 1018 **/ 1019 public String getTunesDirPath() 1020 { 1021 return getWebappHomePath() + "/" + tunesSubPath_; 1022 } // getWebappHomePath Method 1023 1024 1025 private String cleanJSON(StringBuilder sb) 1026 { 1027 String retVal = sb.toString(); 1028 retVal = Util.tokenReplace(retVal, ","+SYSTEM_LINE_SEPERATOR+" ,", ","); 1029 retVal = Util.tokenReplace(retVal, ","+SYSTEM_LINE_SEPERATOR+" ,", ","); 1030 retVal = Util.tokenReplace(retVal, ","+SYSTEM_LINE_SEPERATOR+" ,", ","); 1031 retVal = Util.tokenReplace(retVal, ","+SYSTEM_LINE_SEPERATOR+",", ","); 1032 return retVal; 1033 } 1034 1035 1036 /** lists all songs in all artist's albums in a JSON string. 1037 /** 1038 <pre> 1039 [ 1040 { 1041 url: "http://warp4.webarts.bc.ca/tunes/mythTunes/3DoorsDown/3DoorsDown/3DoorsDown03-It'sNotMyTime.ogg", 1042 file: "3DoorsDown03-It'sNotMyTime.ogg", 1043 title: "It'sNotMyTime", 1044 album: "3DoorsDown", 1045 artist: "3DoorsDown", 1046 number: "03", 1047 index: "26" 1048 }, 1049 { 1050 url: "http://warp4.webarts.bc.ca/tunes/mythTunes/3DoorsDown/3DoorsDown/3DoorsDown04-LetMeBeMyself.ogg", 1051 file: "3DoorsDown04-LetMeBeMyself.ogg", 1052 title: "LetMeBeMyself", 1053 album: "3DoorsDown", 1054 artist: "3DoorsDown", 1055 number: "04", 1056 index: "27" 1057 }, 1058 { 1059 url: "http://warp4.webarts.bc.ca/tunes/mythTunes/3DoorsDown/3DoorsDown/3DoorsDown05-Pages.ogg", 1060 file: "3DoorsDown05-Pages.ogg", 1061 title: "Pages", 1062 album: "3DoorsDown", 1063 artist: "3DoorsDown", 1064 number: "05", 1065 index: "28" 1066 } 1067 ] 1068 </pre> 1069 **/ 1070 public String toJsonString() 1071 { 1072 StringBuilder retVal = new StringBuilder("["); 1073 int songCount = 0; int artistCount = 0; int tot = artists_.size(); 1074 retVal.append(SYSTEM_LINE_SEPERATOR); 1075 1076 for(Artist currArtist : artists_) 1077 { 1078 if(currArtist.getNumberOfSongs()>0) 1079 { 1080 retVal.append(currArtist.toJsonString(songCount)); 1081 if(++artistCount<tot)retVal.append(" ,"); // // End Artist "+ currArtist.name(true)); 1082 retVal.append(SYSTEM_LINE_SEPERATOR); 1083 songCount+=currArtist.getNumberOfSongs(); 1084 } 1085 else ++artistCount; 1086 } 1087 retVal.append("]"); 1088 //retVal.append(" // TunesHelper version: $Revision: 1333 $"); 1089 return cleanJSON(retVal); 1090 } 1091 1092 /* **************************************************************************** */ 1093 1094}