tw.idv.palatis.danboorugallery.siteapi.DanbooruAPI.java Source code

Java tutorial

Introduction

Here is the source code for tw.idv.palatis.danboorugallery.siteapi.DanbooruAPI.java

Source

////////////////////////////////////////////////////////////////////////////////
// Danbooru Gallery Android - an danbooru-style imageboard browser
//     Copyright (C) 2014  Victor Tseng
//
//     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 tw.idv.palatis.danboorugallery.siteapi;

import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import tw.idv.palatis.danboorugallery.DanbooruGallerySettings;
import tw.idv.palatis.danboorugallery.R;
import tw.idv.palatis.danboorugallery.database.PostTagsView;
import tw.idv.palatis.danboorugallery.database.PostsTable;
import tw.idv.palatis.danboorugallery.model.Host;
import tw.idv.palatis.danboorugallery.model.Post;
import tw.idv.palatis.danboorugallery.model.Tag;

public class DanbooruAPI extends SiteAPI {
    private static final String TAG = "DanbooruAPI";

    // this is used to format the created attribute in XML
    // it is here because Android frees Locale.ENGLISH when formatter destroyed,
    // resulting reloading of locale data every time which is SLOW.
    private static final DateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ",
            Locale.ENGLISH);

    public static void init() {
        SiteAPI.registerSiteAPI(new DanbooruAPI());
    }

    private DanbooruAPI() {
    }

    public static final int API_ID = 0xdab00002;
    public static final String API_NAME = "Danbooru (JSON)";

    @Override
    public int getApiId() {
        return API_ID;
    }

    @Override
    public String getName() {
        return API_NAME;
    }

    @Override
    public String hashSecret(String login, String password) {
        return Base64.encodeToString((login + ":" + password).getBytes(), Base64.DEFAULT);
    }

    // 1: url, 2: page #, 3: tags, 4: limit
    private static final String URL_POSTS_FORMAT = "%1$s/posts.json?page=%2$d&tags=%3$s&limit=%4$d";
    // 1: url, 2: tag
    private static final String URL_TAGS_FORMAT = "%1$s/tags.json?search[name_matches]=%2$s&search[order]=count&search[hide_empty]=yes";
    // 1: url, 2: post_id
    private static final String URL_POST_WEB = "%1$s/posts/%2$s";

    private static final int _BUFFER_SIZE = 8192;

    @Override
    public List<Tag> searchTags(Host host, String pattern) throws SiteAPIException {
        if (pattern.length() == 0)
            pattern = "*";
        else if (!pattern.contains("*"))
            pattern = "*" + pattern + "*";

        HttpURLConnection connection = null;
        try {
            String url = String.format(URL_TAGS_FORMAT, host.url, URLEncoder.encode(pattern, "UTF-8"));
            Log.v(TAG, String.format("URL: %s", url));
            connection = SiteAPI.openConnection(new URL(url));
            if (!host.getLogin().isEmpty())
                connection.setRequestProperty("Authorization", "Basic " + host.getSecret());
            Reader input = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            Writer output = new StringWriter();

            char buffer[] = new char[_BUFFER_SIZE];
            for (int count = input.read(buffer); count > 0; count = input.read(buffer))
                output.write(buffer, 0, count);

            JSONArray json_tags = new JSONArray(output.toString());
            int len = json_tags.length();
            List<Tag> tags = new ArrayList<>(len);
            for (int i = 0; i < len; ++i)
                tags.add(parseJSONObjectToTag(json_tags.getJSONObject(i)));

            return tags;
        } catch (IOException | JSONException ex) {
            throw new SiteAPIException(this, connection, ex);
        } finally {
            if (connection != null)
                connection.disconnect();
        }
    }

    public Tag parseJSONObjectToTag(JSONObject json) throws JSONException {
        return new Tag(json.getInt(DanbooruTag.KEY_TAG_ID), json.getString(DanbooruTag.KEY_TAG_NAME),
                json.getInt(DanbooruTag.KEY_TAG_POST_COUNT));
    }

    @Override
    public List<Post> fetchPosts(Host host, int startFrom, String[] tags) throws SiteAPIException {
        HttpURLConnection connection = null;
        try {
            int limit = host.getPageLimit(DanbooruGallerySettings.getBandwidthUsageType());
            int page = startFrom / limit + 1;

            String url = String.format(URL_POSTS_FORMAT, host.url, page,
                    URLEncoder.encode(TextUtils.join(" ", tags), "UTF-8"), limit);
            Log.v(TAG, String.format("URL: %s", url));
            connection = SiteAPI.openConnection(new URL(url));
            if (!host.getLogin().isEmpty())
                connection.setRequestProperty("Authorization", "Basic " + host.getSecret());
            Reader input = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            Writer output = new StringWriter();

            char buffer[] = new char[_BUFFER_SIZE];
            for (int count = input.read(buffer); count > 0; count = input.read(buffer))
                output.write(buffer, 0, count);

            JSONArray json_posts = new JSONArray(output.toString());
            int len = json_posts.length();
            List<Post> posts = new ArrayList<>(len);
            for (int j = 0; j < len; ++j) {
                try {
                    posts.add(parseJSONObjectToPost(host, json_posts.getJSONObject(j)));
                } catch (JSONException | ParseException ex) {
                    throw new SiteAPIException(this, connection, ex);
                }
            }

            return posts;
        } catch (JSONException | IOException ex) {
            throw new SiteAPIException(this, connection, ex);
        } finally {
            if (connection != null)
                connection.disconnect();
        }
    }

    public static Post parseJSONObjectToPost(Host host, JSONObject json) throws JSONException, ParseException {
        if (json == null)
            return null;

        String file_url = json.getString(DanbooruPost.KEY_POST_FILE_URL);
        String file_url_large = json.getString(DanbooruPost.KEY_POST_LARGE_FILE_URL);
        String file_url_preview = json.getString(DanbooruPost.KEY_POST_PREVIEW_FILE_URL);
        if (!file_url.startsWith("http"))
            file_url = host.url + file_url;
        if (!file_url_large.startsWith("http"))
            file_url_large = host.url + file_url_large;
        if (!file_url_preview.startsWith("http"))
            file_url_preview = host.url + file_url_preview;

        int uploader_id = -1;
        String uploader_name = "";
        try {
            uploader_id = json.getInt(DanbooruPost.KEY_POST_UPLOADER_ID);
        } catch (JSONException ignored) {
        }
        try {
            uploader_name = json.getString(DanbooruPost.KEY_POST_UPLOADER_NAME);
        } catch (JSONException ignored) {
        }

        return new DanbooruPost(host, json.getInt(DanbooruPost.KEY_POST_ID),
                json.getInt(DanbooruPost.KEY_POST_IMAGE_WIDTH), json.getInt(DanbooruPost.KEY_POST_IMAGE_HEIGHT),
                sDateFormat.parse(json.getString(DanbooruPost.KEY_POST_CREATED_AT)),
                sDateFormat.parse(json.getString(DanbooruPost.KEY_POST_UPDATED_AT)),
                json.getInt(DanbooruPost.KEY_POST_FILE_SIZE), file_url, file_url_large, file_url_preview,
                TextUtils.split(json.getString(DanbooruPost.KEY_POST_TAG_STRING), " "),
                json.getString(DanbooruPost.KEY_POST_RATING), uploader_id, uploader_name,
                json.getString(DanbooruPost.KEY_POST_MD5), json.getString(DanbooruPost.KEY_POST_FILE_EXT),
                json.getInt(DanbooruPost.KEY_POST_SCORE), Math.abs(json.getInt(DanbooruPost.KEY_POST_UP_SCORE)),
                Math.abs(json.getInt(DanbooruPost.KEY_POST_DOWN_SCORE)));
    }

    @Override
    public Post getPostFromCursor(Host host, Cursor post_cursor, Cursor tags_cursor) {
        String[] tags;
        if (tags_cursor != null) {
            tags_cursor.moveToPosition(-1);
            tags = new String[tags_cursor.getCount()];
            while (tags_cursor.moveToNext())
                tags[tags_cursor.getPosition()] = tags_cursor.getString(PostTagsView.INDEX_KEY_POST_TAG_TAG_NAME);
        } else
            tags = new String[0];
        return new DanbooruPost(host, post_cursor.getInt(PostsTable.INDEX_POST_POST_ID),
                post_cursor.getInt(PostsTable.INDEX_POST_IMAGE_WIDTH),
                post_cursor.getInt(PostsTable.INDEX_POST_IMAGE_HEIGHT),
                new Date(post_cursor.getLong(PostsTable.INDEX_POST_CREATED_AT)),
                new Date(post_cursor.getLong(PostsTable.INDEX_POST_UPDATED_AT)),
                post_cursor.getInt(PostsTable.INDEX_POST_FILE_SIZE),
                post_cursor.getString(PostsTable.INDEX_POST_FILE_URL),
                post_cursor.getString(PostsTable.INDEX_POST_LARGE_FILE_URL),
                post_cursor.getString(PostsTable.INDEX_POST_PREVIEW_FILE_URL), tags,
                post_cursor.getString(PostsTable.INDEX_POST_RATING),
                post_cursor.getString(PostsTable.INDEX_POST_EXTRA_INFO));
    }

    private static class DanbooruTag extends Tag {
        public DanbooruTag(int id, String name, int post_count) {
            super(id, name, post_count);
        }
    }

    private static class DanbooruPost extends Post {
        public static final String KEY_POST_ID = "id"; // "id":1595369,
        public static final String KEY_POST_CREATED_AT = "created_at"; // "created":"2014-01-17T18:35:43-05:00",
        public static final String KEY_POST_UPDATED_AT = "updated_at"; // "updated":"2014-01-17T18:35:43-05:00",
        public static final String KEY_POST_FILE_SIZE = "file_size"; // "file_size":172776,
        public static final String KEY_POST_IMAGE_WIDTH = "image_width"; // "image_width":640,
        public static final String KEY_POST_IMAGE_HEIGHT = "image_height"; // "image_height":800,
        public static final String KEY_POST_FILE_URL = "file_url"; // "file_url":"/data/80f21ab51c9f9c8779491aaa13eba6c8.jpg",
        public static final String KEY_POST_LARGE_FILE_URL = "large_file_url"; // "large_file_url":"/data/sample/sample-80f21ab51c9f9c8779491aaa13eba6c8.jpg",
        public static final String KEY_POST_PREVIEW_FILE_URL = "preview_file_url"; // "preview_file_url":"/ssd/data/preview/80f21ab51c9f9c8779491aaa13eba6c8.jpg",
        public static final String KEY_POST_TAG_STRING = "tag_string"; // "tag_string":"ark_performance wooser's_hand-to-mouth_life tagme"
        public static final String KEY_POST_RATING = "rating"; // "rating":"s",

        public static final String KEY_POST_MD5 = "md5"; // "md5":"80f21ab51c9f9c8779491aaa13eba6c8",
        public static final String KEY_POST_FILE_EXT = "file_ext"; // "file_ext":"jpg",
        public static final String KEY_POST_UPLOADER_ID = "uploader_id"; // "uploader_id":99262,
        public static final String KEY_POST_UPLOADER_NAME = "uploader_name"; // "uploader_name":"DrLove",
        public static final String KEY_POST_SCORE = "score"; // "score":1,
        public static final String KEY_POST_UP_SCORE = "up_score"; // "up_score":0,
        public static final String KEY_POST_DOWN_SCORE = "down_score"; // "down_score":0,

        public int uploader_id;
        public String uploader_name;
        public String md5;
        public String file_ext;
        public int score, score_up, score_down;

        protected DanbooruPost(// superclass
                Host host, int post_id, int image_width, int image_height, Date created_at, Date updated_at,
                int file_size, String file_url, String file_url_large, String file_url_preview, String[] tags,
                String rating,
                // this class
                int uploader_id, String uploader_name, String md5, String file_ext, int score, int score_up,
                int score_down) {
            super(host, post_id, image_width, image_height, created_at, updated_at, file_size, file_url,
                    file_url_large, file_url_preview, tags, rating);

            this.uploader_id = uploader_id;
            this.uploader_name = uploader_name;
            this.md5 = md5;
            this.file_ext = file_ext;
            this.score = score;
            this.score_up = score_up;
            this.score_down = score_down;
        }

        protected DanbooruPost(// superclass
                Host host, int post_id, int image_width, int image_height, Date created_at, Date updated_at,
                int file_size, String file_url, String file_url_large, String file_url_preview, String[] tags,
                String rating,
                // this class
                String extras) {
            super(host, post_id, image_width, image_height, created_at, updated_at, file_size, file_url,
                    file_url_large, file_url_preview, tags, rating);

            try {
                JSONObject json = new JSONObject(extras);
                try {
                    md5 = json.getString(KEY_POST_MD5);
                } catch (JSONException ignored) {
                }
                try {
                    file_ext = json.getString(KEY_POST_FILE_EXT);
                } catch (JSONException ignored) {
                }
                try {
                    uploader_id = json.getInt(KEY_POST_UPLOADER_ID);
                } catch (JSONException ignored) {
                }
                try {
                    uploader_name = json.getString(KEY_POST_UPLOADER_NAME);
                } catch (JSONException ignored) {
                }
                try {
                    score = json.getInt(KEY_POST_SCORE);
                } catch (JSONException ignored) {
                }
                try {
                    score_up = json.getInt(KEY_POST_UP_SCORE);
                } catch (JSONException ignored) {
                }
                try {
                    score_down = json.getInt(KEY_POST_DOWN_SCORE);
                } catch (JSONException ignored) {
                }
            } catch (JSONException ignored) {
            }
        }

        @Override
        public String getReferer() {
            return host.url + "/posts/" + post_id;
        }

        @Override
        public String getDownloadFilename() {
            return String.format("%1$d - %2$s.%3$s", post_id, TextUtils.join(" ", tags), file_ext);
        }

        @Override
        public String getExtras() {
            JSONObject json = new JSONObject();
            try {
                json.put(KEY_POST_MD5, md5);
            } catch (JSONException ignored) {
            }
            try {
                json.put(KEY_POST_FILE_EXT, file_ext);
            } catch (JSONException ignored) {
            }
            try {
                json.put(KEY_POST_UPLOADER_ID, uploader_id);
            } catch (JSONException ignored) {
            }
            try {
                json.put(KEY_POST_UPLOADER_NAME, uploader_name);
            } catch (JSONException ignored) {
            }
            try {
                json.put(KEY_POST_SCORE, score);
            } catch (JSONException ignored) {
            }
            try {
                json.put(KEY_POST_UP_SCORE, score_up);
            } catch (JSONException ignored) {
            }
            try {
                json.put(KEY_POST_DOWN_SCORE, score_down);
            } catch (JSONException ignored) {
            }
            return json.toString();
        }

        @Override
        public String getWebUrl() {
            return String.format(URL_POST_WEB, host.url, post_id);
        }

        @Override
        public String describeContent(Context context) {
            return context.getResources().getString(R.string.api_danbooru_post_description, host.name, host.url,
                    host.getAPI().getName(), post_id, image_width, image_height, created_at.toString(),
                    updated_at.toString(), rating, uploader_id, uploader_name, md5, file_ext, score, score_up,
                    score_down);
        }
    }
}