8 Commits
Author SHA1 Message Date
leosam1024 758ac48627 MOD:优化编码格式utf-8 2023-08-06 15:28:55 +08:00
leosam1024 4af5166003 MOD:优化乱码查询-2 2023-07-15 19:01:12 +08:00
leosam1024 793ce9a20d MOD:优化乱码查询 2023-07-15 18:50:28 +08:00
leosam1024 82df0d274c MOD:支持docker 部署 2023-07-11 09:06:31 +08:00
leosam1024 2a09fb46af MOD:修正乱码 2023-06-29 22:30:40 +08:00
leosam1024 d74a4198d6 MOD:修复字段问题 2023-06-26 22:51:48 +08:00
leosam1024 1de2605158 MOD:修复错误 2023-06-25 08:48:17 +08:00
leosam1024 6a214d9cb3 MOD:支持加载其他目录索引 2023-06-25 08:12:29 +08:00
15 changed files with 522 additions and 100 deletions
+27
View File
@@ -0,0 +1,27 @@
# 使用支持Maven打包的Java 11环境的基础镜像
FROM maven:3.8.4-openjdk-11 AS build
# 设置工作目录
WORKDIR /app
# 拉取项目代码
COPY src/ ./src/
COPY pom.xml .
# 使用Maven进行构建和打包
RUN mvn clean package
# 使用一个仅包含JRE 17的运行时镜像
FROM openjdk:11-jre-slim
# 设置工作目录
WORKDIR /app
# 从构建镜像中复制打包好的jar文件到运行镜像中
COPY --from=build /app/target/tvbox-mv-0.0.1-SNAPSHOT.jar /app/tvbox-mv-0.0.1-SNAPSHOT.jar
# 暴露端口
EXPOSE 7777
# 运行应用程序
CMD ["java", "-jar", "tvbox-mv-0.0.1-SNAPSHOT.jar"]
+117 -2
View File
@@ -3,9 +3,11 @@ mv视频搜索服务
# 请求接口
## Vod搜索接口
## MV视频Vod搜索接口
http://localhost:7777/mv/vod?maxCount=100&wd=五月天后来的我们
该接口只请求MV视频
请求参数定义
~~~
wd :搜索值,mv名称或者歌手名 都可以
@@ -31,8 +33,34 @@ maxCount:最大返回值,返回结果里面 list 最大数量。 最大值10
}
~~~
## 自定义数据源-Vod搜索接口
http://localhost:7777/vod/:index?maxCount=100&wd=五月天后来的我们
## 搜素接口
### 使用方法
1.`jar`所在文件目录下创建`data`文件夹
2. 将自定义数据源文件放入`data`文件夹下
3. 自定义数据源名称为数据源文件名,如`dome.json`,则数据源名称为`dome`
4. 自定义数据源文件格式如下: `视频名称,视频链接`
4. 请求接口`http://localhost:7777/vod/dome?maxCount=100&wd=五月天后来的我们`
请求路径参数定义
~~~
:index :数据源名称,如:dome,不带文件后缀
~~~
请求参数定义
~~~
wd :搜索值,mv名称或者歌手名 都可以
ids vodId,同wd
maxCount:最大返回值,返回结果里面 list 最大数量。 最大值1000
~~~
返回参数结果
~~~
同上
~~~
## MV视频搜素接口
http://localhost:7777/mv/search?maxCount=100&query=五月天后来的我们
请求参数定义
@@ -71,6 +99,7 @@ maxCount:最大返回值,返回结果里面 list 最大数量。 最大值10
# TvBox配置
~~~ json
// 示例
{
"sites": [
{
@@ -81,8 +110,94 @@ maxCount:最大返回值,返回结果里面 list 最大数量。 最大值10
"searchable": 1,
"quickSearch": 1,
"filterable": 1
}, {
"key": "MV_vod_DOME",
"name": "👀┃DOME┃视频",
"type": 1,
"api": "http://你自己域名:7777/dome",
"searchable": 1,
"quickSearch": 1,
"filterable": 1
}
]
}
// 已有接口-任选其一即可
{
"sites": [
{
"key": "MV_vod",
"name": "🎸┃明星-接口1┃MV",
"type": 1,
"api": "https://tvbox-mv--leosam2048.repl.co/mv/vod",
"searchable": 1,
"quickSearch": 1,
"filterable": 1
}, {
"key": "MV_vod",
"name": "🎸┃明星-接口2┃MV",
"type": 1,
"api": "https://mv.饭太硬.ml/mv/vod",
"searchable": 1,
"quickSearch": 1,
"filterable": 1
}
]
}
~~~
# 安装和启动
## 使用docker进行启动
### 镜像获取(二选一)
docker中央仓库拉取
~~~sh
# 注:需要自行安装docker环境
# 拉取git仓库
docker pull leosam1024/tvbox-mv:latest
~~~
本地构建docker镜像
~~~sh
# 注:需要自行安装git和docker环境
# 拉取git仓库
git clone https://github.com/leosam1024/tvbox-mv.git
# 进入仓库
cd tvbox-mv
# 构建docker镜像
docker build ./ -t leosam1024/tvbox-mv:latest
~~~
### 启动
~~~sh
# 运行docker镜像(前台运行)
docker run -p 7777:7777 leosam1024/tvbox-mv:latest
# 运行docker镜像(后台运行)
docker run -dit --restart always -p 8898:7777 leosam1024/tvbox-mv:latest
# 运行docker镜像(后台运行+指定容器名称) -- 推荐
docker run -dit --name tvbox-mv --hostname tvbox-mv --restart always -p 7777:7777 leosam1024/tvbox-mv:latest
# 运行docker镜像(后台运行+指定容器名称+自定义数据)
docker run -dit --name tvbox-mv --hostname tvbox-mv --restart always -p 7777:7777 -v /opt/mv/data:/app/data leosam1024/tvbox-mv:latest
# 如果再次构建运行的时候,出现说container冲突的话,删除container对应的的ID就行
docker rm container对应的的ID
~~~
+6
View File
@@ -0,0 +1,6 @@
docker build -t tvbox-mv:latest .
# docker login
# docker images
# docker tag d4c90f23cb5f leosam1024/tvbox-mv:latest
# docker push leosam1024/tvbox-mv:latest
+24
View File
@@ -0,0 +1,24 @@
水陆古法养鱼池塘缸,保姆级教程,https://www.bilibili.com/video/BV1Bg4y1K7yC/
伤心情歌,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgmvmc4gUo8NSjoQI.mp4
等你等到我心痛,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAg5fTW4gUoypKv_AQ.mp4
高安杭娇_一生无悔,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgtL6q4gUojIS-mQE.mp4
刚好遇见你,http://15799848.s21v.faiusr.com/58/ABUIABA6GAAg3q6M5gUohPidmQc.mp4
甘心情愿爱着你.安东阳张怡诺,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgq_2a4gUo686M8wc.mp4
风中花雨楼.任妙音,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgypLV4gUo-ZmUhwc.mp4
分手.冷漠,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgwsfE4gUokp27lAQ.mp4
放狠爱.慕容晓晓肖玄,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgpoLG4gUo0JOBggI.mp4
渡红尘.张碧晨,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgkN-64wUorOL7mQI.mp4
都说.龙梅子老猫,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAghPbQ7AUohPLwrAM.mp4
丁当_野兽,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAg0MOm4gUo-LnKpQE.mp4
邓紫棋_喜欢你,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAg6uyj4QUosO5G.mp4
等你一万年.白雪,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgyKyM5gUooJurKA.mp4
等你一万年,http://15799848.s21v.faiusr.com/58/ABUIABA6GAAgyKyM5gUooJurKA.mp4
等到山花开_钟小冰,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAggcOv5wUo4pShggc.mp4
当爱离别时,http://15799848.s21v.faiusr.com/58/ABUIABA6GAAgstTE4gUo856frgY.mp4
当,http://15799848.s21v.faiusr.com/58/ABUIABA6GAAgpPTW4gUoz_DgdQ.mp4
单身情歌_黄晓凤,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgjfPW4gUo4umD3QE.mp4
错过了缘分错过你_候俊辉,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAgqfLW4gUogNTT1wY.mp4
崔子格老猫_老婆最大,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAg29is4gUopvmF8wI.mp4
传奇_黄晓凤,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAg9b62-AUomeie4gY.mp4
成龙金喜善_美丽的神话,https://15799848.s21v.faiusr.com/58/ABUIABA6GAAg_tma4gUowLvk9wM.mp4
陈慧娴-逝去的诺言,http://bizcommon.alicdn.com/l2nDqpMmn6DGHnWzZQA/i78cWdjCSglPJHDaedL%40%40ld.m3u8
+3 -4
View File
@@ -9,7 +9,9 @@
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.encoding>UTF-8</project.encoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -116,9 +118,6 @@
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
@@ -53,6 +53,8 @@ public class MainVerticle extends AbstractVerticle {
Router router = Router.router(vertx);
router.route("/mv/search").handler(this::searchMv);
router.route("/mv/vod").handler(this::searchMvVod);
router.route("/vod").handler(this::searchVod);
router.route("/vod/:index").handler(this::searchVod);
router.route("/*").handler(StaticHandler.create("static"));
router.route().handler(this::home);
httpServer.requestHandler(router);
@@ -63,49 +65,63 @@ public class MainVerticle extends AbstractVerticle {
logger.info("HTTP server started on port 7777");
} else {
startPromise.fail(http.cause());
logger.info("HTTP server fail, on port 7777, 启动失败,更换端口重试", http.cause());
}
});
port = httpServer.actualPort();
}
private void searchMvVod(RoutingContext req) {
searchVod(req, MvService.MV_FILE_NAME);
}
private void searchVod(RoutingContext req) {
searchVod(req, StringUtils.EMPTY);
}
/**
* 参考 <a href="https://github.com/FongMi/TV/blob/release/app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java#L32">...</a>
* @param req
*/
private void searchMvVod(RoutingContext req) {
private void searchVod(RoutingContext req, String index) {
// 获取参数
String wd = VertxUtils.queryParam(req, "wd");
String ids = VertxUtils.queryParam(req, "ids");
String query = StringUtils.isNotEmpty(wd) ? wd : ids;
String query = StringUtils.trimToEmpty(StringUtils.defaultIfEmpty(wd, ids));
query = StringUtils.cleanString(query);
String ac = VertxUtils.queryParam(req, "ac");
String type = VertxUtils.queryParam(req, "t");
//query = StringUtils.isNotEmpty(query) ? query : "我";
String maxCount = VertxUtils.queryParam(req, "maxCount");
int max = Math.min(Math.max(NumberUtils.toInt(maxCount, 200), 10), 1000);
String pg = VertxUtils.queryParam(req, "pg");
int page = NumberUtils.toInt(pg, 0);
if(StringUtils.isEmpty(index)){
String pathIndex = req.pathParam("index");
String queryIndex = VertxUtils.queryParam(req, "index");
index = StringUtils.trimToEmpty(StringUtils.defaultIfEmpty(pathIndex, queryIndex));
}
// 处理结果
VodResult vodResult = null;
try {
// 搜索
if (mvService != null && StringUtils.isNotEmpty(query) && page <= 0) {
vodResult = mvService.searchVod(query, max);
vodResult = mvService.searchVod(index, query, max);
}
// 首页
if (mvService != null && StringUtils.isEmpty(query)) {
vodResult = mvService.searchVodHome(type, page);
vodResult = mvService.searchVodHome(index, type, page);
}
} catch (Exception e) {
throw new RuntimeException(e);
logger.error("searchVod fail, index={}, query={}, page={} ", index, query, page, e);
}
// 返回数据
String jsonString = Json.encode(vodResult);
req.response()
.putHeader("content-type", "application/json")
.putHeader("content-type", "application/json;charset=utf-8")
.end(jsonString);
}
@@ -114,6 +130,7 @@ public class MainVerticle extends AbstractVerticle {
String query = VertxUtils.queryParam(req, "query");
String wd = VertxUtils.queryParam(req, "wd");
query = StringUtils.isNotEmpty(query) ? query : wd;
query = StringUtils.cleanString(query);
String maxCount = VertxUtils.queryParam(req, "maxCount");
int max = Math.min(Math.max(NumberUtils.toInt(maxCount, 200), 10), 1000);
@@ -121,16 +138,16 @@ public class MainVerticle extends AbstractVerticle {
MvResult search = null;
try {
if (mvService != null) {
search = mvService.search(query, max);
search = mvService.search(null, query, max);
}
} catch (Exception e) {
throw new RuntimeException(e);
logger.error("searchMv fail, query={}, maxCount={} ", query, maxCount, e);
}
// 返回数据
String jsonString = Json.encode(search);
req.response()
.putHeader("content-type", "application/json")
.putHeader("content-type", "application/json;charset=utf-8")
.end(jsonString);
}
@@ -1,5 +1,6 @@
package com.leosam.tvbox.mv.lucene;
import com.leosam.tvbox.mv.utils.StringUtils;
import io.vertx.ext.web.impl.LRUCache;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
@@ -7,10 +8,14 @@ import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.StoredFields;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
@@ -55,6 +60,34 @@ public class MvSearcher {
return docs;
}
public TopDocs searchTopDocs(String field, String queryStr, String termField, String termQueryStr, int maxHit) throws Exception {
String cacheKey = field + "_" + queryStr + "_" + termField + "_" + termQueryStr + "_" + maxHit;
TopDocs topDocs = topDocsLruCache.get(cacheKey);
if (topDocs != null) {
return topDocs;
}
BooleanQuery.Builder queryBuilder = new BooleanQuery.Builder();
// 全文检索
if(StringUtils.isNotEmpty(queryStr)){
Analyzer analyzer = new SmartChineseAnalyzer();
// Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser(field, analyzer);
Query query = parser.parse(queryStr);
queryBuilder.add(query, BooleanClause.Occur.MUST);
}
// 关键字检索
if (StringUtils.isNotEmpty(termQueryStr)) {
Query keywordQuery = new TermQuery(new Term(termField, termQueryStr));
queryBuilder.add(keywordQuery, BooleanClause.Occur.MUST);
}
BooleanQuery combinedQuery = queryBuilder.build();
TopDocs docs = searcher.search(combinedQuery, maxHit);
topDocsLruCache.put(cacheKey, docs);
return docs;
}
public void search(String field, String queryStr) throws Exception {
Analyzer analyzer = new SmartChineseAnalyzer();
// Analyzer analyzer = new StandardAnalyzer();
@@ -26,7 +26,6 @@ import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@@ -41,11 +40,16 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class MvService {
private static final Logger logger = LoggerFactory.getLogger(MvService.class);
public static final String MV_FILE_NAME = "16wMV";
private static final String indexDirectoryPath = "index";
private static final String MV_FILE = "tvbox/16wMV.txt";
private static final Map<String, List<String>> homeConfigMap = new LinkedHashMap<>();
private static final Map<String, String> singerMap = new LinkedHashMap<>();
private static final String HOME_KEY = "HOME";
private static final List<String> FILE_PATH_LIST = List.of(
"classpath:tvbox/16wMV.txt",
"./data/",
"../data/"
);
/**
* 最低相似分数
*/
@@ -53,15 +57,18 @@ public class MvService {
private MvSearcher mvSearcher;
public MvResult search(String query, int max) throws Exception {
if (StringUtils.isEmpty(query) || mvSearcher == null) {
public MvResult search(String index, String query, int max) throws Exception {
if (mvSearcher == null || max > 10000) {
return new MvResult();
}
if (StringUtils.isEmpty(index) && StringUtils.isEmpty(query)) {
return new MvResult();
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
TopDocs topDocs = mvSearcher.searchTopDocs("name", query, max);
TopDocs topDocs = mvSearcher.searchTopDocs("name", query, "index", index, max);
MvResult result = new MvResult();
result.setQuery(query);
@@ -69,7 +76,7 @@ public class MvService {
result.setList(new ArrayList<>(topDocs.scoreDocs.length));
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
// 分数太低 忽略
if (scoreDoc.score < MIN_QUERY_SCORE) {
if (scoreDoc.score < MIN_QUERY_SCORE && result.getList().size() > 50) {
break;
}
Document document = mvSearcher.getDocument(scoreDoc.doc);
@@ -103,16 +110,16 @@ public class MvService {
return result;
}
public VodResult searchVod(String wd, int max) throws Exception {
MvResult search = search(wd, max);
public VodResult searchVod(String index, String wd, int max) throws Exception {
MvResult search = this.search(index, wd, max);
if (CollectionUtils.isEmpty(search.getList())) {
return new VodResult();
}
Vod vod = new Vod();
vod.setVodId(wd);
vod.setVodName(wd);
vod.setVodPlayFrom("mv");
vod.setVodPic(getVodPic(wd));
vod.setVodPlayFrom(StringUtils.defaultIfEmpty(index, "mv"));
vod.setVodPic(getVodPic(index, wd));
List<String> playUrlList = new LinkedList<>();
Set<String> vodActorList = new LinkedHashSet<>();
for (MvContent content : search.getList()) {
@@ -130,7 +137,7 @@ public class MvService {
return vodResult;
}
public VodResult searchVodHome(String type, int page) throws Exception {
public VodResult searchVodHome(String index, String type, int page) throws Exception {
// 防止分页请求
if (page > 1) {
return new VodResult();
@@ -141,6 +148,7 @@ public class MvService {
VodResult vodResult = new VodResult().init();
if (MV_FILE_NAME.equals(index)) {
// 分类
if (StringUtils.isEmpty(type)) {
vodResult.setVodClassList(new ArrayList<>(homeConfigMap.size()));
@@ -162,21 +170,34 @@ public class MvService {
Vod vod = new Vod();
vod.setVodId(singer);
vod.setVodName(singer);
vod.setVodPlayFrom("mv");
vod.setVodPic(getVodPic(singer));
vod.setVodPlayFrom(StringUtils.defaultIfEmpty(index, "mv"));
vod.setVodPic(getVodPic(index, singer));
vodResult.getList().add(vod);
}
stopWatch.stop();
logger.info("加载首页成功,类型={}, 返回={}条, 耗时{}毫秒", thisType, vodResult.getList().size(), stopWatch.getTotalTimeMillis());
} else {
vodResult.setList(new ArrayList<>());
MvResult search = search(index, "", 100);
if (CollectionUtils.isNotEmpty(search.getList())) {
for (MvContent content : search.getList()) {
Vod vod = new Vod();
vod.setVodId(content.getName());
vod.setVodName(content.getName());
vod.setVodPlayFrom(StringUtils.defaultIfEmpty(index, "mv"));
vod.setVodPic(getVodPic(index, content.getName()));
vodResult.getList().add(vod);
}
}
logger.info("加载首页成功,文件={}, 返回={}条, 耗时{}毫秒", index, vodResult.getList().size(), stopWatch.getTotalTimeMillis());
}
return vodResult;
}
private String getVodPic(String vodName) {
private String getVodPic(String index, String vodName) {
String vodPic = null;
if (StringUtils.isEmpty(vodName)) {
vodPic = "http://yanxuan.nosdn.127.net/b6fb987ce79f308949e44f5129a4b51c.jpeg";
} else if (singerMap.containsKey(vodName)) {
if (StringUtils.isNotEmpty(vodName)) {
if (singerMap.containsKey(vodName)) {
vodPic = singerMap.get(vodName);
} else if (vodName.contains(",")) {
String[] split = vodName.split(",", 2);
@@ -184,7 +205,11 @@ public class MvService {
vodPic = singerMap.get(split[0]);
}
}
vodPic = StringUtils.isNotEmpty(vodPic) ? vodPic : "http://yanxuan.nosdn.127.net/b6fb987ce79f308949e44f5129a4b51c.jpeg";
}
if (StringUtils.isEmpty(vodPic) && StringUtils.isNotEmpty(index)) {
vodPic = singerMap.get(index);
}
vodPic = StringUtils.defaultIfEmpty(vodPic, "http://yanxuan.nosdn.127.net/b6fb987ce79f308949e44f5129a4b51c.jpeg");
return vodPic;
}
@@ -200,13 +225,13 @@ public class MvService {
mvSearcher = new MvSearcher(indexAbsolutePath);
// 搜索一下,看看是否加载成功
search("五月天", 10);
search(MV_FILE_NAME, "五月天", 10);
}
private void buildHomeConfig() {
// 首页配置
try {
String content = ClassPathReaderUtils.getContent("tvbox/home.json");
String content = ClassPathReaderUtils.getContent("config/home.json");
Map<String, List<String>> map = new ObjectMapper().readValue(content, new TypeReference<LinkedHashMap<String, List<String>>>() {
});
homeConfigMap.putAll(map);
@@ -216,7 +241,7 @@ public class MvService {
}
// 歌手详情配置
try {
String content = ClassPathReaderUtils.getContent("tvbox/singerPic.json");
String content = ClassPathReaderUtils.getContent("config/singerPic.json");
Map<String, String> map = new ObjectMapper().readValue(content, new TypeReference<LinkedHashMap<String, String>>() {
});
singerMap.putAll(map);
@@ -228,14 +253,17 @@ public class MvService {
}
private static void reBuildIndex(String indexDirectoryPath) throws IOException {
Set<String> dataFilePath = getDataFilePath();
if (dataFilePath.isEmpty()) {
return;
}
File file = new File(indexDirectoryPath).getAbsoluteFile();
int mvFileSize = ClassPathReaderUtils.getSize(MV_FILE);
File mvFileSizeTxt = Path.of(file.getAbsolutePath(), "" + mvFileSize + ".txt").toFile();
long dataFileSize = ClassPathReaderUtils.getAllFileSize(dataFilePath);
File mvFileSizeTxt = Path.of(file.getAbsolutePath(), "" + dataFileSize + ".txt").toFile();
if (mvFileSizeTxt.exists()) {
logger.info("{}已经索引, 跳过索引", MV_FILE);
logger.info("所有数据都已经索引, 跳过索引");
return;
}
StopWatch stopWatch = new StopWatch();
logger.info("重建索引中....");
@@ -243,32 +271,57 @@ public class MvService {
// 清空以前的索引
stopWatch.start("清空以前索引");
logger.info("清空历史索引....");
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File file1 : files) {
if (file1.isFile()) {
file1.delete();
}
}
}
ClassPathReaderUtils.deleteAllFile(file);
logger.info("清空历史索引....完成");
stopWatch.stop();
// 创建索引
stopWatch.start("创建索引");
logger.info("创建索引中....");
AtomicInteger line = new AtomicInteger();
MvIndex mvIndex = new MvIndex(indexDirectoryPath);
ClassPathReaderUtils.getBufferedReader(MV_FILE).lines()
.filter(l -> l.contains(",h"))
.filter(l -> l.contains("-"))
for (String path : dataFilePath) {
buildIndex(mvIndex, path);
}
mvIndex.close();
mvFileSizeTxt.createNewFile();
stopWatch.stop();
logger.info("重建索引中....完成,耗时 {} 毫秒, 索引位置:{}", stopWatch.getTotalTimeMillis(), file.getPath());
}
private static Set<String> getDataFilePath() throws IOException {
Map<String, String> pathMap = new LinkedHashMap<>();
for (String filePath : FILE_PATH_LIST) {
if (filePath.startsWith(ClassPathReaderUtils.CLASS_PATH_PREFIX)) {
pathMap.put(filePath, new File(filePath).getName());
continue;
}
Map<String, String> directoryFiles = ClassPathReaderUtils.getDirectoryFiles(filePath);
if (directoryFiles.isEmpty()) {
continue;
}
pathMap.putAll(directoryFiles);
}
logger.info("↓↓↓↓↓ 搜集数据文件目录 ↓↓↓↓↓↓");
for (Map.Entry<String, String> entry : pathMap.entrySet()) {
logger.info("*** 数据文件:{}", entry.getValue());
}
logger.info("↑↑↑↑↑ 搜集数据文件目录 ↑↑↑↑↑↑");
return new LinkedHashSet<>(pathMap.keySet());
}
private static void buildIndex(MvIndex mvIndex, String path) {
String fileName = ClassPathReaderUtils.extractFileNameWithoutExtension(path);
AtomicInteger line = new AtomicInteger();
ClassPathReaderUtils.getBufferedReader(path).lines()
.filter(l -> l.contains(",http"))
.forEach(l -> {
String[] split = l.split(",", 2);
if (split.length != 2) {
List<String> split = StringUtils.reverseSplit(l, ",http", 2);
if (split.size() != 2) {
return;
}
String name = split[0].trim();
String url = split[1].trim();
String name = split.get(0).trim();
String url = "http" + split.get(1).trim();
String shortUrl = url;
if (shortUrl.startsWith("http://em.21dtv")) {
shortUrl = shortUrl
@@ -278,21 +331,19 @@ public class MvService {
try {
Document document = new Document();
document.add(new TextField("name", name, Field.Store.YES));
document.add(new StringField("index", fileName, Field.Store.YES));
document.add(new StringField("url", shortUrl, Field.Store.YES));
mvIndex.indexFile(document);
int i = line.incrementAndGet();
if (i % 10000 == 0) {
logger.info("创建索引中....已完成{}万条索引", i / 10000);
logger.info("创建{}索引中....已完成{}万条索引", fileName, i / 10000);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
mvIndex.close();
mvFileSizeTxt.createNewFile();
logger.info("创建索引中....完成, 总共{}条", line.get());
stopWatch.stop();
logger.info("重建索引中....完成,耗时 {} 毫秒, 索引位置:{}", stopWatch.getTotalTimeMillis(), file.getPath());
logger.info("创建索引中....完成, 源文件:{}, 总共{}条", new File(path).getName(), line.get());
}
@@ -2,11 +2,19 @@ package com.leosam.tvbox.mv.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* @author admin
@@ -14,7 +22,72 @@ import java.nio.charset.StandardCharsets;
*/
public class ClassPathReaderUtils {
public static int getSize(String path) {
public static final String CLASS_PATH_PREFIX = "classpath:";
public static Map<String, String> getDirectoryFiles(String filePath) throws IOException {
Map<String, String> pathMap = new LinkedHashMap<>();
if (StringUtils.isEmpty(filePath) || filePath.startsWith(CLASS_PATH_PREFIX)) {
return pathMap;
}
File data = new File(filePath);
if (data.exists() && data.isFile()) {
pathMap.put(data.getCanonicalPath(), data.getName());
}
if (data.exists() && data.isDirectory()) {
File[] files = data.listFiles();
for (File file : files) {
if (file.isFile()) {
pathMap.put(filePath + file.getName(), file.getName());
}
}
}
return pathMap;
}
public static String extractFileNameWithoutExtension(String filePath) {
filePath = filePath.replace(CLASS_PATH_PREFIX, "");
Path path = Paths.get(filePath);
String fileNameWithExtension = path.getFileName().toString();
int lastDotIndex = fileNameWithExtension.lastIndexOf(".");
String fileNameWithoutExtension = lastDotIndex == -1 ? fileNameWithExtension
: fileNameWithExtension.substring(0, lastDotIndex);
return fileNameWithoutExtension;
}
public static void deleteAllFile(File file) {
if (file == null) {
return;
}
if (file.isFile()) {
file.delete();
}
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File file1 : files) {
if (file1.isFile()) {
file1.delete();
}
}
}
}
public static long getAllFileSize(Set<String> dataPath) {
long size = 0;
for (String path : dataPath) {
if (path.startsWith(CLASS_PATH_PREFIX)) {
String pathName = path.replace(CLASS_PATH_PREFIX, "");
size += getClassPathFileSize(pathName);
} else {
File file = new File(path);
if (file.exists() && file.isFile()) {
size += file.length();
}
}
}
return size;
}
public static int getClassPathFileSize(String path) {
try {
ClassPathResource resource = new ClassPathResource(path);
InputStream inputStream = resource.getInputStream();
@@ -38,10 +111,14 @@ public class ClassPathReaderUtils {
public static BufferedReader getBufferedReader(String path) {
try {
InputStreamReader inputStreamReader = null;
if (path.startsWith(CLASS_PATH_PREFIX)) {
path = path.replace(CLASS_PATH_PREFIX, "");
ClassPathResource resource = new ClassPathResource(path);
InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream(),StandardCharsets.UTF_8);
inputStreamReader = new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8);
} else {
inputStreamReader = new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8);
}
BufferedReader reader = new BufferedReader(inputStreamReader);
return reader;
} catch (IOException ex) {
@@ -79,6 +156,9 @@ public class ClassPathReaderUtils {
private final ClassLoader classLoader;
public ClassPathResource(String path) {
if (path.startsWith(CLASS_PATH_PREFIX)) {
path = path.replace(CLASS_PATH_PREFIX, "");
}
this.path = path;
this.classLoader = getDefaultClassLoader();
}
@@ -96,8 +176,7 @@ public class ClassPathReaderUtils {
return is;
}
public static ClassLoader getDefaultClassLoader() {
private static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
@@ -12,4 +12,8 @@ public class CollectionUtils {
return (collection == null || collection.isEmpty());
}
public static boolean isNotEmpty(Collection<?> collection) {
return !isEmpty(collection);
}
}
@@ -1,11 +1,17 @@
package com.leosam.tvbox.mv.utils;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @author admin
* @since 2023/6/12 21:32
*/
public class StringUtils {
public static final String EMPTY = "";
public static boolean isNotEmpty(final CharSequence cs) {
return !isEmpty(cs);
}
@@ -13,4 +19,57 @@ public class StringUtils {
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
public static String defaultIfEmpty(final String str, String defaultStr) {
return isNotEmpty(str) ? str : defaultStr;
}
public static String trimToEmpty(final String str) {
return str == null ? EMPTY : str.trim();
}
public static List<String> reverseSplit(String s, String delimiter, int limit) {
List<String> result = new ArrayList<>();
if (s == null || delimiter == null || limit <= 0) {
return result;
}
int lastIndex = s.lastIndexOf(delimiter);
while (limit > 1 && lastIndex != -1) {
result.add(0, s.substring(lastIndex + delimiter.length()));
s = s.substring(0, lastIndex);
lastIndex = s.lastIndexOf(delimiter);
limit--;
}
result.add(0, s);
return result;
}
public static String cleanString(String s) {
// 去除乱码 以ISO8859-1方式读取UTF-8编码的中文
if (s.contains("ç") ||
s.contains("å") ||
s.contains("ğ") ||
s.contains("è") ||
s.contains("â") ||
s.contains("Š") ||
s.contains("ã") ||
s.contains("é") ||
s.contains("æ") ||
s.contains("§") ||
s.contains("ï")) {
s = new String(s.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
}
s = s
.replace('', ' ')
.replace('', ' ')
.replace('(', ' ')
.replace(')', ' ')
.replace('/', ' ')
.replace('[', ' ')
.replace(']', ' ')
.trim();
return s;
}
}
+2 -2
View File
@@ -5,8 +5,8 @@
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<property name="ENCODING" value="utf-8" />
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg %n"/>
<property name="PATTERN-COLOR" value="%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %green(%logger{36}) - %highlight(%msg) %n"/>
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS, GMT+8} [%thread] %-5level %logger{36} - %msg %n"/>
<property name="PATTERN-COLOR" value="%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS, GMT+8}) [%thread] %highlight(%-5level) %green(%logger{36}) - %highlight(%msg) %n"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+8
View File
@@ -8,6 +8,14 @@
"searchable": 1,
"quickSearch": 1,
"filterable": 1
}, {
"key": "MV_vod_2",
"name": "👀┃DOME┃视频",
"type": 1,
"api": "./vod/dome",
"searchable": 1,
"quickSearch": 1,
"filterable": 1
}
]
}