public void testFFMPEGCodec(String activeCodec) {
    log.info("testBpmDetectorOnSongs(): activeCodec=" + activeCodec);
    for (String disabledCodec : ALL_CODECS) RE3Properties.setProperty(disabledCodec, "false");
    RE3Properties.setProperty(activeCodec, "true");
    try {
      RE3Properties.setProperty("bpm_detector_quality", "0");

      DetectedBpm result = BpmDetector.detectBpm("data/test/audio/songs/germanic.mp3");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 105.8f) fail("incorrect bpm, is=" + result.getBpm());

      result = BpmDetector.detectBpm("data/test/audio/songs/freaktimebaby.mp3");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 103.6f) fail("incorrect bpm, is=" + result.getBpm());

      result = BpmDetector.detectBpm("data/test/audio/songs/2.flac");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 114.7f) fail("incorrect bpm, is=" + result.getBpm());

      result = BpmDetector.detectBpm("data/test/audio/songs/sh3.ape");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 99.0f) fail("incorrect bpm, is=" + result.getBpm());

      result = BpmDetector.detectBpm("data/test/audio/songs/rastic chevch.m4a");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 99.8f) fail("incorrect bpm, is=" + result.getBpm());

    } catch (Exception e) {
      log.error("testBpmDetector(): error", e);
      fail(e.getMessage());
    }
  }
 public long getMinimumTimeBetweenQueries() {
   return RE3Properties.getLong("billboard_artist_minimum_query_interval_days")
       * 1000
       * 60
       * 60
       * 24;
 }
 private String downloadImage(String url) {
   String filename = WebHelper.getUniqueFilenameFromURL(url);
   if (filename != null) {
     String videoImageDirectory =
         CommonMiningAPIWrapper.getMinedDataDirectory(dataSource) + "videoimages/";
     String fullPath = videoImageDirectory + filename;
     if (log.isTraceEnabled())
       log.trace(
           "downloadImage(): saving videoImageURL=" + url + ", to local filepath=" + fullPath);
     if (ImageHelper.saveImageURL(url, fullPath)) {
       if (RE3Properties.getBoolean("server_mode")) {
         try {
           BufferedImage img = FileSystemAccess.getFileSystem().readBufferedImage(fullPath);
         } catch (InvalidImageException e) {
           return null;
         }
       } else {
         try {
           fetched = true;
           image = FileSystemAccess.getFileSystem().readQImage(fullPath);
         } catch (InvalidImageException e) {
           return null;
         }
       }
       return fullPath;
     }
   }
   return null;
 }
  public FilterSongTabTableView(RecordTableModelManager modelManager) {
    super(modelManager);

    removeAction = new QAction(Translations.get("remove_text"), this);
    removeAction.triggered.connect(this, "removeSongs()");
    removeAction.setIcon(new QIcon(RE3Properties.getProperty("menu_remove_icon")));
  }
  public RecommendedSearchTableView(SearchModelManager modelManager) {
    super(modelManager);

    hideAction = new QAction(Translations.get("hide_text"), this);
    hideAction.triggered.connect(this, "hideRecords()");
    hideAction.setIcon(new QIcon(RE3Properties.getProperty("menu_hide_icon")));

    separator1 = new QAction("", this);
    separator1.setSeparator(true);
  }
  public void testJavaCodec(String activeCodec) {
    log.info("testBpmDetectorOnSongs(): activeCodec=" + activeCodec);
    for (String disabledCodec : ALL_CODECS) RE3Properties.setProperty(disabledCodec, "false");
    RE3Properties.setProperty(activeCodec, "true");
    try {
      RE3Properties.setProperty("bpm_detector_quality", "0");

      /*
      DetectedBpm result = BpmDetector.detectBpm("data/test/audio/songs/germanic.mp3");
      if (result == null)
      	fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 106.1f)
      	fail("incorrect bpm, is=" + result.getBpm());
      */

      DetectedBpm result = BpmDetector.detectBpm("data/test/audio/songs/freaktimebaby.mp3");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 103.6f) fail("incorrect bpm, is=" + result.getBpm());

      result = BpmDetector.detectBpm("data/test/audio/songs/2.flac");
      if (result == null) fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 114.7f) fail("incorrect bpm, is=" + result.getBpm());

      /*
      result = BpmDetector.detectBpm("data/test/audio/songs/jeroensnake_-_Echo_Guitar_Jeroen.ogg");
      if (result == null)
      	fail("bpm detector did not return a result");
      if (result.getBpm().getBpmValue() != 103.8f)
      	fail("incorrect bpm, is=" + result.getBpm());
      */

    } catch (Exception e) {
      log.error("testBpmDetector(): error", e);
      fail(e.getMessage());
    }
  }
