/* * Copyright (C) 2015 Hippo Seven * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hippo.ehviewer.client; import com.hippo.ehviewer.data.Comment; import com.hippo.ehviewer.data.TagGroup; import com.hippo.ehviewer.util.EhUtils; import com.hippo.util.AssertUtils; import com.hippo.util.Utils; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; public class GalleryDetailParser { private static final DateFormat WEB_COMMENT_DATA_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm z", Locale.US); private static final DateFormat OUT_COMMENT_DATA_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); static { WEB_COMMENT_DATA_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); OUT_COMMENT_DATA_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); } private static final String OFFENSIVE_STRING = "

(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)

"; private static final String PINING_STRING = "

This gallery is pining for the fjords.

"; public static final int REQUEST_DETAIL = 0x1; public static final int REQUEST_TAG = 0x2; public static final int REQUEST_PREVIEW_INFO = 0x4; public static final int REQUEST_COMMENT = 0x10; private static final CommentSort COMMENT_SORTER = new CommentSort(); public int gid; public String token; public String thumb; public String title; public String titleJpn; public int category; public String uploader; public int torrentCount; public String torrentUrl; public String posted; public String parent; public String visible; public String language; public String size; public String resize; public int pageCount; public int favoredTimes; public boolean isFavored; public float rating; public int ratedTimes; public List tags; public List comments; public int previewPageCount; private String sortOut(String str) { // Avoid null if (str == null) { str = ""; } return Utils.unescapeXml(str).trim(); } private int sortOutInteger(String str, int defaultValue) { return Utils.parseIntSafely(sortOut(str).replace(",", ""), defaultValue); } private void parseTagGroup(TagGroup tagGroup, String body) { Pattern p = Pattern.compile("]+>]+>([\\w\\s]+)"); Matcher m = p.matcher(body); while (m.find()) { tagGroup.addTag(sortOut(m.group(1))); } } private void parse(String body, int request) throws EhException { Pattern p; Matcher m; if (!body.startsWith("<")) { throw new EhException(body); } if (body.contains(OFFENSIVE_STRING)) { throw new OffensiveException(); } if (body.contains(PINING_STRING)) { throw new PiningException(); } // TODO I can't remember what's this for p = Pattern.compile("
\n

([^<]+)

"); m = p.matcher(body); if (m.find()) { throw new EhException(m.group(1)); } if ((request & REQUEST_DETAIL) != 0) { p = Pattern.compile("var gid = (\\d+)" // 1 gid + ".+?" + "var token = \"([a-z0-9A]+)\"" // 2 token + ".+?" + "
]+>
" // 3 thumb + "
" + "
" + "

([^<>]+)

" // 4 title + "

([^<>]*)

" // 5 title_jpn might be empty string + "
" + ".+?" + "]*onclick=\"return popUp\\('([^']+)'[^)]+\\)\">Torrent Download \\( (\\d+) \\)" // 6 torrentUrl, 7 torrentCount + ".+?" + "
]+><[^<>]*alt=\"([\\w|\\-]+)\"[^<>]*>
" // 8 category + "
]+>([^<>]+)" // 9 uploader + ".+?" + "]*>Posted:]*>([\\w|\\-|\\s|:]+)" // 10 posted + "]*>Parent:]*>(?:]*>)?([^<>]+)(?:)?" // 11 parent + "]*>Visible:]*>([^<>]+)" // 12 visible + "]*>Language:]*>([^<>]+)(?:]*>[^<>]*)?" // 13 language + "]*>File Size:]*>([^<>]+)(?:]*>([^<>]+))?" // 14 File size, 15 resize + "]*>Length:]*>([\\d|,]+) pages" // 16 pageCount + "]*>Favorited:<[^<>]*>([^<>]+)" // 17 Favorite times "([\d|,]+) times" or "Once" or "Never" + ".+?" + "([\\d|,]+)" // 18 ratedTimes + "" + "]*>([^<>]+)" // 19 rating "Average: x.xx" or "Not Yet Rated" + ".+?" + "]*>(.+?)", Pattern.DOTALL); // 20 isFavored "Favorite Gallery" for favorite m = p.matcher(body); if (m.find()) { gid = sortOutInteger(m.group(1), 0); token = sortOut(m.group(2)); thumb = sortOut(m.group(3)); title = sortOut(m.group(4)); titleJpn = sortOut(m.group(5)); torrentUrl = sortOut(m.group(6)); torrentCount = sortOutInteger(m.group(7), 0); category = EhUtils.getCategory(sortOut(m.group(8))); uploader = sortOut(m.group(9)); posted = sortOut(m.group(10)); parent = sortOut(m.group(11)); visible = sortOut(m.group(12)); language = sortOut(m.group(13)); size = sortOut(m.group(14)); resize = sortOut(m.group(15)); pageCount = sortOutInteger(m.group(16), 0); String favTimeStr = sortOut(m.group(17)); switch (favTimeStr) { case "Never": favoredTimes = 0; break; case "Once": favoredTimes = 1; break; default: int index = favTimeStr.indexOf(' '); if (index == -1) { favoredTimes = 0; } else { favoredTimes = Utils.parseIntSafely(favTimeStr.substring(0, index), 0); } break; } ratedTimes = sortOutInteger(m.group(18), 0); String ratingStr = sortOut(m.group(19)); if ("Not Yet Rated".equals(ratingStr)) { rating = Float.NaN; } else { int index = ratingStr.indexOf(' '); if (index == -1 || index >= ratingStr.length()) { rating = 0f; } else { rating = Utils.parseFloatSafely(ratingStr.substring(index + 1), 0f); } } isFavored = "Favorite Gallery".equals(sortOut(m.group(20))); } else { throw new ParseException("Parse gallery body error", body); } } if ((request & REQUEST_TAG) != 0) { tags = new LinkedList<>(); p = Pattern.compile("]+>([\\w\\s]+):(?:]+>]+>[\\w\\s]+
)+"); m = p.matcher(body); while (m.find()) { TagGroup tagGroup = new TagGroup(); tagGroup.groupName = sortOut(m.group(1)); parseTagGroup(tagGroup, m.group(0)); tags.add(tagGroup); } } // Get preview info if ((request & REQUEST_PREVIEW_INFO) != 0) { p = Pattern.compile("]+>]+>([\\d,]+)]+>(?:]+>)?>(?:)?"); m = p.matcher(body); if (m.find()) { previewPageCount = sortOutInteger(m.group(1), 0); } else { previewPageCount = 0; } if (previewPageCount <= 0) { throw new ParseException("Parse preview page count error", body); } } // Get comment if ((request & REQUEST_COMMENT) != 0) { p = Pattern.compile("
Posted on ([^<>]+) by:   ]+>([^<>]+).+?
]*>(.+?)
(); while (m.find()) { String webDateString = sortOut(m.group(1)); Date date; try { date = WEB_COMMENT_DATA_FORMAT.parse(webDateString); } catch (java.text.ParseException e) { date = new Date(0l); } String outDateString = OUT_COMMENT_DATA_FORMAT.format(date); comments.add(new Comment(outDateString, date, sortOut(m.group(2)), m.group(3))); } Collections.sort(comments, COMMENT_SORTER); } } public void parse(String body, int request, int source) throws Exception { AssertUtils.assertNotNull("Body is null when parse gallery list", body); switch (source) { default: case EhClient.SOURCE_G: case EhClient.SOURCE_EX: { parse(body, request); break; } case EhClient.SOURCE_LOFI: { //parseLofi(body, request); //break; throw new EhException("Not support lofi now"); } } } static class CommentSort implements Comparator { @Override public int compare(Comment c1, Comment c2) { return c1.date.compareTo(c2.date); } } }