001/*
002 *
003 *  $Revision: 1313 $
004 *  $Date: 2020-03-19 09:10:10 -0700 (Thu, 19 Mar 2020) $
005 *  $URL: svn://fred.webarts.bc.ca/open/trunk/projects/WebARTS/ca/bc/webarts/widgets/tunes/Song.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 java.io.File;
029import java.util.Vector;
030
031
032import org.apache.commons.codec.DecoderException;
033import org.apache.commons.codec.binary.Hex;
034
035/** Class to hold a Song as a java object. **/
036  public class Song implements java.lang.Comparable<Song>
037  {
038  /**  A holder for this clients System File Separator.  */
039  public final static String SYSTEM_FILE_SEPERATOR = java.io.File.separator;
040
041  /**  A holder for this clients System line termination separator.  */
042  public final static String SYSTEM_LINE_SEPERATOR =
043                                           System.getProperty("line.separator");
044    private String name_ = "";
045    private String artistName_ = "";
046    private String albumArtistName_ = "";
047    private String artistDir_ = "";
048    private File albumDirFile_ = null;
049    private String htmlLinkStart_ = "";
050    private String htmlLinkEnd_ = "";
051    /** The songs track number within its {@link Album Album}. **/
052    private int number_ = 0;
053    private int cdNumber_ = 1;
054    private File songFile_ = null;
055    private boolean lastFmLoved_ = false;
056    private static String tunesSubPath_ = TunesHelper.getTunesSubPath() ;
057    /** The optional absolute index for this Song in the users overall Library of songs. It can be used by a user
058      * to assign a unique key/index that this song has in a users entire Artist/Album library of songs. **/
059    private int libraryIndex_=-1;
060
061    /** The 2 digit HEX char/byte Reference KEY for this Song .<br>
062      * Example: 0 a <br>
063      * Each song should have a unique key, <b>WITHIN ITS OWN ALBUM</b>, based on the parsing of the tunes root
064      * directory. It is created each time the root directory is parsed.
065      * It is based on the song/track number as it inits album
066      **/
067    private char[] refKEY_ = {'0','0'};
068    private char[] albumRefKEY_ = {'0','0'};
069    private char[] artistRefKEY_ = {'0','0','0','0'};
070
071
072    /**  Minimal Constructor for Song.
073      *
074      * @param songFileDirPath the full path to the songFile.
075      * @throws Exception if the artist file dir does not exist of can't read as a dir.
076    **/
077    public Song(String songFileDirPath) throws Exception
078    {
079      this(songFileDirPath, "", "", null);
080    } //Constructor
081
082
083    /**  Constructor for Song with specied html start/end.
084      *
085      * @param songFileDirPath the full path to the songFile.
086      * @param htmlLinkStart used in the creation of the html toString method.
087      * @param htmlLinkEnd used in the creation of the html toString method.
088      * @throws Exception if the artist file dir does not exist of can't read as a dir.
089    **/
090    public Song(String songFileDirPath, String htmlLinkStart, String htmlLinkEnd) throws Exception
091    {
092      this(songFileDirPath, htmlLinkStart, htmlLinkEnd, null);
093    }
094
095
096    /**  Constructor for Song allowing fully defined metadata.
097      *
098      * @param songFileDirPath the full path to the songFile.
099      * @param htmlLinkStart used in the creation of the html toString method.
100      * @param htmlLinkEnd used in the creation of the html toString method.
101      * @param albumRefKEY see {@link #refKEY_ refKEY_}.
102      * @throws Exception if the artist file dire does not exist of can't read as a dir.
103    **/
104    public Song(String songFileDirPath, String htmlLinkStart, String htmlLinkEnd, char[] albumRefKEY) throws Exception
105    {
106      if(albumRefKEY!=null && albumRefKEY_.length==2) albumRefKEY_ = albumRefKEY;
107      File songFile = new File(songFileDirPath);
108      String songFilePath = songFile.getAbsolutePath();
109      if(songFile!=null && !songFile.isDirectory() && songFile.canRead() &&
110         (songFilePath.endsWith(".ogg") || songFilePath.endsWith(".oga") ||
111          songFilePath.endsWith(".mp3") || songFilePath.endsWith(".flac")))
112      {
113        this.songFile_ = songFile;
114        String number = "00";
115        String cdNumber = "01";
116        String sng = "";
117        try
118        {
119          sng = songFile_.getAbsolutePath().substring(songFile_.getAbsolutePath().lastIndexOf("/")+1).trim();
120
121          if(sng.indexOf("_")>0)
122            //number = right(sng.substring(sng.indexOf("_")+1, sng.indexOf("-")),2);
123            //number = sng.substring(sng.indexOf("_")+1, sng.indexOf("-"));
124            number = sng.substring(sng.indexOf("_")+1, sng.indexOf("-", sng.indexOf("_")+1));
125          else if(sng.indexOf("-")>0)
126            number = sng.substring(sng.indexOf("-")-2, sng.indexOf("-"));
127          if(number.length()>2)
128          {
129            // it includes a disc number
130            cdNumber = left(number,2);
131            number = right(number,2);
132            //System.out.print("   ++++++ "+sng.indexOf("_")+"} " +sng);
133            //System.out.println("  +++++++ Multi-CD: "+cdNumber+"["+number+"]");
134          }
135        }
136        catch(Exception ex)
137        {
138          ex.printStackTrace();
139          if(sng!=null && !"".equals(sng))
140          {
141            System.out.println("   ++++++ "+sng.indexOf("_")+"} " +sng);
142            System.out.println("   ++++++ "+sng.substring(sng.indexOf("_")+1, sng.indexOf("-", sng.indexOf("_")+1)));
143          }
144          number = "00";
145        }
146        try
147        {
148          this.number_=Integer.parseInt(number);
149          this.cdNumber_=Integer.parseInt(cdNumber);
150        }
151        catch(Exception ex)
152        {
153          this.number_ = 0;
154        }
155        if( ! ( this.number_ < (16*16)) || this.number_ <0 ) this.number_=0;
156        refKEY_ = Util.toHEXChars(this.number_, refKEY_.length);
157
158      }
159      else
160        throw new java.lang.Exception("Error with Song File Path or song type (ogg, mp3, .flac): "+songFileDirPath);
161
162      setHtmlLinkStart( htmlLinkStart);
163      setHtmlLinkEnd( htmlLinkEnd);
164    } // -- Constructor
165
166
167    /** SongName part of the filename including extension  with spaces in name . **/
168    public String name(){return name(false);}
169    /** SongName part of the filename including extension with or without thw spaces in name . **/
170    public String name(boolean removeSpaces)
171    {
172      String retVal = songFile_.getAbsolutePath().substring(songFile_.getAbsolutePath().lastIndexOf("/")+1);
173      if (!removeSpaces) retVal = Util.capsToSpacesInString(retVal);
174      return retVal.trim();
175    }
176
177    public String getSongFilePath() { return songFile_.getAbsolutePath(); }
178
179    public File getSongFile() { return songFile_; }
180
181
182    /** The actual fully expanded, cleaned of underscores and explicits / usable Song Title. **/
183    public String songTitle(){return songTitle(false);}
184    /** The actual (optionally removed spaces), cleaned of underscores and explicits / usable Song Title. **/
185    public String songTitle(boolean removeSpaces)
186    {
187      String file = name();  //.substring(name(true).lastIndexOf("-")+1);
188      String title = file.substring(file.indexOf("-")+1,file.lastIndexOf("."));
189      if(file.indexOf("_")>0)
190        try
191        {
192          title = file.substring(file.indexOf("-",
193                                              file.indexOf("_")+1
194                                             )+1,
195                                 file.lastIndexOf("."));
196        }
197        catch (java.lang.StringIndexOutOfBoundsException strEx)
198        {
199          title = file.substring(file.indexOf("-")+1,file.lastIndexOf("."));
200        }
201
202        if (!removeSpaces) title = Util.capsToSpacesInString(title);
203      title = removeExplicit(title);
204      title = Util.tokenReplace(title,"  "," ");
205      title = Util.tokenReplace(title,"_"," ");
206      title = Util.tokenReplace(title,"  "," ");
207      title = Util.tokenReplace(title,"( ","(");
208      title = Util.tokenReplace(title,"He- Man","He-Man");
209      title = Util.tokenReplace(title,"Liveat Live Aid","Live At Live Aid");
210      title = Util.tokenReplace(title,",13th July1985",", 13th July 1985");
211      title = Util.tokenReplace(title,"Livefromthe North Sea Jazz Festival","Live From The North Sea Jazz Festival");
212      title = Util.tokenReplace(title,"Inthe","In The");
213      title = Util.tokenReplace(title,"andthe"," And The");
214      title = Util.tokenReplace(title,"  "," ");
215
216      return title.trim();
217    }
218
219
220      /**
221    * Set Method for class field 'tunesSubPath_'.
222    *
223    * @param tunesSubPath is the value to set this class field to.
224    *
225    **/
226  public static void setTunesSubPath(String tunesSubPath)
227  {
228    tunesSubPath_ = tunesSubPath;
229  }  // setTunesSubPath Method
230
231
232  /**
233    * Get Method for class field 'tunesSubPath_'.
234    *
235    * @return String - The value the class field 'tunesSubPath_'.
236    *
237    **/
238  public static String getTunesSubPath()
239  {
240    return tunesSubPath_;
241  }  // getTunesSubPath Method
242
243  /**
244    * Set Method for class field 'cdNumber_'.
245    *
246    * @param cdNumber is the value to set this class field to.
247    *
248    **/
249  public  void setCdNumber(int cdNumber)
250  {
251    this.cdNumber_ = cdNumber;
252  }  // setCdNumber Method
253
254
255  /**
256    * Get Method for class field 'cdNumber_'.
257    *
258    * @return int - The value the class field 'cdNumber_'.
259    *
260    **/
261  public int getCdNumber()
262  {
263    return cdNumber_;
264  }  // getCdNumber_ Method
265
266
267
268    /**
269      * Set Method for class field 'htmlLinkStart_'.
270      *
271      * @param htmlLinkStart is the value to set this class field to.
272      *
273      **/
274    public  void setHtmlLinkStart(String htmlLinkStart)
275    {
276      this.htmlLinkStart_ = htmlLinkStart;
277    }  // setHtmlLinkStart Method
278
279
280    /**
281      * Get Method for class field 'htmlLinkStart_'.
282      *
283      * @return String - The value the class field 'htmlLinkStart_'.
284      *
285      **/
286    public String getHtmlLinkStart()
287    {
288      return htmlLinkStart_;
289    }  // getHtmlLinkStart Method
290
291
292    /**
293      * Set Method for class field 'htmlLinkEnd_'.
294      *
295      * @param htmlLinkEnd is the value to set this class field to.
296      *
297      **/
298    public  void setHtmlLinkEnd(String htmlLinkEnd)
299    {
300      this.htmlLinkEnd_ = htmlLinkEnd;
301    }  // setHtmlLinkEnd Method
302
303
304    /**
305      * Get Method for class field 'htmlLinkEnd_'.
306      *
307      * @return String - The value the class field 'htmlLinkEnd_'.
308      *
309      **/
310    public String getHtmlLinkEnd()
311    {
312      return htmlLinkEnd_;
313    }  // getHtmlLinkEnd Method
314
315
316  /**
317    * Set this song as  'lastFmLoved_'.
318    *
319    **/
320  public  void setLastFmLoved(){setLastFmLoved(true);}
321
322  /**
323    * Set Method for class field 'lastFmLoved_'.
324    *
325    * @param lastFmLoved is the value to set this class field to.
326    *
327    **/
328  public  void setLastFmLoved(boolean lastFmLoved)
329  {
330    this.lastFmLoved_ = lastFmLoved;
331  }  // setLastFmLoved Method
332
333
334  /**
335    * Get Method for class field 'lastFmLoved_'.
336    *
337    * @return boolean - The value the class field 'lastFmLoved_'.
338    *
339    **/
340  public boolean getLastFmLoved()
341  {
342    return lastFmLoved_;
343  }  // getLastFmLoved Method
344
345
346    public String albumName()
347    {
348      String albumPath = songFile_.getAbsolutePath().substring(0,songFile_.getAbsolutePath().lastIndexOf("/"));
349      //String artistPath = albumPath.substring(0, albumPath.lastIndexOf("/"));
350      String albumName = removeExplicit(albumPath.substring(albumPath.lastIndexOf("/")+1));
351      //String artistName = artistPath.getAbsolutePath().substring(artistPath.getAbsolutePath().lastIndexOf("/")+1);
352      return albumName;
353    }  // getHtmlLinkEnd Method
354
355
356    /** The actual fully expanded, cleaned of underscores and explicits / usable Album Title. **/
357    public String albumTitle()
358    {
359      return albumTitle(false);
360    }
361
362
363    /** The actual cleaned of underscores and explicits / usable Album Title. **/
364    public String albumTitle(boolean removeSpaces)
365    {
366      String title = albumName();
367      title = Song.removeExplicit(title);
368      title = Util.capsToSpacesInString(title);
369      title = Util.tokenReplace(title," <","<");
370      title = Util.tokenReplace(title,"  "," ");
371      title = Util.tokenReplace(title,"+"," +");
372      title = Util.tokenReplace(title,"++","+");
373      title = Util.tokenReplace(title,"_"," ");
374      title = Util.tokenReplace(title,"x &y","x&y");
375      title = Util.tokenReplace(title,"x & y","x&y");
376      title = Util.tokenReplace(title,"X &Y","X&Y");
377      title = Util.tokenReplace(title,"X & Y","X&Y");
378      title = Util.tokenReplace(title,"%2f","/");
379      title = Util.tokenReplace(title,"%2F","/");
380      title = Util.tokenReplace(title,"A & M","A&M");
381      title = Util.tokenReplace(title,"a & m","A&M");
382      title = Util.tokenReplace(title,"A& M","A&M");
383      title = Util.tokenReplace(title,"A &M","A&M");
384      if (removeSpaces) title=Util.spacesToCapsInString(title);
385      return title.trim();
386    }
387
388
389
390    /** the artist dirname for this song. It parses it from the full pathed filename: artist/album/songs.<br>
391      * <b>NOTE:</b> this can be a problem if the top dir is various or variousArtists.  In this case it will parse the artist from the actual filename
392      * it shouold follow my naming convention:  Artist_XX-Songname.ogg <br> where XX is the track number.
393      *
394      * @return String artist dirname for this song
395      **/
396    public String artistDir()
397    {
398      String albumPath = songFile_.getAbsolutePath().substring(0,songFile_.getAbsolutePath().lastIndexOf("/"));
399      //if(albumTitle().contains("Wembley"))System.out.println(" DEBUG: Song.artistDir  albumPath="+albumPath);
400      String artistDir = albumPath.substring(0, albumPath.lastIndexOf("/"));
401      //if(albumTitle().contains("Wembley"))System.out.println(" DEBUG: Song.artistDir  artistDir="+artistDir);
402      artistDir = artistDir.substring( artistDir.lastIndexOf("/")+1);
403      //if(albumTitle().contains("Wembley"))System.out.println(" DEBUG: Song.artistDir  artistDir="+artistDir);
404
405      return artistDir;
406    }  // artistDir Method
407
408
409    /** the artist name for this song. It parses it from the full pathed filename: artist/album/songs.<br>
410      * <b>NOTE:</b> this can be a problem if the top dir is various or variousArtists.  In this case it will parse the artist from the actual filename
411      * it shouold follow my naming convention:  Artist_XX-Songname.ogg <br> where XX is the track number.
412      *
413      * @return String artist name for this song
414      **/
415    public String artistName()
416    {
417      String albumPath = songFile_.getAbsolutePath().substring(0,songFile_.getAbsolutePath().lastIndexOf("/"));
418      String artistPath = albumPath.substring(0, albumPath.lastIndexOf("/"));
419      //String albumName = albumPath.getAbsolutePath().substring(albumPath.getAbsolutePath().lastIndexOf("/")+1);
420      String albumArtistName_ = artistPath.substring(artistPath.lastIndexOf("/")+1);
421      if(
422          albumArtistName_.equalsIgnoreCase("various")
423          || albumArtistName_.equalsIgnoreCase("variousArtists")
424          || albumArtistName_.equalsIgnoreCase("randyBachman&BurtonCummings")
425          )
426      {
427        try
428        {
429          String file = name(true);  //.substring(name(true).lastIndexOf("-")+1);
430          String art = "";;
431          if(file.indexOf("-")>0) art = file.substring(0,file.indexOf("-"));
432          if(art.indexOf("_")>0)
433            art = art.substring(0,file.indexOf("_"));
434          else
435            art = art.substring(0,art.length()-2);
436          artistName_ = art;
437        }
438        catch(Exception ex)
439        {
440          System.out.println("Json parsing choked on artistName for "+ name(true));
441        }
442      }
443      else
444        artistName_=albumArtistName_;
445      return artistName_;
446    }  // getHtmlLinkEnd Method
447
448
449  /**
450    * Set Method for class field 'number_'.
451    *
452    * @param number is the value to set this class field to.
453    *
454    **/
455  public  void setNumber(int number)
456  {
457    this.number_ = number;
458  }  // setNumber Method
459
460
461  /**
462    * Get Method for class field 'number_'.
463    *
464    * @return int - The value the class field 'number_'.
465    *
466    **/
467  public int getNumber()
468  {
469    return number_;
470  }  // getNumber Method
471
472
473  /**
474    * Set Method for class field {@link #libraryIndex_ 'libraryIndex_'}.
475    *
476    * @param libraryIndex is the value to set this class field to.
477    *
478    **/
479  public  void setLibraryIndex(int libraryIndex)
480  {
481    this.libraryIndex_ = libraryIndex;
482  }  // setLibraryIndex Method
483
484
485  /**
486    * Get Method for class field {@link #libraryIndex_ 'libraryIndex_'}.
487    *
488    * @return int - The value the class field 'libraryIndex_'.
489    *
490    **/
491  public int getLibraryIndex()
492  {
493    return libraryIndex_;
494  }  // getLibraryIndex Method
495
496
497  /**
498    * Gets rid (removes) the word EXPLICIT from the title.
499    *
500    * @param title is the string to parse
501    * @return String is the replaced resultant String.
502    *
503    **/
504  public static String removeExplicit(String title)
505  {
506      title = Util.tokenReplace(title,"(explicit)","");
507      title = Util.tokenReplace(title,"explicit","");
508      title = Util.tokenReplace(title,"(Explicit)","");
509      title = Util.tokenReplace(title,"Explicit","");
510      title = Util.tokenReplace(title,"(EXPLICIT)","");
511      title = Util.tokenReplace(title,"EXPLICIT","");
512    return title;
513  }
514
515
516  public String getRefKeyString()
517  {
518    String retVal = "";
519
520    //System.out.println("getRefKeyString() refKEY_="+java.util.Arrays.toString(refKEY_));
521    try
522    {
523      //System.out.println("getRefKeyString() Hex.decodeHex(refKEY_)="+Hex.decodeHex(refKEY_));
524      //System.out.println("getRefKeyString() Hex.encodeHexString(refKEY_)="+Hex.encodeHexString(Hex.decodeHex(refKEY_)));
525      retVal =  Hex.encodeHexString(Hex.decodeHex(refKEY_));  //only takes EVENnumber of chars
526    }
527    catch (DecoderException dEx)
528    {
529      //send back an empty String
530      System.out.println("getRefKeyString() Crapped Out: "+dEx.getMessage());
531    }
532    return retVal;
533  }
534
535
536  /**
537    * Set Method for class field 'artistRefKEY_'.
538    *
539    * @param artistRefKEY is the value to set this class field to.
540    *
541    **/
542  public  void setArtistRefKEY(char[] artistRefKEY)
543  {
544    this.artistRefKEY_ = artistRefKEY;
545  }  // setArtistRefKEY_ Method
546
547
548  /**
549    * Get Method for class field 'artistRefKEY_'.
550    *
551    * @return char[] - The value the class field 'artistRefKEY_'.
552    *
553    **/
554  public char[] getArtistRefKEY()
555  {
556    return artistRefKEY_;
557  }  // getArtistRefKEY_ Method
558
559
560  public String getAlbumRefKeyString()
561  {
562    String retVal = "";
563    try
564    {
565      retVal =  Hex.encodeHexString(Hex.decodeHex(albumRefKEY_));  //only takes EVENnumber of chars
566    }
567    catch (DecoderException dEx)
568    {
569      //send back an empty String
570      System.out.println("getAlbumRefKeyString() Crapped Out: "+dEx.getMessage());
571    }
572    return retVal;
573  }
574
575
576  public String getArtistRefKeyString()
577  {
578    String retVal = "";
579    try
580    {
581      retVal =  Hex.encodeHexString(Hex.decodeHex(artistRefKEY_));  //only takes EVENnumber of chars
582    }
583    catch (DecoderException dEx)
584    {
585      //send back an empty String
586      System.out.println("getArtistRefKeyString() Crapped Out: "+dEx.getMessage());
587    }
588    return retVal;
589  }
590
591
592    /**
593      * Returns the song metadata as a JSON String.
594      * <pre>
595      *    {
596      *       url : "https://red.webarts.bc.ca:9443/static/tunes/38Special/TheVeryBestOfTheA&amplMYears(1977-1988)/38Special_13-Teacher,Teacher.ogg",
597      *      file : "38Special_13-Teacher,Teacher.ogg",
598      *      title : "Teacher, Teacher",
599      *      number : "13"
600      *    }
601      * </pre>
602      * @param songIndex is the int to use as this songs overall library index number to use in its metadata tags.
603      * @return the json String
604    **/
605    public String toJsonString(int songIndex)
606    {
607      setHtmlLinkStart("<a class=\"Song"+songIndex+"\" id=\"Song"+songIndex+"\" name=\"Song"+songIndex+"\" href=\"#\" >");
608      setLibraryIndex(songIndex);
609      //setHtmlLinkEnd("</a>");
610
611      boolean spacedOut = false;
612      StringBuilder retVal = new StringBuilder("  {");
613      try
614      {
615        String file = name(true);  //.substring(name(true).lastIndexOf("-")+1);
616
617        String number = "00";
618        try
619        {
620          if(file.indexOf("_")>0)
621            number = right(file.substring(file.indexOf("_")+1, file.indexOf("-")),2);
622        }
623        catch(Exception ex)
624        {
625          number = "00";
626        }
627        String discNum = "00";
628        try
629        {
630          if(file.indexOf("_")>0 &&
631             file.substring(file.indexOf("_")+1, file.indexOf("-")).length()>2 )
632            discNum = left(file.substring(file.indexOf("_")+1, file.indexOf("-")),2);
633        }
634        catch(Exception ex)
635        {
636          discNum = "00";
637        }
638
639        String url = tunesSubPath_+"/"+artistDir()+"/"+albumName()+"/"+file.replace("?","%3F").replace("&","%26").replace("!","%21").replace("'","%27");
640        url = Util.tokenReplace(url,"%2f","%252f");
641        url = Util.tokenReplace(url,"%2F","%252F");
642        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
643
644        if (spacedOut) retVal.append("  ");
645        retVal.append("url: \"");
646        retVal.append(url);
647        retVal.append("\"");
648        retVal.append(",");
649        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
650
651        if (spacedOut) retVal.append("  ");
652        retVal.append(" file: \"");
653        retVal.append(file);
654        retVal.append("\"");
655        retVal.append(",");
656        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
657
658        if (spacedOut) retVal.append("  ");
659        retVal.append(" title: \"");
660        retVal.append(songTitle());
661        retVal.append("\"");
662        retVal.append(",");
663        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
664
665        if (spacedOut) retVal.append("  ");
666        retVal.append(" album: \"");
667        retVal.append(albumTitle().trim());
668        retVal.append("\"");
669        retVal.append(",");
670        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
671
672        if (spacedOut) retVal.append("  ");
673        retVal.append(" art: \"");
674        retVal.append(Util.capsToSpacesInString(artistName()).trim());
675        retVal.append("\"");
676        retVal.append(",");
677        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
678
679        if (spacedOut) retVal.append("  ");
680        retVal.append(" artDir: \"");
681        retVal.append(artistDir());
682        retVal.append("\"");
683        retVal.append(",");
684        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
685
686        if(!"00".equals(discNum))
687        {
688        if (spacedOut) retVal.append("  ");
689        retVal.append(" disc: \"");
690        retVal.append(discNum);
691        retVal.append("\"");
692        retVal.append(",");
693        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
694        }
695
696        if (spacedOut) retVal.append("  ");
697        retVal.append(" number: \"");
698        retVal.append(number);
699        retVal.append("\"");
700        retVal.append(",");
701        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
702
703        if (spacedOut) retVal.append("  ");
704        retVal.append(" refKey: \"");
705        retVal.append(getArtistRefKeyString()+"."+getAlbumRefKeyString()+"."+getRefKeyString());
706        retVal.append("\"");
707        retVal.append(",");
708        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
709
710        if (spacedOut) retVal.append("  ");
711        retVal.append(" index: \"");
712        retVal.append(songIndex);
713        retVal.append("\"");
714        //retVal.append(",");
715        if (spacedOut) retVal.append(SYSTEM_LINE_SEPERATOR);
716
717        retVal.append("  }");
718      }
719      catch(Exception ex)
720      {
721        System.out.println("Json parsing choked on "+ name(true));
722        retVal=new StringBuilder("{ file: \""+"choked on "+ name(true)+"\"}");
723      }
724      return retVal.toString();
725    }
726
727
728  /**
729    * Gives you the rightmost number of chars in a string.
730    *
731    * @param value is the string to pull result from
732    * @param numChars the number of chars to return from the right side of 'value'
733    * @return a string holding the rightmost number of chars in the passed in String
734    **/
735  public static String right(String value, int numChars)
736  {
737    String retVal = value;
738    if(value!=null && value.length()>= numChars) retVal = value.substring(value.length() - numChars);
739    return retVal;
740  }
741
742
743  /**
744    * Gives you the leftmost number of chars in a string.
745    *
746    * @param value is the string to pull result from
747    * @param numChars the number of chars to return from the left side of 'value'
748    * @return a string holding the leftmost number of chars in the passed in String
749    **/
750  public static String left(String value, int numChars)
751  {
752    String retVal = value;
753    if(value!=null && value.length()>= numChars) retVal = value.substring(0,numChars);
754    return retVal;
755  }
756
757
758  /** Compares the Song Titles. **/
759  public int compareTo(Song o)
760  {
761    return this.toString().compareTo(o.toString());
762  }
763
764
765  /** The Song Title. **/
766  public String toString()
767  {
768    return this.name().trim();
769  }
770
771
772  public String toString(boolean html)
773  {
774    String retVal = "";
775    String title = name().trim();
776    String nm = "";
777    if(title.indexOf("_")>0)
778      try
779      {
780        nm = title.substring(title.indexOf("-",title.indexOf("-",title.indexOf("_")+1)+1),title.lastIndexOf("."));
781      }
782      catch (java.lang.StringIndexOutOfBoundsException strEx)
783      {
784        nm =  title.substring(title.indexOf("-")+1).replace("_"," ").replace("  "," ").trim();
785      }
786    else
787      nm =  title.substring(title.indexOf("-")+1).replace("_"," ").replace("  "," ").trim();
788    //String ttl = songTitle().trim().substring(songTitle().indexOf("-")+1).replace("_"," ").replace("  "," ").trim();
789    String ttl = songTitle().trim().replace("_"," ").replace("  "," ").trim();
790    String url = tunesSubPath_+"/"+artistDir()+"/"+albumName()+"/";
791    url += Util.tokenReplace(name(true),"?","%3F");
792    url = Util.tokenReplace(url,"&","%26");
793    url = Util.tokenReplace(url,"!","%21");
794    url = Util.tokenReplace(url,"%2f","%252f");
795    url = Util.tokenReplace(url,"%2F","%252F");
796    if(html)retVal+= "<li><div class=\"songtip\">";
797    if(html)retVal+= getHtmlLinkStart().trim();
798    //retVal+= nm.substring(0,nm.lastIndexOf(".")).trim();
799    retVal+= ttl;
800    if(html)retVal+= getHtmlLinkEnd().trim();
801    //retVal += " "+ getLibraryIndex();
802    if(html)retVal+= "<span class=\"songtiptextTop\">"+url+"</span>";
803    if(getLastFmLoved())
804    {
805      if(html)
806        retVal+="<img id=\"jplayer_love\" class=\"love\" name=\"love\" style=\"margin-left: 1px; margin-bottom: -1px\" src=\"images/Heart-Gloss-5.svg\" height=\"12\" width=\"12\">";
807      else
808        retVal+=" Loved";
809    }
810    if(html)retVal+= "</div>";
811    if(html)retVal+= "</li>";
812    retVal+= SYSTEM_LINE_SEPERATOR;
813    return retVal;
814  }
815
816}