Search in sources :

Example 26 with Media

use of com.ichi2.libanki.Media in project Anki-Android by ankidroid.

the class AnkiPackageExporterTest method missingFileInDeckExportDoesSkipsFile.

@Test
public void missingFileInDeckExportDoesSkipsFile() throws IOException, ImportExportException {
    // Arrange
    File mediaFilePath = addTempFileToMediaAndNote();
    if (!mediaFilePath.delete()) {
        throw new IllegalStateException("need to delete temp file for test to pass");
    }
    AnkiPackageExporter exporter = getExporterForDeckWithMedia();
    File temp = CreateTempDir.tempDir("/AnkiDroid-missingFileInExportDoesNotThrowException-export");
    File exportedFile = new File(temp.getAbsolutePath() + "/export.apkg");
    // Exporting
    exporter.exportInto(exportedFile.getAbsolutePath(), getTargetContext());
    // Unzipping the export.apkg file
    UnzipFile.unzip(exportedFile, temp.getAbsolutePath() + "/unzipped");
    String unzipDirectory = temp.getAbsolutePath() + "/unzipped";
    // Storing paths of unzipped files in a list
    List<String> files = Arrays.asList(new File(unzipDirectory).list());
    File[] file_names = new File[2];
    int i = 0;
    for (String x : files) {
        File f = new File(unzipDirectory + "/" + x);
        file_names[i++] = f;
    }
    // Checking the unzipped files
    assertThat(files, containsInAnyOrder("collection.anki2", "media"));
    assertThat("Only two files should exist", files, hasSize(2));
    checkMediaExportStringIs(file_names, "{}");
}
Also used : File(java.io.File) UnzipFile(com.ichi2.utils.UnzipFile) RobolectricTest(com.ichi2.anki.RobolectricTest) Test(org.junit.Test)

Example 27 with Media

use of com.ichi2.libanki.Media in project Anki-Android by ankidroid.

the class ImageFieldTest method testNoImagePathIsNothing.

@Test
public void testNoImagePathIsNothing() {
    String knownBadImage = "<br />";
    Collection col = collectionWithMediaDirectory("media");
    String imageSrc = ImageField.getImageFullPath(col, knownBadImage);
    assertThat("no media should return no paths", imageSrc, equalTo(""));
}
Also used : Collection(com.ichi2.libanki.Collection) Test(org.junit.Test)

Example 28 with Media

use of com.ichi2.libanki.Media in project Anki-Android by Ramblurr.

the class Connection method doInBackgroundUpgradeDecks.

