/* * Created by Angel Leon (@gubatron), Alden Torres (aldenml) * Copyright (c) 2011, 2012, FrostWire(TM). All rights reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.bt.download.android.gui.transfers; import java.io.ByteArrayOutputStream; import java.io.File; import java.util.Collections; import java.util.Date; import java.util.List; import android.util.Log; import com.bt.download.android.core.Constants; import com.bt.download.android.core.SystemPaths; import com.frostwire.mp3.ID3Wrapper; import com.frostwire.mp3.ID3v1Tag; import com.frostwire.mp3.ID3v23Tag; import com.frostwire.mp3.Mp3File; import com.frostwire.search.soundcloud.SoundcloudSearchResult; import com.frostwire.transfers.TransferItem; /** * @author gubatron * @author aldenml * */ public class SoundcloudDownload extends TemporaryDownloadTransfer<SoundcloudSearchResult> { private static final String TAG = "FW.SoundcloudDownload"; private static final long MAX_ACCEPTABLE_SOUNDCLOUD_FILESIZE_FOR_COVERART_FETCH = 20971520; //20MB private final TransferManager manager; public SoundcloudDownload(TransferManager manager, SoundcloudSearchResult sr) { this.manager = manager; this.sr = sr; } @Override public String getDisplayName() { return sr.getDisplayName(); } @Override public String getStatus() { return delegate != null ? delegate.getStatus() : ""; } @Override public int getProgress() { return delegate != null ? delegate.getProgress() : 0; } @Override public long getSize() { return delegate != null ? delegate.getSize() : 0; } @Override public Date getDateCreated() { return delegate != null ? delegate.getDateCreated() : new Date(); } @Override public long getBytesReceived() { return delegate != null ? delegate.getBytesReceived() : 0; } @Override public long getBytesSent() { return delegate != null ? delegate.getBytesSent() : 0; } @Override public long getDownloadSpeed() { return delegate != null ? delegate.getDownloadSpeed() : 0; } @Override public long getUploadSpeed() { return delegate != null ? delegate.getUploadSpeed() : 0; } @Override public long getETA() { return delegate != null ? delegate.getETA() : 0; } @Override public boolean isComplete() { if (delegate != null) { //FIXME: we do this differently here because SoundCloud downloads may not have //the same number of bytes as expected at the end, or maybe we don't //even know exactly how many bytes to expect in the first place. //the fix should probably be calculating this number correctly. //Suggestion: maybe look at the Content-length HTTP header for a size //if sound cloud sends this when the download starts and update the //link.getSize() value with this number as it becomes known. return delegate.getStatusCode() == HttpDownload.STATUS_COMPLETE || delegate.getStatusCode() == HttpDownload.STATUS_ERROR; } else { return false; } } @Override public List<TransferItem> getItems() { return Collections.emptyList(); } @Override public void cancel() { if (delegate != null) { delegate.cancel(); } manager.remove(this); } @Override public boolean isDownloading() { return delegate != null ? delegate.isDownloading() : false; } @Override public void cancel(boolean deleteData) { if (delegate != null) { delegate.cancel(deleteData); } manager.remove(this); } public void start() { try { final HttpDownloadLink link = buildDownloadLink(); if (link != null) { delegate = new HttpDownload(manager, SystemPaths.getTemp(), link); delegate.setListener(new HttpDownloadListener() { @Override public void onComplete(HttpDownload download) { downloadAndUpdateCoverArt(download.getSavePath()); moveFile(download.getSavePath(), Constants.FILE_TYPE_AUDIO); scanFinalFile(); } }); delegate.start(); } } catch (Exception e) { Log.e(TAG, "Error starting youtube download", e); } } private void downloadAndUpdateCoverArt(File tempFile) { if (tempFile != null && tempFile.exists() && tempFile.length() <= MAX_ACCEPTABLE_SOUNDCLOUD_FILESIZE_FOR_COVERART_FETCH) { byte[] coverArtBytes = downloadCoverArt(); if (coverArtBytes != null && coverArtBytes.length > 0) { //Log.v(TAG, "cover art array length (@" + coverArtBytes.hashCode() + "): " + coverArtBytes.length); String tempPath = tempFile.getAbsolutePath() + ".tmp"; File tempTemp = new File(tempPath); if (tempFile.renameTo(tempTemp)) { setAlbumArt(coverArtBytes, tempPath, tempFile.getAbsolutePath()); tempTemp.delete(); } } } } private byte[] downloadCoverArt() { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Log.v(TAG, "thumbnail url: " + sr.getThumbnailUrl()); HttpDownload.simpleHTTP(sr.getThumbnailUrl(), baos, 3000); return baos.toByteArray(); } catch (Throwable e) { Log.e(TAG, "Error downloading SoundCloud cover art.", e); } return null; } private HttpDownloadLink buildDownloadLink() { return new SoundcloudDownloadLink(sr); } private boolean setAlbumArt(byte[] imageBytes, String mp3Filename, String mp3outputFilename) { try { Mp3File mp3 = new Mp3File(mp3Filename); ID3Wrapper newId3Wrapper = new ID3Wrapper(new ID3v1Tag(), new ID3v23Tag()); newId3Wrapper.setAlbum(sr.getUsername() + ": " + sr.getDisplayName() + " via SoundCloud.com"); newId3Wrapper.setArtist(sr.getUsername()); newId3Wrapper.setTitle(sr.getDisplayName()); newId3Wrapper.setAlbumImage(imageBytes, "image/jpg"); newId3Wrapper.setUrl(sr.getDetailsUrl()); newId3Wrapper.getId3v2Tag().setPadding(true); mp3.setId3v1Tag(newId3Wrapper.getId3v1Tag()); mp3.setId3v2Tag(newId3Wrapper.getId3v2Tag()); mp3.save(mp3outputFilename); return true; } catch (Throwable e) { e.printStackTrace(); return false; } } }