public class TagUpdateTask extends CommonTask {

  private static Logger log = Logger.getLogger(TagUpdateTask.class);
  private static final long serialVersionUID = 0L;

  private static long TAG_UPDATE_TASK_SLEEP_INTERVAL =
      RE3Properties.getLong("tag_update_task_sleep_interval_millis");

  public TagUpdateTask() {}

  public void execute() {
    try {
      if (log.isTraceEnabled()) log.trace("execute(): updating invalidated tags...");
      for (int tagId : Database.getTagIndex().getIds()) {
        TagRecord tag = Database.getTagIndex().getTagRecord(tagId);
        if ((tag != null) && tag.needsUpdate()) tag.update();
        if (RapidEvolution3.isTerminated || isCancelled()) return;
      }
    } catch (Exception e) {
      log.error("run(): error", e);
    } finally {
      if (!RapidEvolution3.isTerminated)
        SandmanThread.putForegroundTaskToSleep(this, TAG_UPDATE_TASK_SLEEP_INTERVAL);
    }
  }

  public int getTaskPriority() {
    return Task.DEFAULT_TASK_PRIORITY;
  }

  public Object getResult() {
    return null;
  }

  public String toString() {
    return "Tag tree refresher task";
  }

  public boolean isIndefiniteTask() {
    return true;
  }
}
 public void execute() {
   if (log.isDebugEnabled())
     log.debug("execute(): fetching billboard artist profile=" + artistProfile);
   try {
     billboardProfile =
         (BillboardArtistProfile)
             MiningAPIFactory.getBillboardAPI().getArtistProfile(artistProfile);
     if ((billboardProfile != null) && billboardProfile.isValid()) {
       artistProfile.addMinedProfile(billboardProfile);
       artistProfile.save();
     } else {
       artistProfile.addMinedProfileHeader(
           new MinedProfileHeader(DATA_TYPE_ARTISTS, DATA_SOURCE_BILLBOARD),
           System.currentTimeMillis()
               - getMinimumTimeBetweenQueries()
               + RE3Properties.getInt("data_mining_error_delay_minutes") * 1000 * 60);
     }
   } catch (MiningLimitReachedException mlre) {
     log.warn("execute(): mining limit reached for billboard");
   }
 }
 private static Key getKey(int index, boolean major, String type) {
   StringBuffer key = new StringBuffer();
   if (index == 0) key.append("A");
   else if (index == 1) key.append("A#");
   else if (index == 2) key.append("B");
   else if (index == 3) key.append("C");
   else if (index == 4) key.append("C#");
   else if (index == 5) key.append("D");
   else if (index == 6) key.append("D#");
   else if (index == 7) key.append("E");
   else if (index == 8) key.append("F");
   else if (index == 9) key.append("F#");
   else if (index == 10) key.append("G");
   else if (index == 11) key.append("G#");
   else return Key.NO_KEY;
   if (!major) key.append("m");
   if (RE3Properties.getBoolean("detect_advanced_keys")) {
     key.append(" ");
     key.append(type);
   }
   return Key.getKey(key.toString().trim());
 }
  public static SubmittedSong readTags(String filename) {
    SubmittedSong song = null;
    try {
      if (log.isDebugEnabled()) log.debug("readTags(): reading tags for filename=" + filename);
      TagReader tag_reader = TagReaderFactory.getTagReader(filename);
      if (tag_reader != null) {

        // must parse identifier info first...
        String artist = null;
        String album = null;
        String track = null;
        String title = null;
        String remix = null;

        String parsedStartKeyFromTitle = null;
        String parsedEndKeyFromTitle = null;

        // artist:
        try {
          artist = StringUtil.cleanString(tag_reader.getArtist());
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading artist", e);
        }
        if (artist == null) artist = "";

        // album:
        try {
          album = StringUtil.cleanString(tag_reader.getAlbum());
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading album", e);
        }
        if (album == null) album = "";

        // track:
        try {
          track = StringUtil.cleanString(tag_reader.getTrack());
          if ((track.length() == 1) && ((track.charAt(0) >= '1') && (track.charAt(0) <= '9'))) {
            track = "0" + track;
          }
          Integer total_tracks = tag_reader.getTotalTracks();
          if ((total_tracks != null) && (total_tracks.intValue() > 0)) {
            if (!track.endsWith("/" + String.valueOf(total_tracks))) {
              if ((total_tracks.intValue() >= 1) && (total_tracks.intValue() <= 9))
                track += "/0" + String.valueOf(total_tracks);
              else track += "/" + String.valueOf(total_tracks);
            }
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading track", e);
        }
        if (track == null) track = "";

        // title:
        try {
          title = StringUtil.cleanString(tag_reader.getTitle());
          int index = title.indexOf("-");
          if (index > 0) {
            int dualIndex = title.indexOf("->");
            if (dualIndex == index) {
              index = title.indexOf("-", index + 1);
              if (index < 0) index = dualIndex;
            }
            String before = title.substring(0, index).trim();
            int before_int = -1;
            try {
              before_int = Integer.parseInt(before);
            } catch (Exception e) {
            }
            if (before_int > 0) {
              title = title.substring(index + 1).trim();
              if ((track == null) || track.equals("")) track = before;
            } else if (dualIndex > 0) {
              // possibly 2 keys
              String keyBefore = before.substring(0, dualIndex).trim();
              String keyAfter = before.substring(dualIndex + 2).trim();
              if (Key.isWellFormed(keyBefore) && Key.isWellFormed(keyAfter)) {
                title = title.substring(index + 1).trim();
                parsedStartKeyFromTitle = keyBefore;
                parsedEndKeyFromTitle = keyAfter;
              }
            } else if (Key.isWellFormed(before)) {
              title = title.substring(index + 1).trim();
              parsedStartKeyFromTitle = before;
            }
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading title", e);
        }
        if (title == null) title = "";

        // remix:
        try {
          remix = StringUtil.cleanString(tag_reader.getRemix());
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading remix", e);
        }
        if (remix == null) remix = "";

        if (remix.equals("") && !title.equals("")) {
          remix = StringUtil.parseRemix(title);
          title = StringUtil.parseTitle(title);
        }

        if (title.equals("") && RE3Properties.getBoolean("use_filename_as_title_if_necessary")) {
          String fileNameOnly = FileUtil.getFilenameMinusDirectory(filename);
          String fileExtension = FileUtil.getExtension(filename);
          title =
              StringUtil.removeUnderscores(
                  fileNameOnly.substring(0, fileNameOnly.length() - fileExtension.length() - 1));
        }

        if (RE3Properties.getBoolean("tag_reading_parse_title")) {
          try {
            String titleToParse = title;
            String parsedArtist = "";
            String parsedAlbum = "";
            String parsedTrack = "";
            String parsedTitle = "";
            String parsedRemix = "";

            if (track.equals("")) {
              int i = 0;
              while ((i < titleToParse.length()) && Character.isDigit(titleToParse.charAt(i))) ++i;
              if ((i > 0) && (i <= 2) && (titleToParse.length() >= (i + 1))) {
                parsedTrack = titleToParse.substring(0, i);
                titleToParse = titleToParse.substring(i + 1).trim();
              }
            }

            int divider_index = titleToParse.indexOf(" - ");
            if (divider_index > 0) {
              parsedArtist = titleToParse.substring(0, divider_index);
              parsedTitle = titleToParse.substring(divider_index + 3);
            } else {
              parsedTitle = titleToParse;
            }
            titleToParse = parsedTitle;
            divider_index = titleToParse.indexOf(" - ");
            if (divider_index > 0) {
              parsedAlbum = titleToParse.substring(0, divider_index).trim();
              if (parsedAlbum.startsWith("[") && parsedAlbum.endsWith("]")) {
                parsedTrack = parsedAlbum.substring(1, parsedAlbum.length() - 1);
                parsedAlbum = "";
              }

              parsedTitle = titleToParse.substring(divider_index + 3);
            }
            parsedRemix = StringUtil.parseRemix(parsedTitle);
            parsedTitle = StringUtil.parseTitle(parsedTitle);
            boolean replaced_parsedArtist = false;
            boolean replaced_parsedAlbum = false;
            boolean replaced_parsedRemixer = false;
            if (artist.equals("") && !parsedArtist.equals("")) {
              replaced_parsedArtist = true;
              artist = parsedArtist;
            }
            if (album.equals("") && !parsedAlbum.equals("")) {
              replaced_parsedAlbum = true;
              album = parsedAlbum;
            }
            if (((remix == null) || remix.equals("")) && !parsedRemix.equals("")) {
              replaced_parsedRemixer = true;
              remix = parsedRemix;
            }
            if (replaced_parsedArtist && replaced_parsedAlbum && replaced_parsedRemixer) {
              title = parsedTitle;
            } else if (replaced_parsedArtist && replaced_parsedAlbum && !replaced_parsedRemixer) {
              if (!parsedRemix.equals("")) title = parsedTitle + " (" + parsedRemix + ")";
              else if (remix.equalsIgnoreCase(parsedRemix)) title = parsedTitle;
            } else if (replaced_parsedArtist && !replaced_parsedAlbum && replaced_parsedRemixer) {
              if (!parsedAlbum.equals("")) title = parsedAlbum + " - " + parsedTitle;
              else if (album.equalsIgnoreCase(parsedAlbum)) title = parsedTitle;
            } else if (!replaced_parsedArtist && replaced_parsedAlbum && replaced_parsedRemixer) {
              if (!parsedArtist.equals("")) title = parsedArtist + " - " + parsedTitle;
              else if (artist.equalsIgnoreCase(parsedArtist)) title = parsedTitle;
            } else if (replaced_parsedArtist && !replaced_parsedAlbum && !replaced_parsedRemixer) {
              if (!parsedAlbum.equals("") && !parsedRemix.equals(""))
                title = parsedAlbum + " - " + parsedTitle + " (" + parsedRemix + ")";
              else if (parsedAlbum.equals("") && !parsedRemix.equals(""))
                title = parsedTitle + " (" + parsedRemix + ")";
              else if (!parsedAlbum.equals("") && parsedRemix.equals(""))
                title = parsedAlbum + " - " + parsedTitle;
              else title = parsedTitle;
            } else if (!replaced_parsedArtist && !replaced_parsedAlbum && replaced_parsedRemixer) {
              if (!parsedAlbum.equals("") && !parsedArtist.equals(""))
                title = parsedArtist + " - " + parsedAlbum + " - " + parsedTitle;
              else if (parsedAlbum.equals("") && !parsedArtist.equals(""))
                title = parsedArtist + " - " + parsedTitle;
              else if (!parsedAlbum.equals("") && parsedArtist.equals(""))
                title = parsedAlbum + " - " + parsedTitle;
              else title = parsedTitle;
            } else if (!replaced_parsedArtist && replaced_parsedAlbum && !replaced_parsedRemixer) {
              if (!parsedArtist.equals("") && !parsedRemix.equals(""))
                title = parsedArtist + " - " + parsedTitle + " (" + parsedRemix + ")";
              else if (parsedArtist.equals("") && !parsedRemix.equals(""))
                title = parsedTitle + " (" + parsedRemix + ")";
              else if (!parsedArtist.equals("") && parsedRemix.equals(""))
                title = parsedArtist + " - " + parsedTitle;
              else title = parsedTitle;
            } else {
              // didn't replace anything
            }
            if ((parsedTrack.length() > 0) && (track.length() == 0)) {
              track = parsedTrack;
            }
            if (parsedTitle.startsWith("[")) {
              int endIndex = parsedTitle.indexOf("] - ");
              if (endIndex > 0) {
                parsedTrack = parsedTitle.substring(1, endIndex);
                parsedTitle = parsedTitle.substring(endIndex + 4);
                track = parsedTrack;
                title = parsedTitle;
              }
            }
          } catch (Exception e) {
            log.error("readTags(): error parsing title=" + title, e);
          }
        }

        // make sure remix isn't duplicated in title, and if so remove it
        if (StringUtil.substring(remix, title)) {
          int index = title.toLowerCase().indexOf(remix.toLowerCase());
          if (index > 0) title = title.substring(0, index - 1).trim();
        }

        song = new SubmittedSong(artist, album, track, title, remix);
        song.setSongFilename(filename);

        // beat intensity:
        try {
          Integer intensity = tag_reader.getBeatIntensity();
          if (intensity != null)
            song.setBeatIntensity(
                BeatIntensity.getBeatIntensity(intensity.intValue()), DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading beat intensity", e);
        }

        // bpm accurcy:
        byte bpmAccuracy = 0;
        try {
          Integer accuracy = tag_reader.getBpmAccuracy();
          if (accuracy != null) bpmAccuracy = accuracy.byteValue();
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading bpm accuracy", e);
        }

        // bpm start:
        Float bpmStart = null;
        try {
          bpmStart = tag_reader.getBpmStart();
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading start bpm", e);
        }

        // bpm end:
        Float bpmEnd = null;
        try {
          bpmEnd = tag_reader.getBpmEnd();
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading end bpm", e);
        }
        song.setBpm(new Bpm(bpmStart), new Bpm(bpmEnd), bpmAccuracy, DATA_SOURCE_FILE_TAGS);

        Key keyStart = null;
        Key keyEnd = null;
        byte keyAccuracy = 0;

        // comments
        try {
          String comments = tag_reader.getComments();
          int index = comments.indexOf("-");
          if (index > 0) {
            int dualIndex = comments.indexOf("->");
            if (dualIndex == index) {
              index = comments.indexOf("-", index + 1);
              if (index < 0) index = dualIndex;
            }
            String before = comments.substring(0, index).trim();
            if (dualIndex > 0) {
              // possibly 2 keys
              String keyBefore = before.substring(0, dualIndex).trim();
              String keyAfter = before.substring(dualIndex + 2).trim();
              if (Key.isWellFormed(keyBefore) && Key.isWellFormed(keyAfter)) {
                comments = comments.substring(index + 1).trim();
                parsedStartKeyFromTitle = keyBefore;
                parsedEndKeyFromTitle = keyAfter;
              }
            } else if (Key.isWellFormed(before)) {
              comments = comments.substring(index + 1).trim();
              parsedStartKeyFromTitle = before;
            }
          }
          song.setComments(StringUtil.cleanString(comments), DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading comments", e);
        }

        // key accurcy:
        try {
          Integer accuracy = tag_reader.getKeyAccuracy();
          if (accuracy != null) keyAccuracy = accuracy.byteValue();
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading key accuracy", e);
        }

        // start key:
        try {
          String key = StringUtil.cleanString(tag_reader.getKeyStart());
          if (!key.equals("")) keyStart = Key.getKey(key);
          if (((keyStart == null) || !keyStart.isValid()) && (parsedStartKeyFromTitle != null))
            keyStart = Key.getKey(parsedStartKeyFromTitle);

        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading start key", e);
        }

        // end key:
        try {
          keyEnd = Key.getKey(StringUtil.cleanString(tag_reader.getKeyEnd()));
          if (!keyEnd.isValid() && (parsedEndKeyFromTitle != null))
            keyEnd = Key.getKey(parsedEndKeyFromTitle);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading end key", e);
        }
        song.setKey(keyStart, keyEnd, keyAccuracy, DATA_SOURCE_FILE_TAGS);

        // genre:
        Vector<DegreeValue> styleDegrees = new Vector<DegreeValue>();
        try {
          String genre = StringUtil.cleanString(tag_reader.getGenre());
          if (genre != null) {
            if (genre.startsWith("(") && genre.endsWith(")")) {
              try {
                int genreNumber = Integer.parseInt(genre.substring(1, genre.length() - 1));
                genre = GenreUtil.getGenre(genreNumber);
              } catch (Exception e) {
              }
            } else if (genre.startsWith("(") && (genre.indexOf(")") > 0)) {
              try {
                int genreNumber = Integer.parseInt(genre.substring(1, genre.indexOf(")")));
                genre = GenreUtil.getGenre(genreNumber);
              } catch (Exception e) {
              }
            }
            styleDegrees.add(new DegreeValue(genre, 1.0f, DATA_SOURCE_FILE_TAGS));
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading genre", e);
        }

        // rating:
        try {
          Integer rating = tag_reader.getRating();
          if (rating != null)
            song.setRating(Rating.getRating(rating.intValue() * 20), DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading rating", e);
        }

        // replay gain:
        try {
          Float replayGain = tag_reader.getReplayGain();
          if (replayGain != null) {
            song.setReplayGain(replayGain.floatValue(), DATA_SOURCE_FILE_TAGS);
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading replay gain", e);
        }

        // styles:
        try {
          Vector<DegreeValue> styles = tag_reader.getStyles();
          if ((styles != null) && (styles.size() > 0)) {
            styleDegrees = styles; // drop use of genre if styles exist
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading styles", e);
        }
        song.setStyleDegrees(styleDegrees);

        // tags:
        Vector<DegreeValue> tagDegrees = new Vector<DegreeValue>();
        try {
          Vector<DegreeValue> tags = tag_reader.getTags();
          if (tags != null) {
            tagDegrees = tags; // drop use of genre if tags exist
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading tags", e);
        }
        song.setTagDegrees(tagDegrees);

        // time/duration
        try {
          String time = StringUtil.cleanString(tag_reader.getTime());
          if (!StringUtil.isValid(time)) {
            double track_time = 0.0f;
            if (AudioUtil.isSupportedAudioFileType(filename))
              track_time = AudioUtil.getDuration(filename).getDurationInSeconds();
            else if (VideoUtil.isSupportedVideoFileType(filename))
              track_time = VideoUtil.getDuration(filename).getDurationInSeconds();
            time = new Duration(track_time * 1000.0f).getDurationAsString();
          }
          song.setDuration(new Duration(time), DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading time", e);
        }

        // time signature
        try {
          String time_sig = StringUtil.cleanString(tag_reader.getTimeSignature());
          song.setTimeSig(TimeSig.getTimeSig(time_sig), DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading time signature", e);
        }

        // custom user data
        try {
          for (UserDataType userDataType : Database.getSongIndex().getUserDataTypes()) {
            String userValue = tag_reader.getUserField(userDataType.getTitle());
            if ((userValue != null) && !userValue.equals(""))
              song.addUserData(new UserData(userDataType, userValue));
          }
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading custom user data", e);
        }

        // album cover:
        try {
          String albumcover_filename = tag_reader.getAlbumCoverFilename();
          if (log.isTraceEnabled()) log.trace("readTags() read album cover=" + albumcover_filename);
          if (albumcover_filename != null)
            song.addImage(
                new Image(albumcover_filename, albumcover_filename, DATA_SOURCE_FILE_TAGS), true);
        } catch (InvalidImageException iie) {
          if (log.isDebugEnabled())
            log.debug("readTags(): invalid image retrieved for album cover");
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading album cover", e);
        }

        // label
        try {
          String label = tag_reader.getPublisher();
          if ((label != null) && !label.equals("")) song.setLabelName(label);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading label", e);
        }

        // year
        try {
          String year = tag_reader.getYear();
          if ((year != null) && !year.equals(""))
            song.setOriginalYearReleased(Short.parseShort(year), DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading label", e);
        }

        // lyrics
        try {
          String lyrics = tag_reader.getLyrics();
          song.setLyrics(lyrics, DATA_SOURCE_FILE_TAGS);
        } catch (Exception e) {
          if (log.isTraceEnabled()) log.trace("readTags(): error reading lyrics", e);
        }
      }

    } catch (Exception e) {
      log.error("readTags(): error Exception", e);
    }
    return song;
  }
  @Override
  public void paint(QPainter painter, QStyleOptionViewItem option, QModelIndex index) {
    // if (log.isTraceEnabled())
    // log.trace("paint(): index=" + index + ", option.palette().highlight()=" +
    // option.palette().highlight());
    Object data = index.data();
    QModelIndex sourceIndex = modelManager.getProxyModel().mapToSource(index);
    SongRecord songRecord =
        (SongRecord) ((RecordTableModelManager) modelManager).getRecordForRow(sourceIndex.row());
    if (ProfileWidgetUI.instance.getCurrentProfile() instanceof SongProfile) {
      SongProfile relativeProfile = (SongProfile) ProfileWidgetUI.instance.getCurrentProfile();
      QStyle.State state = option.state();
      if (RE3Properties.getBoolean("highlight_broken_file_links")) {
        if (modelManager instanceof RecordTableModelManager) {
          SearchRecord searchRecord =
              (SearchRecord)
                  ((RecordTableModelManager) modelManager).getRecordForRow(sourceIndex.row());
          if (searchRecord instanceof SongRecord) {
            SongRecord song = (SongRecord) searchRecord;
            if (!song.isExternalItem() && !song.hasValidSongFilename()) {
              if (!state.isSet(QStyle.StateFlag.State_Selected)) {
                state.set(QStyle.StateFlag.State_Selected);
                option.setState(state);
                QPalette p = option.palette();
                p.setColor(QPalette.ColorRole.Highlight, BROKEN_FILELINKS_COLOR.getQColor());
                option.setPalette(p);
              }
            }
          }
        }
      }
      if (relativeProfile != null) {
        boolean isMixout = false;
        float mixoutRating = 0.0f;
        for (MixoutRecord mixout : relativeProfile.getMixouts()) {
          if (mixout.getToSongUniqueId() == songRecord.getUniqueId()) {
            isMixout = true;
            mixoutRating = mixout.getRatingValue().getRatingNormalized();
            break;
          }
        }
        if (isMixout && !(parent instanceof MixoutTableWidget)) {
          if (!option.state().isSet(QStyle.StateFlag.State_Selected)) {
            mixoutRating = 0.75f * mixoutRating + 0.25f;
            Color baseColor = new Color(option.palette().color(QPalette.ColorRole.Base));
            painter.fillRect(
                option.rect(), mixoutColor.getLinearGradient(baseColor, mixoutRating).getQColor());
          }
        } else {
          // target bpm/key/time sig
          Bpm targetBpm = ProfileWidgetUI.instance.getStageWidget().getCurrentBpm();
          if (!targetBpm.isValid()) targetBpm = relativeProfile.getBpmEnd();
          if (!targetBpm.isValid()) targetBpm = relativeProfile.getBpmStart();
          Key targetKey = ProfileWidgetUI.instance.getStageWidget().getCurrentKey();
          if (!targetKey.isValid()) targetKey = relativeProfile.getEndKey();
          if (!targetKey.isValid()) targetKey = relativeProfile.getStartKey();

          if (parent instanceof CompatibleSongTableWidget) {
            CompatibleSongTableWidget compatibleWidget = (CompatibleSongTableWidget) parent;
            if ((compatibleWidget.getShowType()
                    == CompatibleSongTableWidget.COMPATIBLE_TYPE_ALL_HARMONIC)
                || (compatibleWidget.getShowType()
                    == CompatibleSongTableWidget.COMPATIBLE_TYPE_BPM_ONLY)) {
              SongKeyRelation keyRelation =
                  SongKeyRelation.getSongKeyRelation(songRecord, targetBpm, targetKey);
              if (keyRelation.isCompatible()) {
                float percentInKey =
                    (0.5f - Math.abs(keyRelation.getBestKeyRelation().getDifference())) * 2.0f;
                if (log.isTraceEnabled()) log.trace("paint(): percentInKey=" + percentInKey);
                if (!option.state().isSet(QStyle.StateFlag.State_Selected)) {
                  Color baseColor = new Color(option.palette().color(QPalette.ColorRole.Base));
                  painter.fillRect(
                      option.rect(),
                      harmonicMatchColor.getLinearGradient(baseColor, percentInKey).getQColor());
                }
              }
            } else if (compatibleWidget.getShowType()
                == CompatibleSongTableWidget.COMPATIBLE_TYPE_KEYLOCK_ONLY) {
              KeyRelation keyRelation =
                  Key.getClosestKeyRelation(
                      targetBpm.getBpmValue(),
                      songRecord.getStartKey(),
                      targetBpm.getBpmValue(),
                      targetKey);
              if (keyRelation.isCompatible()) {
                float percentInKey = (0.5f - Math.abs(keyRelation.getDifference())) * 2.0f;
                if (log.isTraceEnabled()) log.trace("paint(): percentInKey=" + percentInKey);
                if (!option.state().isSet(QStyle.StateFlag.State_Selected)) {
                  Color baseColor = new Color(option.palette().color(QPalette.ColorRole.Base));
                  painter.fillRect(
                      option.rect(),
                      harmonicMatchColor.getLinearGradient(baseColor, percentInKey).getQColor());
                }
              }
            } else if (compatibleWidget.getShowType()
                == CompatibleSongTableWidget.COMPATIBLE_TYPE_NO_KEYLOCK) {
              KeyRelation keyRelation =
                  Key.getClosestKeyRelation(
                      songRecord.getStartBpm(),
                      songRecord.getStartKey(),
                      targetBpm.getBpmValue(),
                      targetKey);
              if (keyRelation.isCompatible()) {
                float percentInKey = (0.5f - Math.abs(keyRelation.getDifference())) * 2.0f;
                if (log.isTraceEnabled()) log.trace("paint(): percentInKey=" + percentInKey);
                if (!option.state().isSet(QStyle.StateFlag.State_Selected)) {
                  Color baseColor = new Color(option.palette().color(QPalette.ColorRole.Base));
                  painter.fillRect(
                      option.rect(),
                      harmonicMatchColor.getLinearGradient(baseColor, percentInKey).getQColor());
                }
              }
            }
          } else {
            if (RE3Properties.getBoolean("enable_harmonic_coloring_in_search_table")
                && CentralWidgetUI.instance.isDetailsTabOpen()) {
              SongKeyRelation keyRelation =
                  SongKeyRelation.getSongKeyRelation(songRecord, targetBpm, targetKey);
              if (keyRelation.isCompatible()) {
                float percentInKey =
                    (0.5f - Math.abs(keyRelation.getBestKeyRelation().getDifference())) * 2.0f;
                if (log.isTraceEnabled()) log.trace("paint(): percentInKey=" + percentInKey);
                if (!option.state().isSet(QStyle.StateFlag.State_Selected)) {
                  Color baseColor = new Color(option.palette().color(QPalette.ColorRole.Base));
                  painter.fillRect(
                      option.rect(),
                      harmonicMatchColor.getLinearGradient(baseColor, percentInKey).getQColor());
                }
              }
            }
          }
        }
      }
    }
    super.paint(painter, option, index);
  }
/**
 * This class allows for a lazy style loading of thumbnail images. Each search record has a flag of
 * whether the thumbnail image is loaded, set to false initially. The table models will check this
 * flag when populating the image column, and if the image is not loaded, they will return null and
 * call the "loadThumbnailImage" method which will add the image filename to a queue, and when the
 * image is finally read by the worker thread, the model/UI will be updated.
 */
public class ThumbnailImageFactory extends Thread {

  private static Logger log = Logger.getLogger(ThumbnailImageFactory.class);

  ///////////////////
  // STATIC FIELDS //
  ///////////////////

  public static final QSize THUMBNAIL_SIZE =
      new QSize(
          RE3Properties.getInt("thumbnail_image_size"),
          RE3Properties.getInt("thumbnail_image_size"));
  private static LRUCache thumbnails =
      new LRUCache(RE3Properties.getInt("thumbnail_image_cache_size")); // String to QImage objects

  ////////////////////
  // STATIC METHODS //
  ////////////////////

  /** Reads the image from the disk and stores it in the cache/map for lookup. */
  public static QImage fetchThumbnailImage(String imageFilename) {
    if (imageFilename == null) return null;
    String fileKey = FileUtil.unify(imageFilename);
    QImage result = (QImage) thumbnails.get(fileKey);
    if (result != null) return result;
    try {
      QImage img = fetchThumbnailImageNow(imageFilename);
      if (img != null) {
        thumbnails.add(fileKey, img);
        result = img;
      }
    } catch (Exception e) {
      log.error("fetchThumbnailImage(): error", e);
    }
    return result;
  }

  public static void clearCache(String filename) {
    String fileKey = FileUtil.unify(filename);
    thumbnails.remove(fileKey);
  }

  private static QImage fetchThumbnailImageNow(String imageFilename) {
    return fetchThumbnailImageNow(imageFilename, THUMBNAIL_SIZE);
  }

  public static QImage fetchThumbnailImageNow(String imageFilename, QSize size) {
    try {
      QImage img = null;
      if (!imageFilename.equals(SearchRecord.DEFAULT_THUMBNAIL_IMAGE))
        img = FileSystemAccess.getFileSystem().readQImage(imageFilename);
      else img = new QImage(imageFilename);

      /*
      QImage result = (img.width() > size.width()
                 || img.height() > size.height())
                 ? img.scaled(size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
                 : img.copy();
                 */

      QImage result = null;
      if ((img.width() > size.width() || img.height() > size.height())) {
        result =
            img.scaled(
                size,
                Qt.AspectRatioMode.KeepAspectRatio,
                Qt.TransformationMode.SmoothTransformation);
      } else {
        result = img.copy();
      }

      img.dispose();
      return result;
    } catch (InvalidImageException ie) {
      if (log.isDebugEnabled())
        log.debug("fetchThumbnailImageNow(): could not load image=" + imageFilename);
    } catch (Exception e) {
      log.error("fetchThumbnailImageNow(): error", e);
    }
    return null;
  }
}
 public int getTaskPriority() {
   return RE3Properties.getInt("default_task_priority");
 }