private Payload doInBackgroundUpgradeDecks(Payload data) {
    // Enable http request canceller
    mCancelCallback = new CancelCallback();
    String path = (String) data.data[0];
    File ankiDir = new File(path);
    if (!ankiDir.isDirectory()) {
        data.success = false;
        data.data = new Object[] { "wrong anki directory" };
        return data;
    }
    // step 1: gather all .anki files into a zip, without media.
    // we must store them as 1.anki, 2.anki and provide a map so we don't run into
    // encoding issues with the zip file.
    File[] fileList = ankiDir.listFiles(new OldAnkiDeckFilter());
    List<String> corruptFiles = new ArrayList<String>();
    JSONObject map = new JSONObject();
    byte[] buf = new byte[1024];
    String zipFilename = path + "/upload.zip";
    String colFilename = path + AnkiDroidApp.COLLECTION_PATH;
    try {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFilename));
        int n = 1;
        for (File f : fileList) {
            String deckPath = f.getAbsolutePath();
            // set journal mode to delete
            try {
                AnkiDb d = AnkiDatabaseManager.getDatabase(deckPath);
            } catch (SQLiteDatabaseCorruptException e) {
                // ignore invalid .anki files
                corruptFiles.add(f.getName());
                continue;
            } finally {
                AnkiDatabaseManager.closeDatabase(deckPath);
            }
            // zip file
            String tmpName = n + ".anki";
            FileInputStream in = new FileInputStream(deckPath);
            ZipEntry ze = new ZipEntry(tmpName);
            zos.putNextEntry(ze);
            int len;
            while ((len = in.read(buf)) >= 0) {
                zos.write(buf, 0, len);
            }
            zos.closeEntry();
            map.put(tmpName, f.getName());
            n++;
        }
        // if all .anki files were found corrupted, abort
        if (fileList.length == corruptFiles.size()) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
            return data;
        }
        ZipEntry ze = new ZipEntry("map.json");
        zos.putNextEntry(ze);
        InputStream in = new ByteArrayInputStream(Utils.jsonToString(map).getBytes("UTF-8"));
        int len;
        while ((len = in.read(buf)) >= 0) {
            zos.write(buf, 0, len);
        }
        zos.closeEntry();
        zos.close();
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (JSONException e) {
        throw new RuntimeException(e);
    }
    File zipFile = new File(zipFilename);
    // step 1.1: if it's over 50MB compressed, it must be upgraded by the user
    if (zipFile.length() > 50 * 1024 * 1024) {
        data.success = false;
        data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_exceeds) };
        return data;
    }
    // step 2: upload zip file to upgrade service and get token
    BasicHttpSyncer h = new BasicHttpSyncer(null, null);
    // note: server doesn't expect it to be gzip compressed, because the zip file is compressed
    // enable cancelling
    publishProgress(R.string.upgrade_decks_upload, null, true);
    try {
        HttpResponse resp = h.req("upgrade/upload", new FileInputStream(zipFile), 0, false, null, mCancelCallback);
        if (resp == null && !isCancelled()) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
            return data;
        }
        String result;
        String key = null;
        if (!isCancelled()) {
            result = h.stream2String(resp.getEntity().getContent());
            if (result != null && result.startsWith("ok:")) {
                key = result.split(":")[1];
            } else {
                data.success = false;
                data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
                return data;
            }
        }
        while (!isCancelled()) {
            result = h.stream2String(h.req("upgrade/status?key=" + key).getEntity().getContent());
            if (result.equals("error")) {
                data.success = false;
                data.data = new Object[] { "error" };
                return data;
            } else if (result.startsWith("waiting:")) {
                publishProgress(R.string.upgrade_decks_upload, result.split(":")[1]);
            } else if (result.equals("upgrading")) {
                publishProgress(new Object[] { R.string.upgrade_decks_upgrade_started });
            } else if (result.equals("ready")) {
                break;
            } else {
                data.success = false;
                data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
                return data;
            }
            Thread.sleep(1000);
        }
        // gzip compression if the client says it can handle it
        if (!isCancelled()) {
            publishProgress(new Object[] { R.string.upgrade_decks_downloading });
            resp = h.req("upgrade/download?key=" + key, null, 6, true, null, mCancelCallback);
        // uploads/downloads have finished so disable cancelling
        }
        publishProgress(R.string.upgrade_decks_downloading, null, false);
        if (isCancelled()) {
            return null;
        }
        if (resp == null) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_failed) };
            return data;
        }
        // step 5: check the received file is valid
        InputStream cont = resp.getEntity().getContent();
        if (!h.writeToFile(cont, colFilename)) {
            data.success = false;
            data.data = new Object[] { sContext.getString(R.string.upgrade_deck_web_upgrade_sdcard, new File(colFilename).length() / 1048576 + 1) };
            (new File(colFilename)).delete();
            return data;
        }
        // check the received file is ok
        publishProgress(new Object[] { R.string.sync_check_download_file });
        publishProgress(R.string.sync_check_download_file);
        try {
            AnkiDb d = AnkiDatabaseManager.getDatabase(colFilename);
            if (!d.queryString("PRAGMA integrity_check").equalsIgnoreCase("ok")) {
                data.success = false;
                data.data = new Object[] { sContext.getResources() };
                return data;
            }
        } finally {
            AnkiDatabaseManager.closeDatabase(colFilename);
        }
        Collection col = AnkiDroidApp.openCollection(colFilename);
        ArrayList<String> decks = col.getDecks().allNames(false);
        ArrayList<String> failed = new ArrayList<String>();
        ArrayList<File> mediaDirs = new ArrayList<File>();
        for (File f : fileList) {
            String name = f.getName().replaceFirst("\\.anki$", "");
            if (!decks.contains(name)) {
                failed.add(name);
            } else {
                mediaDirs.add(new File(f.getAbsolutePath().replaceFirst("\\.anki$", ".media")));
            }
        }
        File newMediaDir = new File(col.getMedia().getDir());
        // step 6. move media files to new media directory
        publishProgress(new Object[] { R.string.upgrade_decks_media });
        ArrayList<String> failedMedia = new ArrayList<String>();
        File curMediaDir = null;
        for (File mediaDir : mediaDirs) {
            curMediaDir = mediaDir;
            // Check if media directory exists and is local
            if (!curMediaDir.exists() || !curMediaDir.isDirectory()) {
                // If not try finding it in dropbox 1.2.x
                curMediaDir = new File(AnkiDroidApp.getDropboxDir(), mediaDir.getName());
                if (!curMediaDir.exists() || !curMediaDir.isDirectory()) {
                    // No media for this deck
                    continue;
                }
            }
            // Found media dir, copy files
            for (File m : curMediaDir.listFiles()) {
                try {
                    Utils.copyFile(m, new File(newMediaDir, m.getName()));
                } catch (IOException e) {
                    failedMedia.add(curMediaDir.getName().replaceFirst("\\.media$", ".anki"));
                    break;
                }
            }
        }
        data.data = new Object[] { failed, failedMedia, newMediaDir.getAbsolutePath() };
        data.success = true;
        return data;
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } catch (IllegalStateException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        (new File(zipFilename)).delete();
    }
}
Also used : ZipEntry(java.util.zip.ZipEntry) ArrayList(java.util.ArrayList) FileNotFoundException(java.io.FileNotFoundException) SQLiteDatabaseCorruptException(android.database.sqlite.SQLiteDatabaseCorruptException) AnkiDb(com.ichi2.anki.AnkiDb) BufferedInputStream(java.io.BufferedInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) JSONException(org.json.JSONException) HttpResponse(org.apache.http.HttpResponse) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) BasicHttpSyncer(com.ichi2.libanki.sync.BasicHttpSyncer) JSONObject(org.json.JSONObject) ByteArrayInputStream(java.io.ByteArrayInputStream) ZipOutputStream(java.util.zip.ZipOutputStream) FileOutputStream(java.io.FileOutputStream) Collection(com.ichi2.libanki.Collection) JSONObject(org.json.JSONObject) File(java.io.File)

