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