001/* 002 * $URL: svn://svn.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/tools/musicbrainz/MusicbrainzRelease.java $ 003 * $Author: tgutwin $ 004 * $Revision: 1288 $ 005 * $Date: 2018-07-09 21:24:57 -0700 (Mon, 09 Jul 2018) $ 006 */ 007/* 008 * 009 * Written by Tom Gutwin - WebARTS Design. 010 * Copyright (C) 2014-2016 WebARTS Design, North Vancouver Canada 011 * http://www.webarts.ca 012 * 013 * This program is free software; you can redistribute it and/or modify 014 * it under the terms of the GNU General Public License as published by 015 * the Free Software Foundation; version 3 of the License, or 016 * (at your option) any later version. 017 * 018 * This program is distributed in the hope that it will be useful, 019 * but WITHOUT ANY WARRANTY; without_ even the implied warranty of 020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 021 * GNU General Public License for more details. 022 * 023 * You should have received a copy of the GNU General Public License 024 * along with this program; if not, write to the Free Software 025 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 026 */ 027 028package ca.bc.webarts.tools.musicbrainz; 029 030import java.io.IOException; 031import java.io.File; 032import java.io.FileNotFoundException; 033import java.lang.Integer; 034import java.net.HttpURLConnection; 035import java.net.MalformedURLException; 036import java.net.URL; 037import java.net.URLEncoder; 038import java.util.Arrays; 039import java.util.Comparator; 040import java.util.Hashtable; 041import java.util.Set; 042import java.util.Vector; 043 044 045import ca.bc.webarts.tools.RestRequester; 046import ca.bc.webarts.widgets.Util; 047import ca.bc.webarts.widgets.Quick; 048 049import org.apache.commons.codec.binary.Base64; 050 051import nu.xom.Attribute; 052import nu.xom.Builder; 053import nu.xom.Document; 054import nu.xom.Element; 055import nu.xom.Elements; 056import nu.xom.Node; 057import nu.xom.ParsingException; 058import nu.xom.ValidityException; 059import nu.xom.Serializer; 060import nu.xom.XPathException; 061 062/** 063 * 064 * 065 * Musicbrainz Release Class Object<br> 066 * ~~~~~~~~~~~~~~~~ 067 * representing the returned XML from the mb api query 068 *<pre> 069 <?xml version="1.0" encoding="UTF-8"?> 070 071 </pre> 072 * 073 **/ 074public class MusicbrainzRelease implements Comparable<MusicbrainzRelease> 075{ 076 protected static final String CLASSNAME = "ca.bc.webarts.tools.musicbrainz.MusicbrainzRelease"; //ca.bc.webarts.widgets.Util.getCurrentClassName(); 077 public static final String LOG_TAG = "\n"+CLASSNAME; //+"."+ca.bc.webarts.android.Util.getCurrentClassName(); 078 079 boolean debugOut_ = MusicbrainzRestRequester.debugOut_; 080 String artistName_ = ""; 081 String name = ""; 082 String id = ""; 083 String sortName = ""; 084 String country = ""; 085 String releaseDate = ""; 086 int year = 0; 087 int releaseCount = 0; 088 int releaseCountOffset = 0; 089 String status = ""; 090 MusicbrainzArea area = null; 091 /** 092 The URL for the CoverArt for any MRID looked up release. Various Coverart API calls are 093 available to get different images: 094 see <a href="https://musicbrainz.org/doc/Cover_Art_Archive/API">https://musicbrainz.org/doc/Cover_Art_Archive/API</a> 095 **/ 096 protected String coverartImageUrl=""; 097 String metaDataDirFilename_ = "."; 098 String metaDataFilename_ = "mbData.props"; 099 boolean useCacheOnly_ = true; 100 boolean reloadFromMB_ = false; // forces a request to MB 101 boolean mbDataLoaded_ = false; 102 103 String propsString = ""; 104 String propsPrepend_ = "MusicbrainzRelease_"; 105 String mbSearchResults_ = ""; 106 nu.xom.Document mbSearchResultsDoc_ = null; 107 java.util.Properties mbCachedProps_ = null; 108 109 110 public MusicbrainzRelease(String artistName, String albName) 111 { 112 debugOut_ = MusicbrainzRestRequester.debugOut_; 113 artistName_ = artistName; 114 name = albName.trim(); 115 sortName = name; 116 117 if (!useCacheOnly_) 118 { 119 retrieveFromMusicbrainz(artistName, albName); 120 mbDataLoaded_ = true; 121 } 122 } 123 124 125 public MusicbrainzRelease(String artistName, String albName, boolean useCacheOnly) 126 { 127 debugOut_ = MusicbrainzRestRequester.debugOut_; 128 useCacheOnly_ = useCacheOnly; 129 artistName_ = artistName; 130 name = albName.trim(); 131 sortName = name; 132 133 if (!useCacheOnly_) 134 { 135 retrieveFromMusicbrainz(artistName, albName); 136 mbDataLoaded_ = true; 137 } 138 } 139 140 141 public MusicbrainzRelease(String artistName, String albName, String metaDataDirFilename) 142 { 143 debugOut_ = MusicbrainzRestRequester.debugOut_; 144 if(metaDataDirFilename==null||metaDataDirFilename.trim().length()==0) metaDataDirFilename="."; 145 metaDataDirFilename_ = metaDataDirFilename; 146 artistName_ = artistName; 147 name = albName.trim(); 148 sortName = name; 149 mbCachedProps_ = readCachedMetaData(); 150 if (reloadFromMB_ || (!useCacheOnly_ && mbCachedProps_==null)) 151 { 152 retrieveFromMusicbrainz(artistName, albName); 153 mbDataLoaded_ = true; 154 writeMetaData(metaDataDirFilename_); 155 } 156 if (mbCachedProps_!=null) mbDataLoaded_ = true; 157 } 158 159 160 public MusicbrainzRelease(String artistName, String albName, String metaDataDirFilename, boolean useCacheOnly) 161 { 162 debugOut_ = MusicbrainzRestRequester.debugOut_; 163 useCacheOnly_ = useCacheOnly; 164 if(metaDataDirFilename==null||metaDataDirFilename.trim().length()==0) metaDataDirFilename="."; 165 metaDataDirFilename_ = metaDataDirFilename; 166 artistName_ = artistName; 167 name = albName.trim(); 168 sortName = name; 169 mbCachedProps_ = readCachedMetaData(); 170 if (reloadFromMB_ || (!useCacheOnly_ && mbCachedProps_==null)) 171 { 172 retrieveFromMusicbrainz(artistName, albName); 173 mbDataLoaded_ = true; 174 writeMetaData(metaDataDirFilename_); 175 } 176 if (mbCachedProps_!=null) mbDataLoaded_ = true; 177 } 178 179 180 public MusicbrainzRelease(String artistName, String albName, String metaDataDirFilename, boolean useCacheOnly, boolean overWriteDataFile) 181 { 182 debugOut_ = MusicbrainzRestRequester.debugOut_; 183 useCacheOnly_ = useCacheOnly; 184 metaDataDirFilename_ = metaDataDirFilename; 185 artistName_ = artistName; 186 name = albName.trim(); 187 sortName = name; 188 if(debugOut_) System.out.println(" >> MusicbrainzRelease "+artistName+" "+ albName); 189 if (!overWriteDataFile) mbCachedProps_ = readCachedMetaData(); 190 if (mbCachedProps_==null && !useCacheOnly_) 191 { 192 if(debugOut_) System.out.println(" >> MusicbrainzRelease retrieveFromMusicbrainz"); 193 retrieveFromMusicbrainz(artistName, albName); 194 mbDataLoaded_ = true; 195 writeMetaData(metaDataDirFilename); 196 } 197 if (mbCachedProps_!=null) mbDataLoaded_ = true; 198 } 199 200 201 /** Requests Musicbrainz Release info via a search to the Musicbrainz rest API and fills the class data. **/ 202 public void retrieveFromMusicbrainz(String artistName, String albName) 203 { 204 MusicbrainzRestRequester mbRR = new MusicbrainzRestRequester(); 205 mbSearchResults_ = mbRR.searchRelease(artistName, name).toString(); 206 if(debugOut_) System.out.println(" MusicbrainzRelease.retrieveFromMusicbrainz : artistName="+ artistName+" albName="+albName); 207 try 208 { 209 mbSearchResultsDoc_ = mbRR.parseXMLResponse(mbSearchResults_); 210 nu.xom.Element firstRelease = mbRR.parseSearchResultsForFirstReleaseElem(mbSearchResultsDoc_); 211 id=firstRelease.getAttributeValue("id"); 212 coverartImageUrl="http://"+mbRR.DEFAULT_COVERARTARCHIVE_IP+"/" 213 +mbRR.DEFAULT_COVERARTARCHIVE_REST_URL_PATHSTR+"/" 214 +id+"/front"; 215 if(debugOut_) System.out.println(" Release ID: "+ id); 216 if(debugOut_) System.out.println(" MB Search Results:\n"); 217 if(debugOut_) System.out.println(Util.xmlToPrettyString(mbSearchResults_, 2)); 218 status=mbRR.parseSearchResultsForFirstReleaseStatus(mbSearchResultsDoc_); 219 country=mbRR.parseSearchResultsForReleaseSubElement(mbSearchResultsDoc_, 0, "country"); 220 releaseDate=mbRR.parseSearchResultsForReleaseSubElement(mbSearchResultsDoc_, 0, "date"); 221 if(releaseDate!=null) year = Integer.parseInt(releaseDate.substring(0,4)); 222 } 223 catch (Exception ex) 224 { 225 ex.printStackTrace(); 226 System.out.println("\n---------------------------------\nParsing error\n - raw results:"); 227 try {System.out.println(Util.xmlToPrettyString(mbSearchResults_, 2));} 228 catch (Exception exx) {System.out.println(mbSearchResults_);} 229 } 230 } 231 232 233 public String toString(){return name;} 234 235 236 /** 237 * Set Method for class field 'useCacheOnly_'. 238 * 239 * @param useCacheOnly_ is the value to set this class field to. 240 * 241 **/ 242 public void setUseCacheOnly(boolean useCacheOnly) 243 { 244 this.useCacheOnly_ = useCacheOnly; 245 } // setUseCacheOnly Method 246 247 248 /** 249 * Get Method for class field 'useCacheOnly_'. 250 * 251 * @return boolean - The value the class field 'useCacheOnly_'. 252 * 253 **/ 254 public boolean getUseCacheOnly() 255 { 256 return useCacheOnly_; 257 } // getUseCacheOnly Method 258 259 260 /** 261 * Set Method for class field 'sortName'. 262 * 263 * @param sortName is the value to set this class field to. 264 * 265 **/ 266 public void setSortName(String sortName) 267 { 268 this.sortName = sortName; 269 } // setSortName Method 270 271 272 /** 273 * Get Method for class field 'sortName'. 274 * 275 * @return String - The value the class field 'sortName'. 276 * 277 **/ 278 public String getSortName() 279 { 280 return sortName; 281 } // getSortName Method 282 283 284 /** 285 * Set Method for class field 'year'. 286 * 287 * @param year is the value to set this class field to. 288 * 289 **/ 290 public void setYear(int year) 291 { 292 this.year = year; 293 } // setYear Method 294 295 296 /** 297 * Get Method for class field 'year'. 298 * 299 * @return int - The value the class field 'year'. 300 * 301 **/ 302 public int getYear() 303 { 304 return year; 305 } // getYear Method 306 307 308 /** 309 * Set Method for class field 'coverartImageUrl'. 310 * 311 * @param coverartImageUrl is the value to set this class field to. 312 * 313 **/ 314 public void setCoverartImageUrl(String coverartImageUrl) 315 { 316 this.coverartImageUrl = coverartImageUrl; 317 } // setCoverartImageUrl Method 318 319 320 /** 321 * Get Method for class field 'coverartImageUrl'. 322 * 323 * @return String - The value the class field 'coverartImageUrl'. 324 * 325 **/ 326 public String getCoverartImageUrl() 327 { 328 return coverartImageUrl; 329 } // getCoverartImageUrl Method 330 331 332 public String getPropsString() 333 { 334 String p = propsPrepend_; 335 String retVal = "#MusicBrainz Realease (Album) Properties\n"; 336 retVal += "# "+Util.createCurrentDateTime()+"\n"; 337 retVal += p+"artistName="+artistName_+"\n"; 338 retVal += p+"name="+name+"\n"; 339 retVal += p+"sortName="+getSortName()+"\n"; 340 retVal += p+"id="+id+"\n"; 341 retVal += p+"country="+country+"\n"; 342 retVal += p+"date="+releaseDate+"\n"; 343 retVal += p+"year="+year+"\n"; 344 retVal += p+"status="+status+"\n"; 345 retVal += p+"coverartImageUrl="+coverartImageUrl+"\n"; 346 retVal += p+"metaDataDirFilename="+metaDataDirFilename_+"\n"; 347 348 return retVal; 349 } 350 351 352 public String getCachedProperty(String propKey) 353 { 354 String retVal = null; 355 if(propKey!=null && !"".equals(propKey) && mbCachedProps_.contains(propKey)) 356 { 357 retVal = mbCachedProps_.getProperty(propKey,""); 358 } 359 360 return retVal; 361 } 362 363 364 /** Writes a cache of properties to the metaDataDirFilename_/metaDataFilename_ file.**/ 365 public void writeMetaData(String metaDataDirFilename) 366 { 367 if(metaDataDirFilename==null||metaDataDirFilename.trim().length()==0) metaDataDirFilename="."; 368 metaDataDirFilename_ = metaDataDirFilename; 369 if(debugOut_) System.out.println("\nCreating Metadata file for: "+getSortName()); 370 if(debugOut_) System.out.println(" Filename: "+metaDataDirFilename_+"/"+metaDataFilename_); 371 if(debugOut_) System.out.println(getPropsString()); 372 if(MusicbrainzRestRequester.doWrites_) Util.writeStringToFile(getPropsString(), metaDataDirFilename_+"/"+metaDataFilename_); 373 } 374 375 376 /** Reads the cache of properties from the metaDataDirFilename_/metaDataFilename_ file. 377 * 378 * @return the properties read, or null if no file or is emoty 379 **/ 380 public java.util.Properties readCachedMetaData() 381 { 382 java.util.Properties retVal = null; 383 if(metaDataDirFilename_==null||metaDataDirFilename_.trim().length()==0) metaDataDirFilename_="."; 384 String mbFilePath = metaDataDirFilename_+"/"+metaDataFilename_; 385 //debugOut_=true; 386 if(debugOut_) System.out.println("\nReading Metadata file for release: "+getSortName()); 387 if(debugOut_) System.out.print(" Filename: "+mbFilePath+ " "); 388 String propsStr = Util.readFileToString(mbFilePath); 389 if(debugOut_) System.out.println(" |"); 390 //debugOut_=MusicbrainzRestRequester.debugOut_; 391 if(propsStr!=null && !"".equals(propsStr)) 392 { 393 if(debugOut_) System.out.println(" >"); 394 String [] propRows = propsStr.split("\n"); 395 if(propRows!=null && propRows.length>0) 396 { 397 retVal = new java.util.Properties(); 398 int eqIndex = -1; 399 String currLine = ""; 400 String k=""; 401 String v=""; 402 for (int i=0; i<propRows.length; i++) 403 { 404 currLine = propRows[i]; 405 if(currLine!=null && !"".equals(currLine) 406 && !currLine.startsWith("#") && !currLine.startsWith("//") 407 && currLine.contains("=") && currLine.startsWith(propsPrepend_) ) 408 { 409 eqIndex = currLine.indexOf("="); 410 k=currLine.substring(propsPrepend_.length(), eqIndex); 411 v=currLine.substring(eqIndex+1); 412 if(debugOut_) System.out.print(" key ="+k+"\n value="+v); 413 if(k.trim().length()>0 && v.trim().length()>0) 414 { 415 retVal.setProperty(k,v); 416 /* 417 retVal += p+"artistName="+artistName_+"\n"; 418 retVal += p+"name="+name+"\n"; 419 retVal += p+"sortName="+getSortName()+"\n"; 420 retVal += p+"id="+id+"\n"; 421 retVal += p+"country="+country+"\n"; 422 retVal += p+"date="+releaseDate+"\n"; 423 retVal += p+"year="+year+"\n"; 424 retVal += p+"status="+status+"\n"; 425 retVal += p+"coverartImageUrl="+coverartImageUrl+"\n"; 426 retVal += p+"metaDataDirFilename="+metaDataDirFilename_+"\n"; 427 */ 428 if(k.equalsIgnoreCase("artistName")) artistName_ = v; 429 else if(k.equalsIgnoreCase("name")) name = v; 430 else if(k.equalsIgnoreCase("id")) id = v; 431 else if(k.equalsIgnoreCase("country")) country = v; 432 else if(k.equalsIgnoreCase("date")) releaseDate = v; 433 else if(k.equalsIgnoreCase("year")) year = Integer.parseInt(v); 434 else if(k.equalsIgnoreCase("status")) status = v; 435 else if(k.equalsIgnoreCase("coverartImageUrl")) coverartImageUrl = v; 436 else if(k.equalsIgnoreCase("metaDataDirFilename")) metaDataDirFilename_ = v; 437 if(debugOut_) System.out.println(" Prop SET."); 438 } 439 else 440 { 441 if(debugOut_) System.out.println(" IGNORING Prop."); 442 } 443 } 444 } 445 } 446 } 447 448 return retVal; 449 } 450 451 452 /** Comparator for ignore case sort. **/ 453 public int compareToIgnoreCase(MusicbrainzRelease other) 454 { 455 return this.getSortName().compareToIgnoreCase(other.getSortName()); 456 } 457 458 459 /** implements Comparator. **/ 460 @Override public int compareTo(MusicbrainzRelease other) 461 { 462 return this.getSortName().compareTo(other.getSortName()); 463 } 464 465 466 /** A Comparator that can be used to sort Release vectors. **/ 467 public static Comparator <MusicbrainzRelease> ReleaseComparator = new Comparator<MusicbrainzRelease>() 468 { 469 @Override public int compare(MusicbrainzRelease one, MusicbrainzRelease two) 470 { 471 return one.getSortName().compareTo(two.getSortName()); 472 } 473 }; 474 475 476 /** A case in-sensitive Comparator that can be used to sort MusicbrainzRelease vectors. **/ 477 public static Comparator <MusicbrainzRelease> ReleaseComparatorIgnoreCase = new Comparator<MusicbrainzRelease>() 478 { 479 @Override public int compare(MusicbrainzRelease one, MusicbrainzRelease two) 480 { 481 //return one.name(true).compareTo(two.name(true)); 482 return one.getSortName().compareToIgnoreCase(two.getSortName()); 483 } 484 }; 485 486 487}