Example 29 with Media

use of com.ichi2.libanki.Media in project Anki-Android by Ramblurr.

the class Connection method doInBackgroundDownloadMissingMedia.

/**
 * Downloads any missing media files according to the mediaURL deckvar.
 *
 * @param data
 * @return The return type contains data.resultType and an array of Integer in data.data. data.data[0] is the number
 *         of total missing media, data.data[1] is the number of downloaded ones.
 */
private Payload doInBackgroundDownloadMissingMedia(Payload data) {
    // Log.i(AnkiDroidApp.TAG, "DownloadMissingMedia");
    HashMap<String, String> missingPaths = new HashMap<String, String>();
    HashMap<String, String> missingSums = new HashMap<String, String>();
    Decks deck = (Decks) data.data[0];
    // pass it to the return object so we close the deck in the deck picker
    data.result = deck;
    // deck.getDeckName();
    String syncName = "";
    data.success = false;
    data.data = new Object[] { 0, 0, 0 };
    // if (!deck.hasKey("mediaURL")) {
    // data.success = true;
    // return data;
    // }
    // deck.getVar("mediaURL");
    String urlbase = "";
    if (urlbase.equals("")) {
        data.success = true;
        return data;
    }
    // deck.mediaDir(true);
    String mdir = "";
    int totalMissing = 0;
    int missing = 0;
    int grabbed = 0;
    Cursor cursor = null;
    try {
        // deck.getDB().getDatabase().rawQuery("SELECT filename, originalPath FROM media", null);
        cursor = null;
        String path = null;
        String f = null;
        while (cursor.moveToNext()) {
            f = cursor.getString(0);
            path = mdir + "/" + f;
            File file = new File(path);
            if (!file.exists()) {
                missingPaths.put(f, path);
                missingSums.put(f, cursor.getString(1));
            // Log.i(AnkiDroidApp.TAG, "Missing file: " + f);
            }
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    totalMissing = missingPaths.size();
    data.data[0] = new Integer(totalMissing);
    if (totalMissing == 0) {
        data.success = true;
        return data;
    }
    publishProgress(Boolean.FALSE, new Integer(totalMissing), new Integer(0), syncName);
    URL url = null;
    HttpURLConnection connection = null;
    String path = null;
    String sum = null;
    int readbytes = 0;
    byte[] buf = new byte[4096];
    for (String file : missingPaths.keySet()) {
        try {
            android.net.Uri uri = android.net.Uri.parse(Uri.encode(urlbase, ":/@%") + Uri.encode(file));
            url = new URI(uri.toString()).toURL();
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            if (connection.getResponseCode() == 200) {
                path = missingPaths.get(file);
                InputStream is = connection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is, 4096);
                FileOutputStream fos = new FileOutputStream(path);
                while ((readbytes = bis.read(buf, 0, 4096)) != -1) {
                    fos.write(buf, 0, readbytes);
                // Log.i(AnkiDroidApp.TAG, "Downloaded " + readbytes + " file: " + path);
                }
                fos.close();
                // Verify with checksum
                sum = missingSums.get(file);
                if (true) {
                    // sum.equals("") || sum.equals(Utils.fileChecksum(path))) {
                    grabbed++;
                } else {
                    // Download corrupted, delete file
                    // Log.i(AnkiDroidApp.TAG, "Downloaded media file " + path + " failed checksum.");
                    File f = new File(path);
                    f.delete();
                    missing++;
                }
            } else {
                Log.e(AnkiDroidApp.TAG, "Connection error (" + connection.getResponseCode() + ") while retrieving media file " + urlbase + file);
                Log.e(AnkiDroidApp.TAG, "Connection message: " + connection.getResponseMessage());
                if (missingSums.get(file).equals("")) {
                    // Ignore and keep going
                    missing++;
                } else {
                    data.success = false;
                    data.data = new Object[] { file };
                    return data;
                }
            }
            connection.disconnect();
        } catch (URISyntaxException e) {
            Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
        } catch (MalformedURLException e) {
            Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
            Log.e(AnkiDroidApp.TAG, "MalformedURLException while download media file " + path);
            if (missingSums.get(file).equals("")) {
                // Ignore and keep going
                missing++;
            } else {
                data.success = false;
                data.data = new Object[] { file };
                return data;
            }
        } catch (IOException e) {
            Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
            Log.e(AnkiDroidApp.TAG, "IOException while download media file " + path);
            if (missingSums.get(file).equals("")) {
                // Ignore and keep going
                missing++;
            } else {
                data.success = false;
                data.data = new Object[] { file };
                return data;
            }
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        publishProgress(Boolean.TRUE, new Integer(totalMissing), new Integer(grabbed + missing), syncName);
    }
    data.data[1] = new Integer(grabbed);
    data.data[2] = new Integer(missing);
    data.success = true;
    return data;
}
Also used : MalformedURLException(java.net.MalformedURLException) HashMap(java.util.HashMap) BufferedInputStream(java.io.BufferedInputStream) ByteArrayInputStream(java.io.ByteArrayInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) Decks(com.ichi2.libanki.Decks) Uri(android.net.Uri) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) Cursor(android.database.Cursor) URI(java.net.URI) URL(java.net.URL) HttpURLConnection(java.net.HttpURLConnection) BufferedInputStream(java.io.BufferedInputStream) FileOutputStream(java.io.FileOutputStream) JSONObject(org.json.JSONObject) File(java.io.File)

Example 30 with Media

use of com.ichi2.libanki.Media in project Anki-Android by Ramblurr.

the class Media method zipAdded.

// Media syncing - bundling zip files to send to server
// Because there's no standard filename encoding for zips, and because not
// all zip clients support retrieving mtime, we store the files as ascii
// and place a json file in the zip with the necessary information.
// /////////////////////////////////////////////////////
/**
 * Add files to a zip until over SYNC_ZIP_SIZE. Return zip data.
 *
 * @return Returns a tuple with two objects. The first one is the zip file contents, the second a list with the
 *         filenames of the files inside the zip.
 */
public Pair<File, List<String>> zipAdded() {
    File f = new File(mCol.getPath().replaceFirst("collection\\.anki2$", "tmpSyncToServer.zip"));
    String sql = "select fname from log where type = " + Integer.toString(MEDIA_ADD);
    List<String> filenames = mMediaDb.queryColumn(String.class, sql, 0);
    List<String> fnames = new ArrayList<String>();
    try {
        ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
        zos.setLevel(8);
        JSONObject files = new JSONObject();
        int cnt = 0;
        long sz = 0;
        byte[] buffer = new byte[2048];
        boolean finished = true;
        for (String fname : filenames) {
            fnames.add(fname);
            File file = new File(getDir(), fname);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file), 2048);
            ZipEntry entry = new ZipEntry(Integer.toString(cnt));
            zos.putNextEntry(entry);
            int count = 0;
            while ((count = bis.read(buffer, 0, 2048)) != -1) {
                zos.write(buffer, 0, count);
            }
            zos.closeEntry();
            bis.close();
            files.put(Integer.toString(cnt), fname);
            sz += file.length();
            if (sz > SYNC_ZIP_SIZE) {
                finished = false;
                break;
            }
            cnt += 1;
        }
        if (finished) {
            zos.putNextEntry(new ZipEntry("_finished"));
            zos.closeEntry();
        }
        zos.putNextEntry(new ZipEntry("_meta"));
        zos.write(Utils.jsonToString(files).getBytes());
        zos.close();
    } catch (FileNotFoundException e) {
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (JSONException e) {
        throw new RuntimeException(e);
    }
    return new Pair<File, List<String>>(f, fnames);
}
Also used : ZipEntry(java.util.zip.ZipEntry) ArrayList(java.util.ArrayList) FileNotFoundException(java.io.FileNotFoundException) JSONException(org.json.JSONException) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) JSONObject(org.json.JSONObject) BufferedInputStream(java.io.BufferedInputStream) ZipOutputStream(java.util.zip.ZipOutputStream) FileOutputStream(java.io.FileOutputStream) ZipFile(java.util.zip.ZipFile) File(java.io.File) BufferedOutputStream(java.io.BufferedOutputStream) Pair(com.ichi2.anki.Pair)

Aggregations

File (java.io.File)43 IOException (java.io.IOException)26 Collection (com.ichi2.libanki.Collection)25 JSONObject (com.ichi2.utils.JSONObject)19 ArrayList (java.util.ArrayList)17 Test (org.junit.Test)17 ZipFile (java.util.zip.ZipFile)14 JSONException (com.ichi2.utils.JSONException)10 FileOutputStream (java.io.FileOutputStream)10 List (java.util.List)10 JSONArray (com.ichi2.utils.JSONArray)9 FileInputStream (java.io.FileInputStream)9 FileNotFoundException (java.io.FileNotFoundException)9 SharedPreferences (android.content.SharedPreferences)8 JSONObject (org.json.JSONObject)8 Resources (android.content.res.Resources)7 Uri (android.net.Uri)7 RobolectricTest (com.ichi2.anki.RobolectricTest)7 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)7 AnkiPackageImporter (com.ichi2.libanki.importer.AnkiPackageImporter)7