1 /* 2 * aTunes 3 * Copyright (C) Alex Aranda, Sylvain Gaudard and contributors 4 * 5 * See http://www.atunes.org/wiki/index.php?title=Contributing for information about contributors 6 * 7 * http://www.atunes.org 8 * http://sourceforge.net/projects/atunes 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License 12 * as published by the Free Software Foundation; either version 2 13 * of the License, or (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21 package net.sourceforge.atunes.kernel.actions; 22 23 import java.util.Collection; 24 import java.util.HashMap; 25 import java.util.Map; 26 import java.util.regex.Pattern; 27 28 import javax.annotation.Nonnull; 29 30 import net.sourceforge.atunes.kernel.modules.process.SetTrackNumberProcess; 31 import net.sourceforge.atunes.model.IConfirmationDialog; 32 import net.sourceforge.atunes.model.IDialogFactory; 33 import net.sourceforge.atunes.model.ILocalAudioObject; 34 import net.sourceforge.atunes.model.IProcessFactory; 35 import net.sourceforge.atunes.model.IRepositoryHandler; 36 import net.sourceforge.atunes.model.IWebServicesHandler; 37 import net.sourceforge.atunes.utils.I18nUtils; 38 39 import com.google.common.base.Predicate; 40 import com.google.common.collect.Collections2; 41 42 /** 43 * Sets automatically track number of songs 44 * @author alex 45 * 46 */ 47 public class RepairTrackNumbersAction extends CustomAbstractAction { 48 49 private static final class FilesWithEmptyTracksFilter implements Predicate<ILocalAudioObject> { 50 @Override apply(@onnull final ILocalAudioObject ao)51 public boolean apply(@Nonnull final ILocalAudioObject ao) { 52 return ao.getTrackNumber() == 0; 53 } 54 } 55 56 57 private static final long serialVersionUID = 4117130815173907225L; 58 59 private IProcessFactory processFactory; 60 61 private IRepositoryHandler repositoryHandler; 62 63 private IWebServicesHandler webServicesHandler; 64 65 private IDialogFactory dialogFactory; 66 67 private static final Pattern NUMBER_SEPARATOR_PATTERN = Pattern.compile("[^0-9]+"); 68 69 /** 70 * @param dialogFactory 71 */ setDialogFactory(final IDialogFactory dialogFactory)72 public void setDialogFactory(final IDialogFactory dialogFactory) { 73 this.dialogFactory = dialogFactory; 74 } 75 76 /** 77 * @param webServicesHandler 78 */ setWebServicesHandler(final IWebServicesHandler webServicesHandler)79 public void setWebServicesHandler(final IWebServicesHandler webServicesHandler) { 80 this.webServicesHandler = webServicesHandler; 81 } 82 83 /** 84 * @param processFactory 85 */ setProcessFactory(final IProcessFactory processFactory)86 public void setProcessFactory(final IProcessFactory processFactory) { 87 this.processFactory = processFactory; 88 } 89 90 /** 91 * @param repositoryHandler 92 */ setRepositoryHandler(final IRepositoryHandler repositoryHandler)93 public void setRepositoryHandler(final IRepositoryHandler repositoryHandler) { 94 this.repositoryHandler = repositoryHandler; 95 } 96 97 /** 98 * Constructor 99 */ RepairTrackNumbersAction()100 public RepairTrackNumbersAction() { 101 super(I18nUtils.getString("REPAIR_TRACK_NUMBERS")); 102 } 103 104 @Override executeAction()105 protected void executeAction() { 106 // Show confirmation dialog 107 IConfirmationDialog dialog = dialogFactory.newDialog(IConfirmationDialog.class); 108 dialog.setMessage(I18nUtils.getString("REPAIR_TRACK_NUMBERS_MESSAGE")); 109 if (dialog.userAccepted()) { 110 // Call track number edit 111 /* 112 * Given an array of files, returns a map containing each file and its 113 * track number based on information found on file name. 114 */ 115 Map<ILocalAudioObject, Integer> filesToSet = new HashMap<ILocalAudioObject, Integer>(); 116 for (ILocalAudioObject ao : getFilesWithEmptyTracks(repositoryHandler.getAudioFilesList())) { 117 int trackNumber = getTrackNumber(ao); 118 119 if (trackNumber != 0) { 120 filesToSet.put(ao, trackNumber); 121 } 122 } 123 if (!filesToSet.isEmpty()) { 124 // Call process 125 SetTrackNumberProcess process = (SetTrackNumberProcess) processFactory.getProcessByName("setTrackNumberProcess"); 126 process.setFilesAndTracks(filesToSet); 127 process.execute(); 128 } 129 } 130 } 131 132 /** 133 * Returns track number for a given audio file 134 * 135 * @param audioFile 136 * @return 137 */ getTrackNumber(final ILocalAudioObject audioFile)138 private int getTrackNumber(final ILocalAudioObject audioFile) { 139 // Try to get a number from file name 140 String fileName = audioFile.getNameWithoutExtension(); 141 String[] aux = NUMBER_SEPARATOR_PATTERN.split(fileName); 142 int trackNumber = 0; 143 int i = 0; 144 while (trackNumber == 0 && i < aux.length) { 145 String token = aux[i]; 146 try { 147 trackNumber = Integer.parseInt(token); 148 // If trackNumber >= 1000 maybe it's not a track number (year?) 149 if (trackNumber >= 1000) { 150 trackNumber = 0; 151 } 152 } catch (NumberFormatException e) { 153 // Ok, it's not a valid number, skip it 154 } 155 i++; 156 } 157 158 // If trackNumber could not be retrieved from file name, try to get from last.fm 159 // To get this, titles must match 160 if (trackNumber == 0) { 161 trackNumber = webServicesHandler.getTrackNumber(audioFile); 162 } 163 164 return trackNumber; 165 } 166 167 168 /** 169 * Returns files without track number 170 * @param audioFiles 171 * @return 172 */ getFilesWithEmptyTracks(final Collection<ILocalAudioObject> audioFiles)173 private Collection<ILocalAudioObject> getFilesWithEmptyTracks(final Collection<ILocalAudioObject> audioFiles) { 174 return Collections2.filter(audioFiles, new FilesWithEmptyTracksFilter()); 175 } 176 } 177