/* * Overchan Android (Meta Imageboard Client) * Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan> * * 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 nya.miku.wishmaster.chans.makaba; import static nya.miku.wishmaster.chans.makaba.MakabaConstants.*; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import nya.miku.wishmaster.R; import nya.miku.wishmaster.api.models.AttachmentModel; import nya.miku.wishmaster.api.models.BadgeIconModel; import nya.miku.wishmaster.api.models.BoardModel; import nya.miku.wishmaster.api.models.PostModel; import nya.miku.wishmaster.api.models.ThreadModel; import nya.miku.wishmaster.api.util.RegexUtils; import nya.miku.wishmaster.lib.org_json.JSONArray; import nya.miku.wishmaster.lib.org_json.JSONException; import nya.miku.wishmaster.lib.org_json.JSONObject; import org.apache.commons.lang3.StringEscapeUtils; import android.content.res.Resources; /** * Методы преобразования JSON-объектов с 2ch.hk в универсальные модели * @author miku-nyan * */ public class MakabaJsonMapper { private static final Pattern ICON_PATTERN = Pattern.compile("<img.+?src=\"(.+?)\".+?(?:title=\"(.+?)\")?.*?/>"); static BoardModel defaultBoardModel(String boardName, Resources resources) { BoardModel model = new BoardModel(); model.chan = CHAN_NAME; model.uniqueAttachmentNames = true; model.timeZoneId = "GMT+3"; model.readonlyBoard = false; model.allowDeletePosts = false; model.allowDeleteFiles = false; model.allowReport = BoardModel.REPORT_WITH_COMMENT; model.allowSage = true; model.allowEmails = true; model.ignoreEmailIfSage = true; model.allowCustomMark = true; model.allowRandomHash = true; model.searchAllowed = true; model.catalogAllowed = true; model.catalogTypeDescriptions = new String[] { resources.getString(R.string.makaba_catalog_standart), resources.getString(R.string.makaba_catalog_last_reply), resources.getString(R.string.makaba_catalog_num), resources.getString(R.string.makaba_catalog_image_size) }; model.firstPage = 0; model.attachmentsFormatFilters = ATTACHMENT_FORMATS; model.markType = BoardModel.MARK_BBCODE; model.boardName = boardName; model.boardDescription = boardName; model.boardCategory = ""; model.defaultUserName = "Аноним"; model.bumpLimit = 500; model.lastPage = 9; model.nsfw = SFW_BOARDS.indexOf(model.boardName) == -1; model.requiredFileForNewThread = NO_IMAGES_BOARDS.indexOf(model.boardName) == -1; model.attachmentsMaxCount = NO_IMAGES_BOARDS.indexOf(model.boardName) == -1 ? 4 : 0; model.allowSubjects = NO_SUBJECTS_BOARDS.indexOf(model.boardName) == -1; model.allowNames = NO_USERNAMES_BOARDS.indexOf(model.boardName) == -1; return model; } static BoardModel mapBoardModel(JSONObject source, boolean fromMobileBoardsList, Resources resources) throws JSONException { BoardModel model = defaultBoardModel(source.getString(fromMobileBoardsList ? "id" : "Board"), resources); if (fromMobileBoardsList) { model.boardDescription = source.getString("name"); model.boardCategory = source.getString("category"); model.defaultUserName = getStringSafe(source, "default_name", "Аноним"); model.bumpLimit = getIntSafe(source, "bump_limit", 500); model.lastPage = getIntSafe(source, "pages", 10) - 1; } else { model.boardDescription = source.getString("BoardName"); model.boardCategory = null; model.defaultUserName = "Аноним"; model.bumpLimit = 500; try { model.lastPage = source.getJSONArray("pages").length() - 1; } catch (Exception e) { model.lastPage = 9; } } try { JSONArray iconsArray = source.getJSONArray("icons"); if (iconsArray.length() > 0) { String[] icons = new String[iconsArray.length() + 1]; icons[0] = resources.getString(R.string.makaba_no_icon); for (int i=0; i<iconsArray.length(); ++i) { icons[iconsArray.getJSONObject(i).getInt("num")] = iconsArray.getJSONObject(i).getString("name"); } for (int i=0; i<icons.length; ++i) { if (icons[i] == null) throw new Exception(); } model.allowIcons = true; model.iconDescriptions = icons; } } catch (Exception e) { /* щито поделать, десу, получить список иконок не удалось, или их просто нет */ } return model; } static ThreadModel mapThreadModel(JSONObject source, String boardName) throws JSONException { ThreadModel model = new ThreadModel(); model.threadNumber = source.getString("thread_num"); model.postsCount = source.getInt("posts_count"); model.attachmentsCount = source.getInt("files_count"); JSONArray postsArray = source.getJSONArray("posts"); model.postsCount += postsArray.length(); model.posts = new PostModel[postsArray.length()]; for (int i=0; i<postsArray.length(); ++i) { model.posts[i] = mapPostModel(postsArray.getJSONObject(i), boardName); if (postsArray.getJSONObject(i).has("files")) { model.attachmentsCount += postsArray.getJSONObject(i).getJSONArray("files").length(); } } model.isSticky = getIntSafe(postsArray.getJSONObject(0), "sticky", 0) != 0; model.isClosed = getIntSafe(postsArray.getJSONObject(0), "closed", 0) != 0; return model; } static PostModel mapPostModel(JSONObject source, String boardName) throws JSONException { PostModel model = new PostModel(); try { model.number = source.getString("num"); } catch (JSONException e) { model.number = Long.toString(source.getLong("num")); } model.name = StringEscapeUtils.unescapeHtml4(RegexUtils.removeHtmlSpanTags(getStringSafe(source, "name", ""))); model.subject = StringEscapeUtils.unescapeHtml4(getStringSafe(source, "subject", "")); model.comment = getStringSafe(source, "comment", ""); model.email = getStringSafe(source, "email", ""); if (model.email.startsWith("mailto:")) model.email = model.email.substring(7); model.trip = getStringSafe(source, "trip", ""); if (model.trip != null) { if (model.trip.equals("!!%adm%!!")) model.trip = "## Abu ##"; else if (model.trip.equals("!!%mod%!!")) model.trip = "## Mod ##"; else if (model.trip.equals("!!%Inquisitor%!!")) model.trip = "## Applejack ##"; else if (model.trip.equals("!!%coder%!!")) model.trip = "## Кодер ##"; } model.icons = parseIcons(getStringSafe(source, "icon", "")); model.op = getIntSafe(source, "op", 0) == 1; model.sage = model.email.toLowerCase(Locale.US).contains("sage") || model.name.contains("ID:\u00A0Heaven"); model.timestamp = source.getLong("timestamp") * 1000; model.parentThread = getStringSafe(source, "parent", model.number); if (model.parentThread.equals("0")) model.parentThread = model.number; if (source.has("files")) { JSONArray filesArray = source.getJSONArray("files"); model.attachments = new AttachmentModel[filesArray.length()]; for (int i=0; i<filesArray.length(); ++i) { model.attachments[i] = mapAttachmentModel(filesArray.getJSONObject(i), boardName); } } else model.attachments = null; int banned = getIntSafe(source, "banned", 0); switch (banned) { case 1: model.comment = model.comment + "<br/><em><font color=\"red\">(Автор этого поста был забанен. Помянем.)</font></em>"; break; case 2: model.comment = model.comment + "<br/><em><font color=\"red\">(Автор этого поста был предупрежден.)</font></em>"; break; } return model; } static AttachmentModel mapAttachmentModel(JSONObject source, String boardName) throws JSONException { AttachmentModel model = new AttachmentModel(); try { model.size = source.getInt("size"); model.width = source.getInt("width"); model.height = source.getInt("height"); model.thumbnail = fixAttachmentPath(source.getString("thumbnail"), boardName); model.path = fixAttachmentPath(source.getString("path"), boardName); model.type = AttachmentModel.TYPE_IMAGE_STATIC; String pathLower = model.path.toLowerCase(Locale.US); if (pathLower.endsWith(".gif")) model.type = AttachmentModel.TYPE_IMAGE_GIF; else if (pathLower.endsWith(".webm")) model.type = AttachmentModel.TYPE_VIDEO; } catch (Exception e) { if (source.has("path")) { model.type = AttachmentModel.TYPE_OTHER_FILE; model.path = fixAttachmentPath(source.getString("path"), boardName); } else { model.type = AttachmentModel.TYPE_OTHER_NOTFILE; } } return model; } static BadgeIconModel[] parseIcons(String html) { if (html == null || html.length() == 0) return null; Matcher m = ICON_PATTERN.matcher(html); List<BadgeIconModel> list = new ArrayList<BadgeIconModel>(); while (m.find()) { BadgeIconModel icon = new BadgeIconModel(); icon.source = m.group(1); icon.description = m.group(2); list.add(icon); } return list.toArray(new BadgeIconModel[list.size()]); } private static String getStringSafe(JSONObject object, String key, String defaultValue) { try { return object.getString(key); } catch (Exception e) { return defaultValue; } } private static int getIntSafe(JSONObject object, String key, int defaultValue) { try { return object.getInt(key); } catch (Exception e) { return defaultValue; } } private static String fixAttachmentPath(String url, String boardName) { if (url.startsWith("://")) return "http" + url; if (url.startsWith("/") || url.startsWith("http://") || url.startsWith("https://")) { return url; } return "/".concat(boardName).concat("/").concat(url); } }