package net.lamgc.cgj.pixiv;
import com.google.common.base.Strings;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
/**
* Pixiv搜索URL构造器
*
该构造器通过分析Pixiv搜索链接可用的参数而开发, 对搜索链接的构造有高度自定义能力.
* @author LamGC
* @see PixivURL#PIXIV_SEARCH_CONTENT_URL
*/
@SuppressWarnings("unused")
public class PixivSearchLinkBuilder {
private final String content;
private SearchArea searchArea = SearchArea.ARTWORKS;
private SearchMode searchMode = SearchMode.TAG_FULL;
private SearchType searchType = SearchType.ILLUST_AND_UGOIRA;
private SearchOrder searchOrder = SearchOrder.DATE_D;
private SearchContentOption searchContentOption = SearchContentOption.ALL;
private final HashSet includeKeywords = new HashSet<>(0);
private final HashSet excludeKeywords = new HashSet<>(0);
private int page = 1;
private int wgt = 0;
private int hgt = 0;
private int wlt = 0;
private int hlt = 0;
private RatioOption ratioOption = null;
private Date startDate = null;
private Date endDate = null;
public PixivSearchLinkBuilder(String searchContent) {
this.content = Objects.requireNonNull(searchContent);
}
public String buildURL() {
StringBuilder builder;
try {
builder = new StringBuilder(PixivURL.PIXIV_SEARCH_CONTENT_URL.replaceAll("\\{area}", searchArea.name().toLowerCase())
.replaceAll("\\{content}",
URLEncoder.encode(getSearchCondition(), "UTF-8").replaceAll("\\+", "%20")
)
);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
if(searchArea.equals(SearchArea.TOP)) {
return builder.toString();
}
builder.append("&s_mode=").append(searchMode.name().toLowerCase());
builder.append("&type=").append(searchType.name().toLowerCase());
builder.append("&p=").append(page);
builder.append("&order=").append(searchOrder.name().toLowerCase());
builder.append("&mode=").append(searchContentOption.name().toLowerCase());
//可选参数
if(wgt > 0 && hgt > 0) {
builder.append("&wgt=").append(wgt);
builder.append("&hgt").append(hgt);
}
//可选参数
if(wlt > 0 && hlt > 0) {
builder.append("&wlt=").append(wlt);
builder.append("&hlt").append(hlt);
}
if (ratioOption != null) {
builder.append("&ratio=").append(ratioOption.value);
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
if (startDate != null) {
builder.append("&scd=").append(format.format(startDate));
}
if (endDate != null) {
builder.append("&ecd=").append(format.format(endDate));
}
return builder.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PixivSearchLinkBuilder that = (PixivSearchLinkBuilder) o;
return page == that.page &&
wgt == that.wgt &&
hgt == that.hgt &&
wlt == that.wlt &&
hlt == that.hlt &&
content.equals(that.content) &&
searchArea == that.searchArea &&
searchMode == that.searchMode &&
searchType == that.searchType &&
searchOrder == that.searchOrder &&
searchContentOption == that.searchContentOption &&
includeKeywords.equals(that.includeKeywords) &&
excludeKeywords.equals(that.excludeKeywords) &&
ratioOption == that.ratioOption &&
Objects.equals(startDate, that.startDate) &&
Objects.equals(endDate, that.endDate);
}
@Override
public int hashCode() {
return Objects.hash(
content,
searchArea,
searchMode,
searchType,
searchOrder,
searchContentOption,
includeKeywords,
excludeKeywords,
page,
wgt,
hgt,
wlt,
hlt,
ratioOption,
startDate,
endDate);
}
@Override
public String toString() {
return "PixivSearchLinkBuilder{" +
"content='" + content + '\'' +
", searchArea=" + searchArea +
", searchMode=" + searchMode +
", searchType=" + searchType +
", searchOrder=" + searchOrder +
", searchContentOption=" + searchContentOption +
", includeKeywords=" + includeKeywords +
", excludeKeywords=" + excludeKeywords +
", page=" + page +
", wgt=" + wgt +
", hgt=" + hgt +
", wlt=" + wlt +
", hlt=" + hlt +
", ratioOption=" + ratioOption +
", startDate=" + startDate +
", endDate=" + endDate +
'}';
}
public String getContent() {
return content;
}
public PixivSearchLinkBuilder setSearchArea(SearchArea searchArea) {
this.searchArea = Objects.requireNonNull(searchArea);
return this;
}
/**
* 获取搜索条件.
* @return 搜索条件内容
*/
public String getSearchCondition() {
StringBuilder searchContent = new StringBuilder(Strings.nullToEmpty(this.content));
if(searchArea.equals(SearchArea.TOP)) {
return searchContent.toString();
}
excludeKeywords.forEach(keyword -> searchContent.append(" -").append(keyword));
if(!includeKeywords.isEmpty()) {
if(!Strings.isNullOrEmpty(searchContent.toString())) {
searchContent.append(" (");
}
includeKeywords.forEach(keyword -> searchContent.append(keyword).append(" OR "));
int deleteStart = searchContent.lastIndexOf(" OR ");
if(searchContent.length() >= 4 && deleteStart != -1) {
searchContent.delete(deleteStart, searchContent.length());
}
if(!Strings.isNullOrEmpty(searchContent.toString())) {
searchContent.append(")");
}
}
return searchContent.toString();
}
public PixivSearchLinkBuilder setSearchMode(SearchMode searchMode) {
this.searchMode = Objects.requireNonNull(searchMode);
return this;
}
public PixivSearchLinkBuilder setSearchType(SearchType searchType) {
this.searchType = Objects.requireNonNull(searchType);
return this;
}
public PixivSearchLinkBuilder setSearchOrder(SearchOrder searchOrder) {
this.searchOrder = Objects.requireNonNull(searchOrder);
return this;
}
public PixivSearchLinkBuilder setSearchContentOption(SearchContentOption searchContentOption) {
this.searchContentOption = Objects.requireNonNull(searchContentOption);
return this;
}
public PixivSearchLinkBuilder setRatioOption(RatioOption ratioOption) {
this.ratioOption = Objects.requireNonNull(ratioOption);
return this;
}
public PixivSearchLinkBuilder setDateRange(Date startDate, Date endDate) {
this.startDate = startDate;
this.endDate = endDate;
return this;
}
public PixivSearchLinkBuilder setMaxSize(int width, int height) {
this.wgt = width;
this.hgt = height;
return this;
}
public PixivSearchLinkBuilder setMinSize(int width, int height) {
this.wlt = width;
this.hlt = height;
return this;
}
public PixivSearchLinkBuilder setPage(int pageIndex) {
if (pageIndex <= 0) {
throw new IllegalArgumentException("Invalid pageIndex: " + pageIndex);
}
this.page = pageIndex;
return this;
}
public PixivSearchLinkBuilder addExcludeKeyword(String keyword) {
excludeKeywords.add(keyword);
return this;
}
public PixivSearchLinkBuilder removeExcludeKeyword(String keyword) {
excludeKeywords.remove(keyword);
return this;
}
public PixivSearchLinkBuilder addIncludeKeyword(String keyword) {
includeKeywords.add(keyword);
return this;
}
public PixivSearchLinkBuilder removeIncludeKeyword(String keyword) {
includeKeywords.remove(keyword);
return this;
}
/**
* 搜索区域
*/
public enum SearchArea {
/**
* 所有(可能是 插画 + 漫画)
*/
ARTWORKS("illustManga"),
/**
* 顶部(所有内容)
* 同时包含了:
* {@link #ILLUSTRATIONS}
* {@link #MANGA}
* {@link #NOVELS}
* 选择此项后, 将直接显示所有与content相关内容, 而忽略所有附加搜索条件.
* 因为无法指定pageIndex, 数据只有24项
*/
TOP(null),
/**
* 插画
*/
ILLUSTRATIONS("illust"),
/**
* 漫画
*/
MANGA("manga"),
/**
* 小说
*/
NOVELS("novel");
public final String jsonKey;
SearchArea(String jsonKey) {
this.jsonKey = jsonKey;
}
}
/**
* 搜索模式
*/
public enum SearchMode {
/**
* 按标签搜索, 部分一致
*/
TAG,
/**
* 按标签搜索, 完全一致
*/
TAG_FULL,
/**
* 按标题和说明文字搜索
*/
TC
}
/**
* 搜索内容类型
*/
public enum SearchType {
/**
* 全部内容(插画、漫画、动图)
*/
ALL,
/**
* 插画和动图(不包括漫画)
*/
ILLUST_AND_UGOIRA,
/**
* 插图
*/
ILLUST,
/**
* 漫画
*/
MANGA,
/**
* 动图
*/
UGOIRA
}
public enum SearchOrder {
/**
* 按旧排序
*/
DATE,
/**
* 按新排序
*/
DATE_D
}
/**
* 搜索内容选项
*/
public enum SearchContentOption {
/**
* 所有内容
*/
ALL,
/**
* 全年龄
*/
SAFE,
/**
* R18
*/
R18
}
public enum RatioOption {
/**
* 横向
*/
TRANSVERSE(0.5F),
/**
* 纵向
*/
PORTRAIT(-0.5F),
/**
* 正方形
*/
SQUARE(0F)
;
public final float value;
RatioOption(float ratio) {
this.value = ratio;
}
}
}