package com.door43.translationstudio.core;
import com.door43.tools.reporting.Logger;
import com.door43.translationstudio.AppContext;
import com.door43.util.FileUtilities;
import com.door43.util.Zip;
import org.apache.commons.io.FileUtils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
/**
* Created by joel on 8/27/2015.
*/
public class Downloader {
private final String mRootApiUrl;
public Downloader(String apiUrl) {
mRootApiUrl = apiUrl;
}
/**
* Downloads content from a url and returns it as a string
* @param apiUrl the url from which the content will be downloaded
* @return
*/
private String request(String apiUrl) {
HttpURLConnection urlConnection = null;
try {
URL url = new URL(apiUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(5000);
urlConnection.setReadTimeout(5000);
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
String response = FileUtilities.readStreamToString(in);
urlConnection.disconnect();
return response;
} catch (IOException e) {
Logger.e(this.getClass().getName(), "Failed to download file", e);
return null;
} finally {
if(urlConnection != null) {
urlConnection.disconnect();
}
}
}
private boolean requestToFile(String apiUrl, File outputFile, long expectedSize, Library.OnProgressListener listener) {
if(apiUrl.trim().isEmpty()) {
return false;
}
URL url;
try {
url = new URL(apiUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
return false;
}
try {
URLConnection conn = url.openConnection();
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
ReadableByteChannel channel = Channels.newChannel(conn.getInputStream());
FileOutputStream os = new FileOutputStream(outputFile);
long listenerInterval = 1048 * 10; // how much must be downloaded before each listener update
long bytesRead;
long pos = 0;
long lastUpdate = 0;
do {
bytesRead = os.getChannel().transferFrom(channel, pos, expectedSize);
pos += bytesRead;
if(pos - lastUpdate >= listenerInterval) {
lastUpdate = pos;
listener.onProgress((int) pos, (int) expectedSize);
}
} while (bytesRead > 0);
listener.onProgress((int)pos, (int) expectedSize);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* Downloads the project catalog from the server
*/
public boolean downloadProjectList(Indexer targetIndex) {
String catalog = request(mRootApiUrl);
if(catalog != null) {
return targetIndex.indexProjects(catalog);
}
return false;
}
/**
* Downloads the chunk markers for a project from the server
* @param projectSlug
* @param targetIndex
* @return
*/
public boolean downloadChunkMarkerList(String projectSlug, Indexer targetIndex) {
Project project = targetIndex.getProject(projectSlug);
if(project != null && project.chunkMarkerCatalog != null
&& (project.chunkMarkerCatalogLocalDateModified < project.chunkMarkerCatalogServerDateModified
|| project.chunkMarkerCatalogServerDateModified == 0)) {
String catalog = request(project.chunkMarkerCatalog);
if(catalog != null) {
if(targetIndex.indexChunkMarkers(projectSlug, catalog)) {
targetIndex.markChunkMarkerCatalogUpToDate(projectSlug);
}
}
}
return true;
}
/**
* Downloads the source languages for a project from the server
* @param projectSlug
* @param targetIndex
* @return
*/
public boolean downloadSourceLanguageList(String projectSlug, Indexer targetIndex) {
Project project = targetIndex.getProject(projectSlug);
if(project != null && project.sourceLanguageCatalog != null
&& (project.sourceLanguageCatalogLocalDateModified < project.sourceLanguageCatalogServerDateModified
|| project.sourceLanguageCatalogServerDateModified == 0)) {
String catalog = request(project.sourceLanguageCatalog);
if(catalog != null) {
if(targetIndex.indexSourceLanguages(projectSlug, catalog)) {
targetIndex.markSourceLanguageCatalogUpToDate(projectSlug);
}
}
}
return true;
}
/**
* Downloads the resources for a source language from the server
* @param projectSlug
* @param sourceLanguageSlug
* @return
*/
public boolean downloadResourceList(String projectSlug, String sourceLanguageSlug, Indexer targetIndex) {
SourceLanguage sourceLanguage = targetIndex.getSourceLanguage(projectSlug, sourceLanguageSlug);
if(sourceLanguage != null && sourceLanguage.resourceCatalog != null
&& (sourceLanguage.resourceCatalogLocalDateModified < sourceLanguage.resourceCatalogServerDateModified
|| sourceLanguage.resourceCatalogServerDateModified == 0)) {
String catalog = request(sourceLanguage.resourceCatalog);
if(catalog != null) {
if(targetIndex.indexResources(projectSlug, sourceLanguageSlug, catalog)) {
targetIndex.markResourceCatalogUpToDate(projectSlug, sourceLanguageSlug);
}
}
}
return true;
}
/**
* Downloads the source for a source translation from the server
* @param translation
* @param targetIndex the index into which the source will be downloaded
* @return
*/
public boolean downloadSource(SourceTranslation translation, Indexer targetIndex) {
Resource resource = targetIndex.getResource(translation);
if(resource != null && resource.getSourceCatalogUrl() != null
&& (resource.getSourceDateModified() < resource.getSourceServerDateModified()
|| resource.getSourceServerDateModified() == 0)) {
String catalog = request(resource.getSourceCatalogUrl());
if(catalog != null) {
return targetIndex.indexSource(translation, catalog);
} else {
Logger.w(this.getClass().getName(), "Failed to fetch the catalog from " + resource.getSourceCatalogUrl());
return false;
}
}
return true;
}
/**
* Downloads the translationWords for a source translation from the server
* @param translation
* @param targetIndex the index to which the terms will be downloaded
* @return
*/
public boolean downloadWords(SourceTranslation translation, Indexer targetIndex) {
Resource resource = targetIndex.getResource(translation);
if(resource != null && resource.getWordsCatalogUrl() != null
&& (resource.getWordsDateModified() < resource.getWordsServerDateModified()
|| resource.getWordsServerDateModified() == 0)) {
String catalog = request(resource.getWordsCatalogUrl());
if(catalog != null) {
return targetIndex.indexTranslationWords(translation, catalog);
} else {
Logger.w(this.getClass().getName(), "Failed to fetch the catalog from " + resource.getWordsCatalogUrl());
}
}
return true;
}
/**
* Downloads the translationWord assignments for a source translation from the server
* @param translation
* @param targetIndex the index to which the term assignments will be downloaded
* @return
*/
public boolean downloadWordAssignments(SourceTranslation translation, Indexer targetIndex) {
Resource resource = targetIndex.getResource(translation);
if(resource != null && resource.getWordAssignmentsCatalogUrl() != null
&& (resource.getWordAssignmentsDateModified() < resource.getWordAssignmentsServerDateModified()
|| resource.getWordAssignmentsServerDateModified() == 0)) {
String catalog = request(resource.getWordAssignmentsCatalogUrl());
if(catalog != null) {
return targetIndex.indexTermAssignments(translation, catalog);
} else {
Logger.w(this.getClass().getName(), "Failed to fetch the catalog from " + resource.getWordAssignmentsCatalogUrl());
}
}
return true;
}
/**
* Downloads the translationNotes for a source translation from the server
* @param translation
* @param targetIndex the index to which the notes will be downloaded
* @return
*/
public boolean downloadNotes(SourceTranslation translation, Indexer targetIndex) {
Resource resource = targetIndex.getResource(translation);
if(resource != null && resource.getNotesCatalogUrl() != null
&& (resource.getNotesDateModified() < resource.getNotesServerDateModified()
|| resource.getNotesServerDateModified() == 0)) {
String catalog = request(resource.getNotesCatalogUrl());
if(catalog != null) {
return targetIndex.indexTranslationNotes(translation, catalog);
} else {
Logger.w(this.getClass().getName(), "Failed to fetch the catalog from " + resource.getNotesCatalogUrl());
}
}
return true;
}
/**
* Downloads the checkingQuestions for a source translation from the server
* @param translation
* @param targetIndex the index to which the checking questions will be downloaded
* @return
*/
public boolean downloadCheckingQuestions(SourceTranslation translation, Indexer targetIndex) {
Resource resource = targetIndex.getResource(translation);
if(resource != null && resource.getQuestionsCatalogUrl() != null
&& (resource.getQuestionsDateModified() < resource.getQuestionsServerDateModified()
|| resource.getQuestionsServerDateModified() == 0)) {
String catalog = request(resource.getQuestionsCatalogUrl());
if(catalog != null) {
return targetIndex.indexQuestions(translation, catalog);
} else {
Logger.w(this.getClass().getName(), "Failed to fetch the catalog from " + resource.getQuestionsCatalogUrl());
}
}
return true;
}
public boolean downloadImages(Library.OnProgressListener listener) {
// TODO: 1/21/2016 we need to be sure to download images for the correct project. right now only obs has images
// eventually the api will be updated so we can easily download the correct images.
String url = Resource.getImagesCatalogUrl();
String filename = url.replaceAll(".*/", "");
File imagesDir = AppContext.getLibrary().getImagesDir();
File fullPath = new File(String.format("%s/%s", imagesDir, filename));
if (!(imagesDir.isDirectory() || imagesDir.mkdirs())) {
return false;
}
boolean success = requestToFile(url, fullPath, Resource.getImagesCatalogSize(), listener);
if (success) {
try {
File tempDir = new File(imagesDir, "temp");
tempDir.mkdirs();
Zip.unzip(fullPath, tempDir);
success = true;
fullPath.delete();
// move files out of dir
File[] extractedFiles = tempDir.listFiles();
if(extractedFiles != null) {
for (File dir:extractedFiles) {
if(dir.isDirectory()) {
for(File f:dir.listFiles()) {
FileUtils.moveFile(f, new File(imagesDir, f.getName()));
}
}
}
}
FileUtils.deleteQuietly(tempDir);
} catch (IOException e) {
success = false;
}
}
return success;
}
/**
* Downloads the target languages from the server
* @param targetIndex
* @return
*/
public boolean downloadTargetLanguages(Indexer targetIndex) {
// TODO: 10/19/2015 don't hardcode the url
String catalog = request("http://td.unfoldingword.org/exports/langnames.json");
if(catalog != null) {
return targetIndex.indexTargetLanguages(catalog);
}
return false;
}
}