001/* 002 * 003 * $Revision: 1314 $ 004 * $Date: 2020-03-19 09:11:27 -0700 (Thu, 19 Mar 2020) $ 005 * $URL: svn://fred.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/widgets/tunes/Artist.java $ 006 * 007 * Written by Tom Gutwin - WebARTS Design. 008 * http://www.webarts.bc.ca 009 * Copyright (C) 2016 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.widgets.Quick; 029import ca.bc.webarts.tools.musicbrainz.*; 030 031import java.io.File; 032import java.util.Comparator; 033import java.util.Vector; 034 035import javax.json.Json; 036import javax.json.JsonArray; 037import javax.json.JsonArrayBuilder; 038import javax.json.JsonObject; 039import javax.json.JsonWriter; 040import javax.json.JsonWriterFactory; 041import javax.json.stream.JsonParser; 042 043import org.apache.commons.codec.DecoderException; 044import org.apache.commons.codec.binary.Hex; 045 046 047/** Class to hold an Artist as a java object. **/ 048 public class Artist implements Comparable<Artist> 049 { 050 /** A holder for this clients System File Separator. */ 051 public final static String SYSTEM_FILE_SEPERATOR = java.io.File.separator; 052 053 /** A holder for this clients System line termination separator. */ 054 public final static String SYSTEM_LINE_SEPERATOR = 055 System.getProperty("line.separator"); 056 057 private String name_ = ""; 058 private File artistDirFile_ = null; 059 private String artistDir_ = ""; 060 Vector <Album> albums = new Vector <Album>(); 061 private int numAlbums_ = 0; 062 private int numSongs_ = 0; 063 private MusicbrainzArtist mb_ = null; 064 /** Flag to control if this class should use Musibrainz lookups to augment the information about this Artist. **/ 065 public static boolean useMusicBrainz_ = true; 066 067 /** The 3 digit HEX char/byte Reference KEY for this Artist . 068 * Example 0da. <br> 069 * Each artist should have a unique key based on the parsing of the tunes root directory. It is created each time the roor directory is parsed. 070 * It is based on an alpha nemeric sorted list of Artist Full (non hashed) spaceRemoved DirNames. (ie. VelvetRevolver ) 071 **/ 072 private char[] refKEY_ = {'0','0','0','0'}; 073 074 /** Constructor for Artist. 075 * 076 * @throws Exception if the artist file dire does not exist of can't read as a dir. 077 **/ 078 public Artist(String artistFileDirPath) throws Exception 079 { 080 File artistDirFile = new File(artistFileDirPath); 081 if(artistDirFile!=null && artistDirFile.isDirectory() && artistDirFile.canRead()) 082 this.artistDirFile_ = artistDirFile; 083 else 084 throw new java.lang.Exception("Error with Artist dirFilePath: "+artistFileDirPath); 085 artistDir_ = artistFileDirPath; // artistFileDirPath.substring(0,artistFileDirPath.indexOf("/")); 086 if(artistDir_.trim().length()>0 && useMusicBrainz_) 087 { 088 try 089 { 090 mb_ = new MusicbrainzArtist(artistName(false), artistDir_); 091 } 092 catch(Exception ex) 093 { 094 /* ignore error dirs */ 095 System.out.println("Can't Read Musicbrainz: "+" for dir="+artistDir_); 096 } 097 098 } 099 if(artistDir_.trim().length()<1) 100 { 101 System.out.println("Can't parse artistDir from: "+artistFileDirPath); 102 throw new java.lang.Exception("Error with Artist dirFilePath: "+artistFileDirPath); 103 } 104 105 readAlbums(); 106 107 } // -- Constructor 108 109 110 /** Constructor for Artist. 111 * 112 * @throws Exception if the artist file dir does not exist of can't read as a dir. 113 **/ 114 public Artist(String artistFileDirPath, boolean useMusicbrainz) throws Exception 115 { 116 File artistDirFile = new File(artistFileDirPath); 117 useMusicBrainz_ = useMusicbrainz; 118 if(artistDirFile!=null && artistDirFile.isDirectory() && artistDirFile.canRead()) 119 this.artistDirFile_ = artistDirFile; 120 else 121 throw new java.lang.Exception("Error with Artist dirFilePath: "+artistFileDirPath); 122 artistDir_ = artistFileDirPath; // artistFileDirPath.substring(0,artistFileDirPath.indexOf("/")); 123 if(useMusicBrainz_) 124 { 125 try 126 { 127 mb_ = new MusicbrainzArtist(artistName(false), artistDir_); 128 } 129 catch(Exception ex) 130 { 131 /* ignore error dirs */ 132 System.out.println("Can't Read Musicbrainz: "+" for dir="+artistDir_); 133 } 134 } 135 136 readAlbums(); 137 138 } // -- Constructor 139 140 141 /** Constructor for Artist. 142 * 143 * @param refKey is the unique key value indicating the subDirNumber in the dir - MUST be less than 4096 because it is represented by a 3 char HEX string. 144 * @throws Exception if the artist file dir does not exist of can't read as a dir. 145 **/ 146 public Artist(String artistFileDirPath, boolean useMusicbrainz, int refKey) throws Exception 147 { 148 //System.out.println("New Artist: useMusicbrainz="+useMusicbrainz +" refKey="+refKey); 149 if( ! ( refKey < (16*16*16) && refKey >-1) ) throw new Exception("invalid refKey"); 150 File artistDirFile = new File(artistFileDirPath); 151 useMusicBrainz_ = useMusicbrainz; 152 refKEY_ = Util.toHEXChars(refKey, refKEY_.length); 153 if(artistDirFile!=null && artistDirFile.isDirectory() && artistDirFile.canRead()) 154 this.artistDirFile_ = artistDirFile; 155 else 156 throw new java.lang.Exception("Error with Artist dirFilePath: "+artistFileDirPath); 157 artistDir_ = artistFileDirPath; // artistFileDirPath.substring(0,artistFileDirPath.indexOf("/")); 158 if(useMusicBrainz_) 159 { 160 try 161 { 162 mb_ = new MusicbrainzArtist(artistName(false), artistDir_, false); 163 } 164 catch(Exception ex) 165 { 166 /* ignore error dirs */ 167 System.out.println("Can't Read Musicbrainz: "+" for dir="+artistDir_); 168 } 169 } 170 171 readAlbums(); 172 173 } // -- Constructor 174 175 176 /** Reads all the album subDirs of this Artist into the class var albums. **/ 177 public void readAlbums() 178 { 179 File[] albumDirFiles = artistDirFile_.listFiles(); 180 if(albumDirFiles!=null) 181 { 182 int albNum=0; 183 try 184 { 185 for(File currFile : albumDirFiles) if(currFile.isDirectory() && currFile.canRead()) 186 albums.add(new Album(currFile.getAbsolutePath(), 187 useMusicBrainz_, 188 albNum++, 189 refKEY_)); // refKey is the artist RefKey so the album has a reference 190 getNumberOfSongs(false); 191 } 192 catch(Exception ex) 193 { 194 /* ignore error dirs */ 195 System.out.println("\n\n\n\n *! Artist Exception readAlbums) : "); 196 ex.printStackTrace(); 197 System.out.println("\n*\n*\n*\n* "); 198 } 199 } 200 else 201 { 202 System.out.println(" *! Artist Fudge readAlbums) : NO Albums in dir (??)"+artistDirFile_.toString()); 203 } 204 } 205 206 207 /** albumNames with spaces in names . **/ 208 public String [] albumNames(){return albumNames(false);} 209 /** Each album in an array. 210 * @param removeSpaces is a boolean flag to remove any spaces from the albumName using CamelCase. **/ 211 public String [] albumNames(boolean removeSpaces) 212 { 213 String[] albumNames = new String[albums.size()]; 214 for(int i=0; i< albumNames.length; i++) albumNames[i]=albums.get(i).name(removeSpaces); 215 Quick.sort(albumNames); 216 String[] retVal = albumNames; 217 if(!removeSpaces) for(int i=0; i< albumNames.length; i++) retVal[i] = Util.capsToSpacesInString(albumNames[i]); 218 return retVal; 219 } 220 221 222 public Vector<Album> getAlbums(){ return getAlbums(true);} 223 public Vector<Album> getAlbums(boolean sortByYear) 224 { 225 //System.out.println(" Artist.getAlbums "+(sortByYear?"sorted":"NOTSorted")); 226 if(sortByYear) java.util.Collections.sort(albums); 227 return albums; 228 } 229 230 231 /** Gets you a cheeseBurger. **/ 232 public int getNumberOfAlbums() {return albums.size(); } 233 234 235 public int getNumberOfSongs() {return getNumberOfSongs(true);} 236 /** counts and returns the number of songs in ALL albums. **/ 237 public int getNumberOfSongs(boolean useCache) 238 { 239 int songCount=0; 240 if (useCache && numSongs_>0) 241 songCount=numSongs_; 242 else 243 { 244 for(Album currAlbum : this.albums) songCount+=currAlbum.getNumberOfSongs(); 245 numSongs_=songCount; 246 } 247 return songCount; 248 } 249 250 251 public void markLovedSongs(Vector<String> lovedTrackNames) 252 { 253 //System.out.println(" >> Artists.lastFmLovedTrackNames_=\n"+ lovedTrackNames+"\n"); 254 try 255 { 256 for(Album currAlbum : getAlbums()) 257 { 258 currAlbum.markLovedSongs(lovedTrackNames); 259 } 260 } 261 catch ( Exception ex) 262 { 263 System.out.println(" *! Artist Fudge markLovedSongs) : "+ex.getMessage()); 264 //ex.printStackTrace(); 265 } 266 267 } 268 269 270 /** Each album listed One Per line of the returned String (with spaces in the filename). **/ 271 public String listAlbums(){return listAlbums(false);} 272 /** Each album listed One Per line of the returned String. 273 * @param removeSpaces is a boolean flag to remove any spaces from the albumName using CamelCase. **/ 274 public String listAlbums(boolean removeSpaces) 275 { 276 StringBuilder retVal = new StringBuilder(); 277 for(String currAlbum : albumNames(removeSpaces)) retVal.append(currAlbum+SYSTEM_LINE_SEPERATOR); 278 return retVal.toString(); 279 } 280 281 282 /** ArtistName with spaces in name . **/ 283 public String name(){return name(false);} 284 public String name(boolean removeSpaces) 285 { 286 String retVal = artistDirFile_.getAbsolutePath().substring(artistDirFile_.getAbsolutePath().lastIndexOf("/")+1); 287 if (!removeSpaces) retVal = Util.capsToSpacesInString(retVal); 288 return retVal.trim(); 289 } 290 291 292 /** The actual fully expanded, cleaned of underscores and explicits / usable Artists name. **/ 293 public String artistName() 294 { 295 return artistName(false); 296 } 297 298 299 /** The actual cleaned of underscores and explicits / usable Artists name. **/ 300 public String artistName(boolean removeSpaces) 301 { 302 String title = name(); 303 title = Song.removeExplicit(title); 304 title = Util.tokenReplace(title,"R. E. M","R.E.M"); 305 title = Util.tokenReplace(title," <","<"); 306 title = Util.tokenReplace(title," "," "); 307 title = Util.tokenReplace(title,"+"," +"); 308 title = Util.tokenReplace(title,"++","+"); 309 title = Util.tokenReplace(title,"_"," "); 310 title = Util.tokenReplace(title,"%2f","/"); 311 title = Util.tokenReplace(title,"%2F","/"); 312 if (removeSpaces) title=Util.spacesToCapsInString(title); 313 return title.trim(); 314 } 315 316 317 /** 318 * Get Method for class field 'mb_.id'. 319 * 320 * @return String - The value the class field 'artistDir_'. 321 * 322 **/ 323 public String getMusicBrainzID() 324 { 325 //System.out.println(" getMusicBrainzID() "); 326 String retVal = ""; 327 if(mb_!=null ) retVal = mb_.getId(); 328 return retVal; 329 } // getMusicBrainzIDr Method 330 331 332 /** 333 * Get Method for class field 'artistDir_'. 334 * 335 * @return String - The value the class field 'artistDir_'. 336 * 337 **/ 338 public String getArtistDir() 339 { 340 String retVal = name(true); 341 if(artistDir_!=null && !artistDir_.equals("")) retVal = artistDir_; 342 return retVal; 343 } // getArtistDir Method 344 345 346 public String getRefKeyString() 347 { 348 String retVal = ""; 349 350 //System.out.println("getRefKeyString() refKEY_="+java.util.Arrays.toString(refKEY_)); 351 try 352 { 353 //System.out.println("getRefKeyString() Hex.decodeHex(refKEY_)="+Hex.decodeHex(refKEY_)); 354 //System.out.println("getRefKeyString() Hex.encodeHexString(refKEY_)="+Hex.encodeHexString(Hex.decodeHex(refKEY_))); 355 retVal = Hex.encodeHexString(Hex.decodeHex(refKEY_)); //only takes EVENnumber of chars 356 } 357 catch (DecoderException dEx) 358 { 359 //send back an empty String 360 System.out.println("getRefKeyString() Crapped Out: "+dEx.getMessage()); 361 } 362 return retVal; 363 } 364 365 366 /** Cobbles together the meta-data information about this Artist Directory in a JSON string. **/ 367 public String toMetaJson() 368 { 369 String retVal = ""; 370 371 retVal = MusicMakerTunesHelper.prettyPrint(toMetaJsonObject()); 372 373 return retVal; 374 } 375 376 377 /** Cobbles together the meta-data information about this Artist Directory in a JSON Object. **/ 378 public JsonObject toMetaJsonObject() 379 { 380 381 int albNum = 0; 382 JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); 383 String albRefKey = ""; 384 for(Album currAlbum : getAlbums()) 385 { 386 albRefKey = currAlbum.getRefKeyString(); 387 albNum++; 388 JsonObject jsonObject = Json.createObjectBuilder() 389 .add("refKey",albRefKey ) 390 .add("dirFile", currAlbum.albumTitle(true) ) 391 .add("dirType", DirFileRef.DirType.ALBUM.typeStr ) 392 .add("dirState", DirFileRef.DirState.LONG.stateStr) // this needs to be based on parsing 393 .add("albumName", currAlbum.albumTitle(false) ) 394 .add("shortAlbumName", currAlbum.albumTitle(true) ) 395 .add("artistRefKey", currAlbum.getArtistRefKeyString()) 396 .add("musicBrainzID", currAlbum.getMbid()) 397 .add("numSongs", currAlbum.getNumberOfSongs() ) 398 .build(); 399 arrayBuilder.add( jsonObject); 400 } 401 JsonArray albumArray = arrayBuilder.build(); 402 403 JsonObject json = Json.createObjectBuilder() 404 .add("dirFile", getArtistDir() ) 405 .add("dirType", DirFileRef.DirType.ARTIST.typeStr ) 406 .add("dirState", DirFileRef.DirState.LONG.stateStr) // this needs to be based on parsing 407 .add("refKey", getRefKeyString() ) 408 .add("artistName", artistName(false) ) 409 .add("shortArtistName", artistName(true) ) 410 .add("musicBrainzID", mb_.getId() ) 411 .add("numAlbums", getNumberOfAlbums() ) 412 .add("albums", albumArray ) 413 .build(); 414 415 return json; 416 } 417 418 419 /** lists all songs in all albums in a JSON string. **/ 420 public String toJsonString(int songIndex) 421 { 422 StringBuilder retVal = new StringBuilder(""); 423 int songCount = 0; int albumCount = 0; int tot = albums.size(); 424 for(Album currAlbum : this.albums) 425 { 426 retVal.append(currAlbum.toJsonString(songIndex+songCount)); 427 if(albumCount<tot-1 && currAlbum.getNumberOfSongs()>0) 428 retVal.append(" ,"+SYSTEM_LINE_SEPERATOR); 429 //else 430 // retVal.append(" // "+currAlbum.albumTitle() +SYSTEM_LINE_SEPERATOR); 431 songCount+=currAlbum.getNumberOfSongs(); 432 albumCount++; 433 } 434 return retVal.toString(); 435 } 436 437 438 public void writeMetaJsonFile() 439 { 440 String metaStr = toMetaJson(); 441 System.out.println("\nARTIST MetaJson\n"+getArtistDir()+SYSTEM_FILE_SEPERATOR+"MetaData.json"); 442 Util.writeStringToFile(metaStr, getArtistDir()+SYSTEM_FILE_SEPERATOR+"MetaData.json"); 443 } 444 445 446 /** Comparator for ignore case sort. **/ 447 public int compareToIgnoreCase(Artist other) 448 { 449 return this.name().compareToIgnoreCase(other.name()); 450 } 451 452 453 /** implements Comparator. **/ 454 @Override public int compareTo(Artist other) 455 { 456 return this.name().compareTo(other.name()); 457 } 458 459 460 /** A Comparator that can be used to sort Artist vectors. **/ 461 public static Comparator <Artist> ArtistComparator = new Comparator<Artist>() 462 { 463 @Override public int compare(Artist one, Artist two) 464 { 465 return one.name().compareTo(two.name()); 466 } 467 }; 468 469 470 /** A case in-sensitive Comparator that can be used to sort Artist vectors. **/ 471 public static Comparator <Artist> ArtistComparatorIgnoreCase = new Comparator<Artist>() 472 { 473 @Override public int compare(Artist one, Artist two) 474 { 475 //return one.name(true).compareTo(two.name(true)); 476 return one.name().compareToIgnoreCase(two.name()); 477 } 478 }; 479 }