package com.torrenttunes.client; import static com.torrenttunes.client.db.Tables.LIBRARY; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.node.ObjectNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.frostwire.jlibtorrent.TorrentHandle; import com.musicbrainz.mp3.tagger.Tools.Song; import com.torrenttunes.client.db.Actions; import com.torrenttunes.client.db.Tables.Library; import com.torrenttunes.client.tools.DataSources; import com.torrenttunes.client.tools.Tools; public class ScanDirectory { static final Logger log = LoggerFactory.getLogger(ScanDirectory.class); private File dir; public static Set<ScanInfo> start(File dir) { ScanDirectory sd = new ScanDirectory(dir); return sd.scan(); } public ScanDirectory(File dir) { this.dir = dir; } private Set<ScanInfo> scan() { log.info("Scanning directory: " + dir.getAbsolutePath()); Set<ScanInfo> newScanInfos = new LinkedHashSet<ScanInfo>(); List<File> files = fetchUntaggedSongsFromDir(dir); log.info("New mp3 files: "); for (File file : files) { log.info(file.getAbsolutePath()); }; Set<ScanInfo> scanInfos = LibtorrentEngine.INSTANCE.getScanInfos(); // Use ScanInfo to keep track of operations and messages while you're doing them // The main scanning loop for (File file : files) { // Create a scanInfo from it, check if its a new one added ScanInfo si = ScanInfo.create(file); // Add it to the new scan infos newScanInfos.add(si); boolean isNew = scanInfos.add(si); // if (isNew) { // Fetch the song MBID si.setStatus(ScanStatus.Scanning); si.setStatus(ScanStatus.FetchingMusicBrainzId); log.info("Querying file: " + file.getAbsolutePath()); Song song = null; try { song = Song.fetchSong(si.getFile()); log.info("MusicBrainz query: " + song.getQuery()); si.setMbid(song.getRecordingMBID()); } // Couldn't find the song catch (NoSuchElementException | NullPointerException | NumberFormatException e) { log.error("Couldn't Find MusicBrainz ID for File: " + file.getAbsolutePath()); si.setStatus(ScanStatus.MusicBrainzError); continue; } // Create a torrent for the file, put it in the /.app/torrents dir si.setStatus(ScanStatus.CreatingTorrent); File torrentFile = createAndSaveTorrent(si, song); // Upload the torrent to the tracker try { si.setStatus(ScanStatus.UploadingTorrent); Tools.uploadFileToTracker(torrentFile); } catch(NoSuchElementException e) { e.printStackTrace(); si.setStatus(ScanStatus.UploadingError); continue; } // Start seeding it si.setStatus(ScanStatus.Seeding); log.info(si.getFile().getParentFile().getAbsolutePath()); TorrentHandle torrent = LibtorrentEngine.INSTANCE.addTorrent(si.getFile().getParentFile(), torrentFile, true, true); // torrent.pause(); // upload it to the server try { String songJson = Tools.MAPPER.writeValueAsString(song); // Add the mac_address ObjectNode on = Tools.MAPPER.valueToTree(Tools.jsonToNode(songJson)); on.put("uploader_ip_hash", DataSources.IP_HASH); String songUploadJson = Tools.nodeToJson(on); Tools.uploadTorrentInfoToTracker(songUploadJson); } catch(NoSuchElementException | IOException | NullPointerException e) { e.printStackTrace(); si.setStatus(ScanStatus.UploadingError); continue; } // Save it to the DB Library track = null; try { Tools.dbInit(); track = Actions.saveSongToLibrary(song.getRecordingMBID(), torrentFile.getAbsolutePath(), torrent.getInfoHash().toHex(), si.getFile().getAbsolutePath(), song.getArtist(), song.getArtistMBID(), song.getRecording(), song.getDuration()); } catch(Exception e) { e.printStackTrace(); si.setStatus(ScanStatus.DBError); continue; } finally { Tools.dbClose(); } // Set it as scanned si.setScanned(true); } log.info("Done scanning"); return newScanInfos; } public static String scanInfosReport(Set<ScanInfo> sis) { if (sis.size() == 0) { return "No .mp3 files were scanned"; } StringBuilder sb = new StringBuilder(); for (ScanInfo si : sis) { sb.append("ScanInfo: " + si.getMbid()); sb.append(" | FileName: " + si.getFile().getAbsolutePath()); sb.append(" | Status: " + si.getStatusString()); sb.append(" | Scanned: " + si.getScanned()); sb.append("\n"); } return sb.toString(); } public static List<File> fetchUntaggedSongsFromDir(File dir) { // List all the music files in the sub or sub directories String[] types = {"mp3"}; Collection<File> files = null; try { files = FileUtils.listFiles(dir, types , true); } catch(java.lang.IllegalArgumentException e) { throw new NoSuchElementException("Couldn't find directory: " + dir); } // Remove all that aren't already in the library(you don't need to upload or seed them) Set<File> dbFilePaths = loadFilePathsFromDB(); files.removeAll(dbFilePaths); // sort the files List<File> sortedFiles = new ArrayList<File>(files); Collections.sort(sortedFiles); return sortedFiles; } public static Set<File> loadFilePathsFromDB() { Tools.dbInit(); List<Library> library = LIBRARY.findAll(); library.isEmpty(); Set<File> libraryFiles = new HashSet<File>(); for (Library track : library) { libraryFiles.add(new File(track.getString("file_path"))); } Tools.dbClose(); return libraryFiles; } public static File createAndSaveTorrent(ScanInfo si, Song song) { String torrentFileName = Tools.constructTrackTorrentFilename( si.getFile(), song); File torrentFile = new File(DataSources.TORRENTS_DIR() + "/" + torrentFileName + ".torrent"); return Tools.createAndSaveTorrent(torrentFile, si.getFile()); } /** * An enum list of states and messages while scanning * @author tyler * */ public enum ScanStatus { Pending(" "), Scanning("Scanning"), FetchingMusicBrainzId("Fetching MusicBrainz ID"), MusicBrainzError("Couldn't Find MusicBrainz ID"), CreatingTorrent("Creating a torrent file"), AlreadyUploaded("Already uploaded"), UploadingTorrent("Uploading torrent file to server"), UploadingError("Couldn't upload the torrent file"), Seeding("Completed, and seeding file"), DBError("DB error, couldn't save"); private String s; ScanStatus(String s) { this.s = s; } @Override public String toString() { return s; } } public static class ScanInfo { private File file; private ScanStatus status; private String mbid; private Boolean scanned; public static ScanInfo create(File file) { return new ScanInfo(file); } private ScanInfo(File file) { this.file = file; this.status = ScanStatus.Pending; this.scanned = false; } public File getFile() { return file; } public String getFileName() { return file.getName(); } public ScanStatus getStatus() { return status; } public String getStatusString() { return status.toString(); } public void setStatus(ScanStatus status) { log.debug("Status for " + file.getName() + " : " + status.toString()); this.status = status; } public String getMbid() { return mbid; } public void setMbid(String mbid) { this.mbid = mbid; } public String toJson() { String json = null; try { json = Tools.MAPPER.writeValueAsString(this); } catch (IOException e) { e.printStackTrace(); } return json; } public void setScanned(Boolean scanned) { this.scanned = scanned; } public Boolean getScanned() { return scanned; } @Override public int hashCode() { return new HashCodeBuilder(17, 31). // two randomly chosen prime numbers // if deriving: appendSuper(super.hashCode()). append(file). append(mbid). toHashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof ScanInfo)) return false; if (obj == this) return true; ScanInfo rhs = (ScanInfo) obj; return new EqualsBuilder(). // if deriving: appendSuper(super.equals(obj)). append(file, rhs.file). append(mbid, rhs.mbid). isEquals(); } } public File getDir() { return dir; } }