├── .DS_Store ├── image ├── weixin.jpg ├── zhifubao.jpg └── hongbaoma.jpg ├── src ├── main │ ├── resources │ │ ├── application.properties │ │ ├── application-dev.properties │ │ ├── application-prod.properties │ │ └── banner.txt │ └── java │ │ └── com │ │ └── suda │ │ └── MyVideoApi │ │ ├── domian │ │ ├── DataConverter.java │ │ ├── PageDTO.java │ │ ├── dto │ │ │ ├── VideoSourceDTO.java │ │ │ ├── SiteInfoDTO.java │ │ │ ├── VideoDTO.java │ │ │ └── VideoPlayDTO.java │ │ ├── BaseDomain.java │ │ ├── dos │ │ │ ├── VideoSeriesDO.java │ │ │ ├── VideoDO.java │ │ │ ├── VideoDetailDO.java │ │ │ └── VideoPlayDO.java │ │ ├── converter │ │ │ └── VideoConverter.java │ │ ├── ResultDTO.java │ │ └── BizException.java │ │ ├── MyVideoApiApplication.java │ │ ├── conf │ │ ├── ScheduleConfig.java │ │ ├── WebAppConfig.java │ │ ├── FastJsonRedisSerializer.java │ │ ├── RedisConfig.java │ │ └── InterceptorConfig.java │ │ ├── constant │ │ ├── BizErrorCodeConstants.java │ │ ├── API.java │ │ └── Source.java │ │ ├── job │ │ ├── HJJob.java │ │ ├── MJWJob.java │ │ ├── CartoonJob.java │ │ ├── LongzhuJob.java │ │ ├── DililiJob.java │ │ └── BaseJob.java │ │ ├── service │ │ ├── VideoService.java │ │ ├── VideoLZServiceImpl.java │ │ ├── VideoMjwServiceImpl.java │ │ ├── VideoDiliServiceImpl.java │ │ ├── VideoHJServiceImpl.java │ │ ├── VideoCartoonServiceImpl.java │ │ └── BaseVideoService.java │ │ ├── controller │ │ ├── MyExceptionHandler.java │ │ └── VideoController.java │ │ ├── util │ │ ├── SuplayerUtil.java │ │ └── JsoupUtils.java │ │ └── bot │ │ └── Telegram.java └── test │ └── java │ └── com │ └── suda │ └── MyVideoApi │ └── MyVedioApiApplicationTests.java ├── .gitignore ├── pom.xml ├── README.md ├── mvnw.cmd └── mvnw /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudaVideo/MyVideoApi/HEAD/.DS_Store -------------------------------------------------------------------------------- /image/weixin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudaVideo/MyVideoApi/HEAD/image/weixin.jpg -------------------------------------------------------------------------------- /image/zhifubao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudaVideo/MyVideoApi/HEAD/image/zhifubao.jpg -------------------------------------------------------------------------------- /image/hongbaoma.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudaVideo/MyVideoApi/HEAD/image/hongbaoma.jpg -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.profiles.active=dev 2 | logging.file=log/server.log 3 | spring.jackson.default-property-inclusion=non_null 4 | ##站点名称 5 | spring.siteName= 6 | ##联系邮件 7 | spring.contactEmail= 8 | ##allowOrigin 9 | spring.allowOrigin=* 10 | -------------------------------------------------------------------------------- /src/main/resources/application-dev.properties: -------------------------------------------------------------------------------- 1 | # REDIS 2 | # Redis数据库索引(默认为0) 3 | spring.redis.database=0 4 | # Redis服务器地址 (默认为127.0.0.1) 5 | spring.redis.host=127.0.0.1 6 | # Redis服务器连接端口 (默认为6379) 7 | spring.redis.port=6379 8 | # Redis服务器连接密码(默认为空) 9 | spring.redis.password=123456 10 | # 连接超时时间(毫秒) 11 | spring.redis.timeout=2000 -------------------------------------------------------------------------------- /src/main/resources/application-prod.properties: -------------------------------------------------------------------------------- 1 | # REDIS 2 | # Redis数据库索引(默认为0) 3 | spring.redis.database=0 4 | # Redis服务器地址 (默认为127.0.0.1) 5 | spring.redis.host=127.0.0.1 6 | # Redis服务器连接端口 (默认为6379) 7 | spring.redis.port=6379 8 | # Redis服务器连接密码(默认为空) 9 | spring.redis.password=123456 10 | # 连接超时时间(毫秒) 11 | spring.redis.timeout=2000 -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/DataConverter.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian; 2 | 3 | /** 4 | * 转换接口 5 | * 6 | * @author guhaibo 7 | * @date 2018/7/31 8 | */ 9 | public interface DataConverter { 10 | 11 | /** 12 | * 转换 13 | * 14 | * @param from 15 | * @param to 16 | */ 17 | void convert(T from, R to); 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | /nbproject/private/ 21 | /build/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/PageDTO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author guhaibo 10 | * @date 2018/8/3 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | public class PageDTO { 15 | private int pageSize; 16 | private List data; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dto/VideoSourceDTO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dto; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | /** 7 | * @author guhaibo 8 | * @date 2018/8/5 9 | */ 10 | @Data 11 | @AllArgsConstructor 12 | public class VideoSourceDTO { 13 | private int source; 14 | private String type; 15 | private String name; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/BaseDomain.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author guhaibo 9 | * @date 2018/7/22 10 | */ 11 | public class BaseDomain implements Serializable { 12 | 13 | @Override 14 | public String toString() { 15 | return JSONObject.toJSONString(this); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dos/VideoSeriesDO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dos; 2 | 3 | import com.suda.MyVideoApi.domian.BaseDomain; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author guhaibo 9 | * @date 2018/7/29 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | public class VideoSeriesDO extends BaseDomain { 14 | private String seriesId; 15 | private String name; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dto/SiteInfoDTO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dto; 2 | 3 | import com.suda.MyVideoApi.domian.BaseDomain; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author guhaibo 9 | * @date 2018/8/11 10 | */ 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class SiteInfoDTO extends BaseDomain { 15 | private String siteName; 16 | private String contactEmail; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/suda/MyVideoApi/MyVedioApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class MyVedioApiApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dos/VideoDO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dos; 2 | 3 | import com.suda.MyVideoApi.domian.BaseDomain; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author guhaibo 9 | * @date 2018/7/29 10 | */ 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class VideoDO extends BaseDomain { 15 | private String title; 16 | private String thumb; 17 | private String originUrl; 18 | private int source; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dto/VideoDTO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dto; 2 | 3 | import com.suda.MyVideoApi.domian.BaseDomain; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | /** 8 | * @author guhaibo 9 | * @date 2018/7/29 10 | */ 11 | 12 | @Data 13 | @NoArgsConstructor 14 | public class VideoDTO extends BaseDomain { 15 | private String title; 16 | private String thumb; 17 | private String videoId; 18 | private int source; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/MyVideoApiApplication.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.scheduling.annotation.EnableScheduling; 6 | 7 | @EnableScheduling 8 | @SpringBootApplication 9 | public class MyVideoApiApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(MyVideoApiApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dos/VideoDetailDO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dos; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author guhaibo 10 | * @date 2018/7/29 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | public class VideoDetailDO { 15 | /** 16 | * 剧集 17 | */ 18 | private List videoSeries; 19 | /** 20 | * 描述 21 | */ 22 | private String desc; 23 | /** 24 | * 预览图 25 | */ 26 | private List previewImgs; 27 | 28 | /** 29 | * 标题 30 | */ 31 | private String title; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dos/VideoPlayDO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dos; 2 | 3 | import com.suda.MyVideoApi.domian.BaseDomain; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | public class VideoPlayDO extends BaseDomain { 10 | private String playUrl; 11 | private String type; 12 | 13 | public String getPlayUrl() { 14 | return playUrl; 15 | } 16 | 17 | public void setPlayUrl(String playUrl) { 18 | this.playUrl = playUrl; 19 | } 20 | 21 | public String getType() { 22 | return type; 23 | } 24 | 25 | public void setType(String type) { 26 | this.type = type; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/conf/ScheduleConfig.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.conf; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.scheduling.annotation.SchedulingConfigurer; 5 | import org.springframework.scheduling.config.ScheduledTaskRegistrar; 6 | 7 | import java.util.concurrent.Executors; 8 | 9 | 10 | /** 11 | * @author guhaibo 12 | * @date 2018/7/29 13 | */ 14 | @Configuration 15 | public class ScheduleConfig implements SchedulingConfigurer { 16 | @Override 17 | public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { 18 | //设定一个长度10的定时任务线程池 19 | taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10)); 20 | } 21 | } -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/dto/VideoPlayDTO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.dto; 2 | 3 | import com.suda.MyVideoApi.domian.BaseDomain; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | public class VideoPlayDTO extends BaseDomain { 10 | private String playUrl; 11 | private String type; 12 | 13 | public String getPlayUrl() { 14 | return playUrl; 15 | } 16 | 17 | public void setPlayUrl(String playUrl) { 18 | this.playUrl = playUrl; 19 | } 20 | 21 | public String getType() { 22 | return type; 23 | } 24 | 25 | public void setType(String type) { 26 | this.type = type; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BLACK} 2 | _ooOoo_ 3 | o8888888o 4 | 88" . "88 5 | (| -_- |) 6 | O\ = /O 7 | ____/`---'\____ 8 | .' \\| |// `. 9 | / \\||| : |||// \ 10 | / _||||| -:- |||||- \ 11 | | | \\\ - /// | | 12 | | \_| ''\---/'' | | 13 | \ .-\__ `-` ___/-. / 14 | ___`. .' /--.--\ `. . __ 15 | ."" '< `.___\_<|>_/___.' >'"". 16 | | | : `- \`.;`\ _ /`;.`/ - ` : | | 17 | \ \ `-. \_ __\ /__ _/ .-` / / 18 | ======`-.____`-.___\_____/___.-`____.-'====== 19 | `=---=' 20 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 21 | 佛祖保佑 永无BUG -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/conf/WebAppConfig.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.conf; 2 | 3 | import lombok.AllArgsConstructor; 4 | import org.springframework.boot.SpringBootConfiguration; 5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 7 | 8 | /** 9 | * @author guhaibo 10 | * @date 2018/8/10 11 | */ 12 | @AllArgsConstructor 13 | @SpringBootConfiguration 14 | public class WebAppConfig extends WebMvcConfigurerAdapter { 15 | 16 | 17 | private InterceptorConfig interceptorConfig; 18 | 19 | @Override 20 | public void addInterceptors(InterceptorRegistry registry) { 21 | //注册自定义拦截器,添加拦截路径和排除拦截路径 22 | registry.addInterceptor(interceptorConfig).addPathPatterns("/video/**"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/constant/BizErrorCodeConstants.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.constant; 2 | 3 | /** 4 | * 业务异常错误码 5 | * 6 | * @author guhaibo 7 | * @date 2018/7/22 8 | */ 9 | public enum BizErrorCodeConstants { 10 | 11 | /** 12 | * 业务异常 13 | */ 14 | S0000("S0000", "服务器业务异常"), 15 | S0001("S0001", "数据库异常"), 16 | S0002("S0002", "解析失败"), 17 | S0003("S0003", "安全校验失败"), 18 | S0004("S0004", "参数非法"); 19 | 20 | private String code; 21 | 22 | private String codeMSG; 23 | 24 | BizErrorCodeConstants(String code, String codeMSG) { 25 | this.code = code; 26 | this.codeMSG = codeMSG; 27 | } 28 | 29 | public String getCode() { 30 | return code; 31 | } 32 | 33 | public String getCodeMSG() { 34 | return codeMSG; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/job/HJJob.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.job; 2 | 3 | import com.suda.MyVideoApi.service.VideoHJServiceImpl; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | import org.springframework.scheduling.annotation.Scheduled; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author guhaibo 11 | * @date 2018/7/29 12 | */ 13 | @Component 14 | @Log4j2 15 | public class HJJob extends BaseJob { 16 | 17 | public HJJob(VideoHJServiceImpl videoService, RedisTemplate redisTemplate) { 18 | super(videoService, redisTemplate); 19 | } 20 | 21 | /** 22 | * 每隔1小时,爬韩剧 23 | */ 24 | @Scheduled(fixedRate = 60 * 60 * 1000) 25 | public void startFilm() { 26 | getVideos("hanju"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/job/MJWJob.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.job; 2 | 3 | import com.suda.MyVideoApi.service.VideoMjwServiceImpl; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | import org.springframework.scheduling.annotation.Scheduled; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author guhaibo 11 | * @date 2018/7/29 12 | */ 13 | @Component 14 | @Log4j2 15 | public class MJWJob extends BaseJob { 16 | 17 | public MJWJob(VideoMjwServiceImpl videoService, RedisTemplate redisTemplate) { 18 | super(videoService, redisTemplate); 19 | } 20 | 21 | /** 22 | * 每隔1小时,爬美剧 23 | */ 24 | @Scheduled(fixedRate = 60 * 60 * 1000) 25 | public void startFilm() { 26 | getVideos("all_mj"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/converter/VideoConverter.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian.converter; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.DataConverter; 5 | import com.suda.MyVideoApi.domian.dos.VideoDO; 6 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 7 | 8 | /** 9 | * @author guhaibo 10 | * @date 2018/7/31 11 | */ 12 | public class VideoConverter implements DataConverter { 13 | 14 | public static final String PLAY_SPLITE = "+"; 15 | 16 | @Override 17 | public void convert(VideoDO from, VideoDTO to) { 18 | to.setTitle(from.getTitle()); 19 | to.setThumb(from.getThumb()); 20 | to.setSource(from.getSource()); 21 | API api = API.getApiBySourceId(from.getSource()); 22 | to.setVideoId(from.getOriginUrl().replace(api.resUrl, "").replace("/", PLAY_SPLITE)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/job/CartoonJob.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.job; 2 | 3 | import com.suda.MyVideoApi.service.VideoCartoonServiceImpl; 4 | import com.suda.MyVideoApi.service.VideoDiliServiceImpl; 5 | import lombok.extern.log4j.Log4j2; 6 | import org.springframework.data.redis.core.RedisTemplate; 7 | import org.springframework.scheduling.annotation.Scheduled; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * @author guhaibo 12 | * @date 2018/7/29 13 | */ 14 | @Component 15 | @Log4j2 16 | public class CartoonJob extends BaseJob { 17 | 18 | public CartoonJob(VideoCartoonServiceImpl videoService, RedisTemplate redisTemplate) { 19 | super(videoService, redisTemplate); 20 | } 21 | /** 22 | * 每隔1小时,爬动漫 23 | */ 24 | @Scheduled(fixedRate = 60 * 60 * 1000) 25 | public void startDocumentaryFilm() { 26 | getVideos("all"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/ResultDTO.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian; 2 | 3 | /** 4 | * 返回结果 5 | * 6 | * @author guhaibo 7 | * @date 2018/7/22 8 | */ 9 | public class ResultDTO { 10 | private T data; 11 | private Boolean success; 12 | protected String errorCode; 13 | protected String errorMSG; 14 | 15 | public T getData() { 16 | return data; 17 | } 18 | 19 | public void setData(T data) { 20 | this.data = data; 21 | } 22 | 23 | public Boolean getSuccess() { 24 | return success; 25 | } 26 | 27 | public void setSuccess(Boolean success) { 28 | this.success = success; 29 | } 30 | 31 | public String getErrorCode() { 32 | return errorCode; 33 | } 34 | 35 | public void setErrorCode(String errorCode) { 36 | this.errorCode = errorCode; 37 | } 38 | 39 | public String getErrorMSG() { 40 | return errorMSG; 41 | } 42 | 43 | public void setErrorMSG(String errorMSG) { 44 | this.errorMSG = errorMSG; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "ResultDTO{" + 50 | "data=" + data + 51 | ", success=" + success + 52 | ", errorCode='" + errorCode + '\'' + 53 | ", errorMSG='" + errorMSG + '\'' + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/conf/FastJsonRedisSerializer.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.conf; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.serializer.SerializerFeature; 5 | import org.springframework.data.redis.serializer.RedisSerializer; 6 | import org.springframework.data.redis.serializer.SerializationException; 7 | 8 | import java.nio.charset.Charset; 9 | 10 | /** 11 | * @author guhaibo 12 | * @date 2018/7/22 13 | */ 14 | public class FastJsonRedisSerializer implements RedisSerializer { 15 | 16 | public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 17 | 18 | private Class clazz; 19 | 20 | public FastJsonRedisSerializer(Class clazz) { 21 | super(); 22 | this.clazz = clazz; 23 | } 24 | 25 | @Override 26 | public byte[] serialize(T t) throws SerializationException { 27 | if (null == t) { 28 | return new byte[0]; 29 | } 30 | return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); 31 | } 32 | 33 | @Override 34 | public T deserialize(byte[] bytes) throws SerializationException { 35 | if (null == bytes || bytes.length <= 0) { 36 | return null; 37 | } 38 | String str = new String(bytes, DEFAULT_CHARSET); 39 | return (T) JSON.parseObject(str, clazz); 40 | } 41 | 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/constant/API.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.constant; 2 | 3 | /** 4 | * @author guhaibo 5 | * @date 2018/7/29 6 | */ 7 | public enum API { 8 | 9 | 10 | 11 | DILI(0, "http://www.dlili.tv/", "http://www.dlili.tv/gresource/", "http://www.dlili.tv/gresource/%s?Play=%s"), 12 | MJW(1, "https://91mjw.com/category/", "https://91mjw.com/video/", "https://91mjw.com/video/%s?Play=%s"), 13 | HJ(2, "https://www.hmtv.me/", "https://www.hmtv.me/show/", "https://www.hmtv.me/show/%s?Play-%s"), 14 | LZ(3, "http://www.lzvod.net/", "http://www.lzvod.net/", "http://www.lzvod.net/%s?Play=%s"), 15 | CARTOON(4, "http://www.domanwo.com/category/", "http://www.domanwo.com/", "http://www.domanwo.com/%s?Play-%s.html"), 16 | 17 | ; 18 | 19 | 20 | public static API getApiBySourceId(int sourceId) { 21 | for (API api : API.values()) { 22 | if (api.sourceId == sourceId) { 23 | return api; 24 | } 25 | } 26 | return DILI; 27 | } 28 | 29 | public String baseKey; 30 | public int sourceId; 31 | public String baseUrl; 32 | public String resUrl; 33 | public String resSeriresUrl; 34 | 35 | API(int sourceId, String baseUrl, String resUrl, String resSeriresUrl) { 36 | this.sourceId = sourceId; 37 | this.baseUrl = baseUrl; 38 | this.resUrl = resUrl; 39 | this.resSeriresUrl = resSeriresUrl; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/constant/Source.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.constant; 2 | 3 | import com.suda.MyVideoApi.domian.dto.VideoSourceDTO; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * @author guhaibo 10 | * @date 2018/8/5 11 | */ 12 | public class Source { 13 | public static List videoSourceDTOS = new ArrayList<>(); 14 | 15 | static { 16 | videoSourceDTOS.add(new VideoSourceDTO(API.DILI.sourceId, "film", "电影1")); 17 | videoSourceDTOS.add(new VideoSourceDTO(API.LZ.sourceId, "movies", "电影2")); 18 | 19 | videoSourceDTOS.add(new VideoSourceDTO(API.DILI.sourceId, "teleplay", "剧集1")); 20 | videoSourceDTOS.add(new VideoSourceDTO(API.LZ.sourceId, "teleplay", "剧集2")); 21 | 22 | videoSourceDTOS.add(new VideoSourceDTO(API.DILI.sourceId, "cartoon", "动漫1")); 23 | videoSourceDTOS.add(new VideoSourceDTO(API.LZ.sourceId, "cartoon", "动漫2")); 24 | videoSourceDTOS.add(new VideoSourceDTO(API.CARTOON.sourceId, "all", "动漫3")); 25 | 26 | videoSourceDTOS.add(new VideoSourceDTO(API.DILI.sourceId, "variety_show", "综艺1")); 27 | videoSourceDTOS.add(new VideoSourceDTO(API.LZ.sourceId, "variety", "综艺2")); 28 | 29 | videoSourceDTOS.add(new VideoSourceDTO(API.DILI.sourceId, "list11", "纪录1")); 30 | videoSourceDTOS.add(new VideoSourceDTO(API.LZ.sourceId, "fact", "纪录2")); 31 | 32 | videoSourceDTOS.add(new VideoSourceDTO(API.MJW.sourceId, "all_mj", "美剧")); 33 | videoSourceDTOS.add(new VideoSourceDTO(API.HJ.sourceId, "hanju", "韩剧")); 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/job/LongzhuJob.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.job; 2 | 3 | import com.suda.MyVideoApi.service.VideoLZServiceImpl; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | import org.springframework.scheduling.annotation.Scheduled; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author guhaibo 11 | * @date 2018/7/29 12 | */ 13 | @Component 14 | @Log4j2 15 | public class LongzhuJob extends BaseJob { 16 | public LongzhuJob(VideoLZServiceImpl videoService, RedisTemplate redisTemplate) { 17 | super(videoService, redisTemplate); 18 | } 19 | 20 | /** 21 | * 每隔1小时,爬lz电影 22 | */ 23 | @Scheduled(fixedRate = 60 * 60 * 1000) 24 | public void startFilm() { 25 | getVideos("movies"); 26 | } 27 | 28 | /** 29 | * 每隔1小时,爬lz剧集 30 | */ 31 | @Scheduled(fixedRate = 60 * 60 * 1000) 32 | public void startTeleplay() { 33 | getVideos("teleplay"); 34 | } 35 | 36 | /** 37 | * 每隔1小时,爬lz动漫 38 | */ 39 | @Scheduled(fixedRate = 60 * 60 * 1000) 40 | public void startCartoon() { 41 | getVideos("cartoon"); 42 | } 43 | 44 | /** 45 | * 每隔1小时,爬lz综艺 46 | */ 47 | @Scheduled(fixedRate = 60 * 60 * 1000) 48 | public void startVarietyShow() { 49 | getVideos("variety"); 50 | } 51 | 52 | /** 53 | * 每隔1小时,爬lz纪录片 54 | */ 55 | @Scheduled(fixedRate = 60 * 60 * 1000) 56 | public void startDocumentaryFilm() { 57 | getVideos("fact"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/job/DililiJob.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.job; 2 | 3 | import com.suda.MyVideoApi.service.VideoDiliServiceImpl; 4 | import lombok.extern.log4j.Log4j2; 5 | import org.springframework.data.redis.core.RedisTemplate; 6 | import org.springframework.scheduling.annotation.Scheduled; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author guhaibo 11 | * @date 2018/7/29 12 | */ 13 | @Component 14 | @Log4j2 15 | public class DililiJob extends BaseJob { 16 | 17 | public DililiJob(VideoDiliServiceImpl videoService, RedisTemplate redisTemplate) { 18 | super(videoService, redisTemplate); 19 | } 20 | 21 | /** 22 | * 每隔1小时,爬dilili电影 23 | */ 24 | @Scheduled(fixedRate = 60 * 60 * 1000) 25 | public void startFilm() { 26 | getVideos("film"); 27 | } 28 | 29 | /** 30 | * 每隔1小时,爬dilili剧集 31 | */ 32 | @Scheduled(fixedRate = 60 * 60 * 1000) 33 | public void startTeleplay() { 34 | getVideos("teleplay"); 35 | } 36 | 37 | /** 38 | * 每隔1小时,爬dilili动漫 39 | */ 40 | @Scheduled(fixedRate = 60 * 60 * 1000) 41 | public void startCartoon() { 42 | getVideos("cartoon"); 43 | } 44 | 45 | /** 46 | * 每隔1小时,爬dilili综艺 47 | */ 48 | @Scheduled(fixedRate = 60 * 60 * 1000) 49 | public void startVarietyShow() { 50 | getVideos("variety_show"); 51 | } 52 | 53 | /** 54 | * 每隔1小时,爬dilili纪录片 55 | */ 56 | @Scheduled(fixedRate = 60 * 60 * 1000) 57 | public void startDocumentaryFilm() { 58 | getVideos("list11"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/domian/BizException.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.domian; 2 | 3 | 4 | import com.suda.MyVideoApi.constant.BizErrorCodeConstants; 5 | 6 | /** 7 | * @author guhaibo 8 | * @date 2018/7/22 9 | */ 10 | 11 | public class BizException extends RuntimeException { 12 | 13 | public BizException(BizErrorCodeConstants error, Throwable ex) { 14 | super(error.getCodeMSG(), ex); 15 | this.errorCode = error.getCode(); 16 | this.errorMSG = error.getCodeMSG(); 17 | this.ex = ex; 18 | } 19 | 20 | public BizException(BizErrorCodeConstants error, String errorWhere, Throwable ex) { 21 | super(error.getCodeMSG() + " " + errorWhere, ex); 22 | this.errorCode = error.getCode(); 23 | this.errorMSG = error.getCodeMSG() + " " + errorWhere; 24 | this.ex = ex; 25 | } 26 | 27 | public BizException(Throwable ex) { 28 | super(ex); 29 | this.ex = ex; 30 | } 31 | 32 | protected String errorCode; 33 | protected String errorMSG; 34 | 35 | protected Throwable ex; 36 | 37 | 38 | public String getErrorCode() { 39 | return errorCode; 40 | } 41 | 42 | public void setErrorCode(String errorCode) { 43 | this.errorCode = errorCode; 44 | } 45 | 46 | public String getErrorMSG() { 47 | return errorMSG; 48 | } 49 | 50 | public void setErrorMSG(String errorMSG) { 51 | this.errorMSG = errorMSG; 52 | } 53 | 54 | public Throwable getEx() { 55 | return ex; 56 | } 57 | 58 | public void setEx(Throwable ex) { 59 | this.ex = ex; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/VideoService.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | 4 | import com.suda.MyVideoApi.domian.BizException; 5 | import com.suda.MyVideoApi.domian.PageDTO; 6 | import com.suda.MyVideoApi.domian.dos.VideoDetailDO; 7 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 8 | import com.suda.MyVideoApi.domian.dto.VideoPlayDTO; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author guhaibo 14 | * @date 2018/7/29 15 | */ 16 | public interface VideoService { 17 | 18 | /** 19 | * 根据类别查询电影 20 | * 21 | * @param tag 22 | * @param pageIndex 23 | * @param useCache 24 | * @return 25 | * @throws BizException 26 | */ 27 | PageDTO queryVideosByTypeNew(String tag, int pageIndex, boolean useCache) throws BizException; 28 | 29 | /** 30 | * 根据类别查询电影 31 | * 32 | * @param tag 33 | * @param pageIndex 34 | * @param useCache 35 | * @return 36 | * @throws BizException 37 | */ 38 | List queryVideosByType(String tag, int pageIndex, boolean useCache) throws BizException; 39 | 40 | /** 41 | * 查询影视详情 42 | * 43 | * @param videoId 44 | * @param useCache 45 | * @return 46 | * @throws BizException 47 | */ 48 | VideoDetailDO queryVideosDetail(String videoId, boolean useCache) throws BizException; 49 | 50 | /** 51 | * 查询播放地址 52 | * 53 | * @param videoId 54 | * @param seriesId 55 | * @return 56 | * @throws BizException 57 | */ 58 | VideoPlayDTO queryPlayUrl(String videoId, String seriesId) throws BizException; 59 | 60 | /** 61 | * 根据关键字模糊匹配 62 | * 63 | * @param search 64 | * @return 65 | */ 66 | List queryVideoByKey(String search); 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/conf/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.conf; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.cache.CacheManager; 7 | import org.springframework.cache.annotation.CachingConfigurerSupport; 8 | import org.springframework.cache.annotation.EnableCaching; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.data.redis.cache.RedisCacheManager; 12 | import org.springframework.data.redis.connection.RedisConnectionFactory; 13 | import org.springframework.data.redis.core.RedisTemplate; 14 | import org.springframework.data.redis.core.StringRedisTemplate; 15 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 16 | import org.springframework.data.redis.serializer.StringRedisSerializer; 17 | 18 | /** 19 | * @author guhaibo 20 | * @date 2018/7/29 21 | */ 22 | @Configuration 23 | @EnableCaching 24 | public class RedisConfig extends CachingConfigurerSupport { 25 | 26 | @Bean 27 | public CacheManager cacheManager(RedisTemplate redisTemplate) { 28 | RedisCacheManager rcm = new RedisCacheManager(redisTemplate); 29 | //设置缓存过期时间 30 | // rcm.setDefaultExpiration(60);//秒 31 | return rcm; 32 | } 33 | /** 34 | * RedisTemplate配置 35 | * @param factory 36 | * @return 37 | */ 38 | @Bean 39 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) { 40 | StringRedisTemplate template = new StringRedisTemplate(factory); 41 | FastJsonRedisSerializer fastJsonRedisSerializer = 42 | new FastJsonRedisSerializer(Object.class); 43 | // value值的序列化采用fastJsonRedisSerializer 44 | template.setValueSerializer(fastJsonRedisSerializer); 45 | template.setHashValueSerializer(fastJsonRedisSerializer); 46 | 47 | // key的序列化采用StringRedisSerializer 48 | template.setKeySerializer(new StringRedisSerializer()); 49 | template.setHashKeySerializer(new StringRedisSerializer()); 50 | template.setConnectionFactory(factory); 51 | return template; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/job/BaseJob.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.job; 2 | 3 | import com.suda.MyVideoApi.domian.dos.VideoDetailDO; 4 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 5 | import com.suda.MyVideoApi.service.BaseVideoService; 6 | import com.suda.MyVideoApi.service.VideoService; 7 | import lombok.AllArgsConstructor; 8 | import org.springframework.data.redis.core.RedisTemplate; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author guhaibo 14 | * @date 2018/8/5 15 | */ 16 | @AllArgsConstructor 17 | public class BaseJob { 18 | 19 | private T videoService; 20 | private RedisTemplate redisTemplate; 21 | 22 | protected void getVideos(String tag) { 23 | List videoDTOS; 24 | int page = 1; 25 | while ((videoDTOS = videoService.queryVideosByType(tag, page, false)).size() > 0) { 26 | if (videoDTOS.size() > 0) { 27 | //跟新页数 28 | String pageKey = videoService.getApi().name() + ":" + "VideoTypeSize:" + tag; 29 | Integer allPage = (Integer) redisTemplate.opsForValue().get(pageKey); 30 | if (allPage == null) { 31 | allPage = 0; 32 | } 33 | 34 | if (page > allPage) { 35 | redisTemplate.opsForValue().set(pageKey, page); 36 | } 37 | } 38 | 39 | for (VideoDTO videoDTO : videoDTOS) { 40 | System.out.println(videoDTO.toString()); 41 | try { 42 | VideoDetailDO videoDetailDO = videoService.queryVideosDetail(videoDTO.getVideoId(), false); 43 | // //缓存播放地址 44 | // List videoSeriesDOS = videoDetailDO.getVideoSeries(); 45 | // if (videoSeriesDOS != null) { 46 | // for (VideoSeriesDO videoSeriesDO : videoSeriesDOS) { 47 | // try { 48 | // videoService.queryPlayUrl(videoDTO.getVideoId(), videoSeriesDO.getSeriesId()); 49 | // } catch (Exception e) { 50 | // 51 | // } 52 | // } 53 | // } 54 | } catch (Exception e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | page++; 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/VideoLZServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 5 | import com.suda.MyVideoApi.util.JsoupUtils; 6 | import com.suda.MyVideoApi.util.SuplayerUtil; 7 | import org.jsoup.nodes.Document; 8 | import org.jsoup.nodes.Element; 9 | import org.jsoup.select.Elements; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.regex.Matcher; 16 | import java.util.regex.Pattern; 17 | 18 | /** 19 | * @author guhaibo 20 | * @date 2018/7/29 21 | */ 22 | @Service("videoLZServiceImpl") 23 | public class VideoLZServiceImpl extends BaseVideoService { 24 | 25 | public VideoLZServiceImpl(RedisTemplate redisTemplate) { 26 | super(redisTemplate); 27 | } 28 | 29 | @Override 30 | public API getApi() { 31 | return API.LZ; 32 | } 33 | 34 | @Override 35 | protected VideoPlayDO parsePlayUrl(String refererUrl) { 36 | Document pcDocument = JsoupUtils.getDocWithPC(refererUrl); 37 | Elements scripts = pcDocument.body().getElementsByTag("script"); 38 | List params = new ArrayList<>(); 39 | for (Element script : scripts) { 40 | if (script.toString().indexOf("var") > 0) { 41 | String scriptText = script.data(); 42 | Pattern pattern = Pattern.compile("\"(.*?)\""); 43 | Matcher m = pattern.matcher(scriptText); 44 | params.clear(); 45 | while (m.find()) { 46 | params.add(m.group().replace("\"", "")); 47 | } 48 | if (params.size() >= 2) { 49 | break; 50 | } 51 | } 52 | } 53 | 54 | if (params.get(1).contains("http")){ 55 | VideoPlayDO videoPlayDO = new VideoPlayDO(); 56 | videoPlayDO.setPlayUrl(params.get(1)); 57 | videoPlayDO.setType("dplayer"); 58 | return videoPlayDO; 59 | } 60 | 61 | 62 | String playerUrl = "http://www.lzvod.net/player/?type=" + params.get(0) + "&vkey=" + params.get(1); 63 | return SuplayerUtil.getPlayUrl(refererUrl, playerUrl, "http://www.lzvod.net/player/1suplayer.php"); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/conf/InterceptorConfig.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.conf; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.util.StringUtils; 8 | import org.springframework.web.servlet.HandlerInterceptor; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | 14 | 15 | /** 16 | * @author guhaibo 17 | * @date 2018/8/10 18 | */ 19 | @Component 20 | public class InterceptorConfig implements HandlerInterceptor { 21 | 22 | @Value("${spring.allowOrigin}") 23 | private String allowOrigin; 24 | 25 | private Logger logger = LoggerFactory.getLogger(this.getClass()); 26 | 27 | @Override 28 | public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { 29 | String origin = httpServletRequest.getHeader("Origin"); 30 | 31 | if (!StringUtils.isEmpty(allowOrigin)) { 32 | if ("*".equals(allowOrigin)) { 33 | httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); 34 | } else { 35 | for (String allow : allowOrigin.split(",")) { 36 | if (allow.equals(origin)) { 37 | httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); 38 | break; 39 | } 40 | } 41 | } 42 | } 43 | 44 | httpServletResponse.setHeader("Access-Control-Allow-Methods", "*"); 45 | httpServletResponse.setHeader("Access-Control-Allow-Headers", "*"); 46 | 47 | //logger.info("---------------------开始进入请求地址拦截----------------------------"); 48 | return true; 49 | } 50 | 51 | @Override 52 | public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { 53 | //logger.info("---------------------处理请求完成后视图渲染之前的处理操作---------------"); 54 | } 55 | 56 | @Override 57 | public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { 58 | //logger.info("---------------------视图渲染之后的操作-------------------------------"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/VideoMjwServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 5 | import com.suda.MyVideoApi.util.JsoupUtils; 6 | import com.suda.MyVideoApi.util.SuplayerUtil; 7 | import org.jsoup.nodes.Document; 8 | import org.jsoup.nodes.Element; 9 | import org.jsoup.select.Elements; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.regex.Matcher; 16 | import java.util.regex.Pattern; 17 | 18 | /** 19 | * @author guhaibo 20 | * @date 2018/7/29 21 | */ 22 | @Service("videoMjwServiceImpl") 23 | public class VideoMjwServiceImpl extends BaseVideoService { 24 | public VideoMjwServiceImpl(RedisTemplate redisTemplate) { 25 | super(redisTemplate); 26 | } 27 | 28 | @Override 29 | public API getApi() { 30 | return API.MJW; 31 | } 32 | 33 | @Override 34 | protected VideoPlayDO parsePlayUrl(String refererUrl) { 35 | Document pcDocument = JsoupUtils.getDocWithPC(refererUrl); 36 | Elements scripts = pcDocument.body().getElementsByTag("script"); 37 | List params = new ArrayList<>(); 38 | for (Element script : scripts) { 39 | if (script.toString().indexOf("var") > 0) { 40 | String scriptText = script.data(); 41 | Pattern pattern = Pattern.compile("\"(.*?)\""); 42 | Matcher m = pattern.matcher(scriptText); 43 | params.clear(); 44 | while (m.find()) { 45 | params.add(m.group().replace("\"", "")); 46 | } 47 | if (params.size() >= 2) { 48 | break; 49 | } 50 | } 51 | } 52 | 53 | if (params.get(1).contains("http")){ 54 | VideoPlayDO videoPlayDO = new VideoPlayDO(); 55 | videoPlayDO.setPlayUrl(params.get(1)); 56 | videoPlayDO.setType("dplayer"); 57 | return videoPlayDO; 58 | } 59 | 60 | String playerUrl = "https://api.1suplayer.me/player/?userID=&type=" 61 | + params.get(0) + "&vkey=" + params.get(1); 62 | return SuplayerUtil.getPlayUrl(refererUrl, playerUrl, "https://api.1suplayer.me/player/api.php"); 63 | } 64 | 65 | 66 | @Override 67 | protected int maxVideo() { 68 | return 42; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/controller/MyExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.controller; 2 | 3 | import com.suda.MyVideoApi.constant.BizErrorCodeConstants; 4 | import com.suda.MyVideoApi.domian.BizException; 5 | import com.suda.MyVideoApi.domian.ResultDTO; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.web.bind.MissingServletRequestParameterException; 10 | import org.springframework.web.bind.annotation.ControllerAdvice; 11 | import org.springframework.web.bind.annotation.ExceptionHandler; 12 | import org.springframework.web.bind.annotation.ResponseBody; 13 | 14 | import java.io.PrintWriter; 15 | import java.io.StringWriter; 16 | import java.io.Writer; 17 | 18 | /** 19 | * 异常处理类 20 | * 21 | * @author guhaibo 22 | * @date 2018/7/22 23 | */ 24 | @ControllerAdvice 25 | public class MyExceptionHandler { 26 | 27 | private Logger logger = LoggerFactory.getLogger(this.getClass()); 28 | 29 | @Value("${spring.profiles.active}") 30 | private String env; 31 | 32 | @ExceptionHandler(value = BizException.class) 33 | public 34 | @ResponseBody 35 | ResultDTO MyExceptionHandler(BizException bizException) { 36 | ResultDTO resultDTO = new ResultDTO(); 37 | resultDTO.setSuccess(false); 38 | resultDTO.setErrorCode(bizException.getErrorCode()); 39 | if ("dev".equals(env)) { 40 | Writer writer = new StringWriter(); 41 | PrintWriter pw = new PrintWriter(writer); 42 | bizException.printStackTrace(pw); 43 | pw.close(); 44 | resultDTO.setErrorMSG(writer.toString()); 45 | } else { 46 | resultDTO.setErrorMSG(bizException.getErrorMSG()); 47 | } 48 | logger.error("error", bizException); 49 | return resultDTO; 50 | } 51 | 52 | @ExceptionHandler(value = Exception.class) 53 | public 54 | @ResponseBody 55 | ResultDTO MyExceptionHandler(Exception exception) { 56 | ResultDTO resultDTO = new ResultDTO(); 57 | resultDTO.setSuccess(false); 58 | resultDTO.setErrorCode(BizErrorCodeConstants.S0000.getCode()); 59 | resultDTO.setErrorMSG(BizErrorCodeConstants.S0000.getCodeMSG()); 60 | if (exception instanceof MissingServletRequestParameterException) { 61 | resultDTO.setErrorCode(BizErrorCodeConstants.S0004.getCode()); 62 | resultDTO.setErrorMSG(BizErrorCodeConstants.S0004.getCodeMSG()); 63 | } 64 | logger.error("error", exception); 65 | return resultDTO; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/VideoDiliServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 5 | import com.suda.MyVideoApi.util.JsoupUtils; 6 | import com.suda.MyVideoApi.util.SuplayerUtil; 7 | import org.jsoup.nodes.Document; 8 | import org.jsoup.nodes.Element; 9 | import org.jsoup.select.Elements; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.regex.Matcher; 16 | import java.util.regex.Pattern; 17 | 18 | /** 19 | * @author guhaibo 20 | * @date 2018/7/29 21 | */ 22 | @Service("videoDiliServiceImpl") 23 | public class VideoDiliServiceImpl extends BaseVideoService { 24 | 25 | public VideoDiliServiceImpl(RedisTemplate redisTemplate) { 26 | super(redisTemplate); 27 | } 28 | 29 | @Override 30 | public API getApi() { 31 | return API.DILI; 32 | } 33 | 34 | @Override 35 | protected VideoPlayDO parsePlayUrl(String refererUrl) { 36 | Document pcDocument = JsoupUtils.getDocWithPC(refererUrl); 37 | Elements scripts = pcDocument.body().getElementsByTag("script"); 38 | List params = new ArrayList<>(); 39 | for (Element script : scripts) { 40 | if (script.toString().indexOf("var") > 0) { 41 | String scriptText = script.data(); 42 | Pattern pattern = Pattern.compile("\"(.*?)\""); 43 | Matcher m = pattern.matcher(scriptText); 44 | params.clear(); 45 | while (m.find()) { 46 | params.add(m.group().replace("\"", "")); 47 | } 48 | if (params.size() >= 2) { 49 | break; 50 | } 51 | } 52 | } 53 | String playerUrl = "https://api.1suplayer.me/player/?userID=&type=" 54 | + params.get(0) + "&vkey=" + params.get(1); 55 | 56 | if (params.get(1).contains("http")){ 57 | VideoPlayDO videoPlayDO = new VideoPlayDO(); 58 | videoPlayDO.setPlayUrl(params.get(1)); 59 | videoPlayDO.setType("dplayer"); 60 | return videoPlayDO; 61 | } 62 | 63 | return SuplayerUtil.getPlayUrl(refererUrl, playerUrl, "https://api.1suplayer.me/player/api.php"); 64 | } 65 | 66 | 67 | @Override 68 | protected String parseOriginUrl(Element item) { 69 | String url = super.parseOriginUrl(item); 70 | 71 | return url. 72 | replace("http://www.dlili.tv/gresource/",""). 73 | replace("https://www.dlili.tv/gresource/",""). 74 | replace("http://www.dililitv.com/gresource/",""). 75 | replace("https://www.dililitv.com/gresource/",""); 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/util/SuplayerUtil.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.util; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 5 | import org.jsoup.nodes.Document; 6 | import org.jsoup.select.Elements; 7 | import org.springframework.util.StringUtils; 8 | 9 | import java.io.IOException; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * @author guhaibo 19 | * @date 2018/8/5 20 | */ 21 | public class SuplayerUtil { 22 | 23 | /** 24 | * 解析视频播放地址 25 | * 26 | * @param refererUrl 27 | * @param playerUrl 28 | * @param api 29 | * @return 30 | */ 31 | public static VideoPlayDO getPlayUrl(String refererUrl, String playerUrl, String api) { 32 | try { 33 | Document pcDocument = JsoupUtils 34 | .getConnection(playerUrl) 35 | .header("Referer", refererUrl) 36 | .get(); 37 | 38 | Elements script = pcDocument.body().getElementsByTag("script"); 39 | String scriptText = script.get(0).data(); 40 | Pattern pattern = Pattern.compile(" = '(.+?)'"); 41 | Matcher m = pattern.matcher(scriptText); 42 | 43 | List params = new ArrayList<>(); 44 | while (m.find()) { 45 | params.add(m.group().replace("= '", "").replace("'", "").trim()); 46 | } 47 | 48 | Map map = new HashMap<>(); 49 | map.put("type", params.get(3)); 50 | map.put("vkey", params.get(4)); 51 | map.put("ckey", params.get(2)); 52 | map.put("userID", ""); 53 | map.put("userIP", params.get(0)); 54 | map.put("refres", "1"); 55 | map.put("my_url", params.get(1)); 56 | 57 | int tryTime = 5; 58 | VideoPlayDO videoPlayDO = null; 59 | while (tryTime > 0 && StringUtils.isEmpty(videoPlayDO)) { 60 | Document document = JsoupUtils 61 | .getConnection(api) 62 | .header("Referer", playerUrl) 63 | .data(map) 64 | .post(); 65 | try { 66 | JSONObject jsonObject = JSONObject.parseObject(document.body().text()); 67 | if ("200".equals(jsonObject.getString("code"))){ 68 | videoPlayDO = new VideoPlayDO(); 69 | videoPlayDO.setPlayUrl(jsonObject.getString("url")); 70 | videoPlayDO.setType(jsonObject.getString("app")); 71 | } 72 | } catch (Exception e) { 73 | } 74 | tryTime--; 75 | } 76 | return videoPlayDO; 77 | } catch (IOException e) { 78 | e.printStackTrace(); 79 | } 80 | return null; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/bot/Telegram.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.bot; 2 | 3 | import com.pengrad.telegrambot.Callback; 4 | import com.pengrad.telegrambot.TelegramBot; 5 | import com.pengrad.telegrambot.UpdatesListener; 6 | import com.pengrad.telegrambot.model.Update; 7 | import com.pengrad.telegrambot.model.request.ForceReply; 8 | import com.pengrad.telegrambot.model.request.ParseMode; 9 | import com.pengrad.telegrambot.request.SendMessage; 10 | import com.pengrad.telegrambot.response.SendResponse; 11 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 12 | import com.suda.MyVideoApi.service.VideoService; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.stereotype.Component; 15 | import org.springframework.util.StringUtils; 16 | 17 | import java.io.IOException; 18 | import java.util.List; 19 | 20 | /** 21 | * @author guhaibo 22 | * @date 2018/9/12 23 | */ 24 | @Component 25 | public class Telegram { 26 | 27 | 28 | private TelegramBot telegramBot; 29 | 30 | private VideoService videoDiliServiceImpl; 31 | 32 | public Telegram(VideoService videoDiliServiceImpl, @Value("${spring.botToken}") String botToken) { 33 | this.videoDiliServiceImpl = videoDiliServiceImpl; 34 | if (StringUtils.isEmpty(botToken)){ 35 | return; 36 | } 37 | telegramBot = new TelegramBot(botToken); 38 | telegramBot.setUpdatesListener(new UpdatesListener() { 39 | @Override 40 | public int process(List updates) { 41 | for (Update update : updates) { 42 | String chatId = update.message().chat().id().toString(); 43 | String messageText = update.message().text(); 44 | List videoDTOS = videoDiliServiceImpl.queryVideoByKey(messageText); 45 | if (videoDTOS.size() > 0) { 46 | for (VideoDTO videoDTO : videoDTOS) { 47 | sendTextMessage(chatId, update.message().messageId(), 48 | videoDTO.getTitle() + ":https://sudavideo.site/#/videoDetail/" + videoDTO.getSource() + "/" + videoDTO.getVideoId()); 49 | } 50 | } else { 51 | sendTextMessage(chatId, update.message().messageId(), "未查询到"); 52 | } 53 | 54 | } 55 | return UpdatesListener.CONFIRMED_UPDATES_ALL; 56 | } 57 | }); 58 | } 59 | 60 | public void sendTextMessage(String chatId, int replayId, String text) { 61 | SendMessage request = new SendMessage(chatId, text) 62 | .parseMode(ParseMode.HTML) 63 | .disableWebPagePreview(false) 64 | .disableNotification(false) 65 | .replyToMessageId(replayId); 66 | telegramBot.execute(request, new Callback() { 67 | @Override 68 | public void onResponse(SendMessage request, SendResponse response) { 69 | // System.out.println(response); 70 | } 71 | 72 | @Override 73 | public void onFailure(SendMessage request, IOException e) { 74 | // e.printStackTrace(); 75 | } 76 | }); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/VideoHJServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 5 | import com.suda.MyVideoApi.domian.dos.VideoSeriesDO; 6 | import com.suda.MyVideoApi.util.JsoupUtils; 7 | import com.suda.MyVideoApi.util.SuplayerUtil; 8 | import org.jsoup.nodes.Document; 9 | import org.jsoup.nodes.Element; 10 | import org.jsoup.select.Elements; 11 | import org.springframework.data.redis.core.RedisTemplate; 12 | import org.springframework.stereotype.Service; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.List; 17 | import java.util.regex.Matcher; 18 | import java.util.regex.Pattern; 19 | 20 | /** 21 | * @author guhaibo 22 | * @date 2018/7/29 23 | */ 24 | @Service("videoHjServiceImpl") 25 | public class VideoHJServiceImpl extends BaseVideoService { 26 | 27 | public VideoHJServiceImpl(RedisTemplate redisTemplate) { 28 | super(redisTemplate); 29 | } 30 | 31 | @Override 32 | public API getApi() { 33 | return API.HJ; 34 | } 35 | 36 | @Override 37 | protected VideoPlayDO parsePlayUrl(String refererUrl) { 38 | Document pcDocument = JsoupUtils.getDocWithPC(refererUrl); 39 | Elements scripts = pcDocument.body().getElementsByTag("script"); 40 | List params = new ArrayList<>(); 41 | for (Element script : scripts) { 42 | if (script.toString().indexOf("var") > 0) { 43 | String scriptText = script.data(); 44 | Pattern pattern = Pattern.compile("\"(.*?)\""); 45 | Matcher m = pattern.matcher(scriptText); 46 | params.clear(); 47 | while (m.find()) { 48 | params.add(m.group().replace("\"", "")); 49 | } 50 | if (params.size() >= 2) { 51 | break; 52 | } 53 | } 54 | } 55 | if (params.get(1).contains("http")){ 56 | VideoPlayDO videoPlayDO = new VideoPlayDO(); 57 | videoPlayDO.setPlayUrl(params.get(1)); 58 | videoPlayDO.setType("dplayer"); 59 | return videoPlayDO; 60 | } 61 | 62 | String playerUrl = "https://api.1suplayer.me/player/?userID=&type=" 63 | + params.get(0) + "&vkey=" + params.get(1); 64 | return SuplayerUtil.getPlayUrl(refererUrl, playerUrl, "https://api.1suplayer.me/player/api.php"); 65 | } 66 | 67 | @Override 68 | protected List parseVideoSeries(Document document) { 69 | Elements serieses = document.getElementsByClass("video_list_li"); 70 | if (serieses == null) { 71 | return Collections.emptyList(); 72 | } 73 | List videoSeriesDOS = new ArrayList<>(); 74 | if (serieses.size() > 0) { 75 | for (Element seriese : serieses) { 76 | for (Element element : seriese.getElementsByTag("a")) { 77 | VideoSeriesDO videoSeriesDO = new VideoSeriesDO(); 78 | videoSeriesDO.setName(element.text()); 79 | videoSeriesDO.setSeriesId(element.attr("href") 80 | .replace("??","?") 81 | .replace("?Play-", "")); 82 | videoSeriesDOS.add(videoSeriesDO); 83 | } 84 | } 85 | } 86 | return videoSeriesDOS; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.suda 7 | MyVideoApi 8 | 1.1 9 | jar 10 | 11 | MyVedioApi 12 | Provide VideoApi 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.5.14.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-data-redis 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-web 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-logging 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-tomcat 43 | 44 | 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-undertow 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-test 57 | test 58 | 59 | 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-starter-log4j2 64 | 65 | 66 | 67 | 68 | org.jsoup 69 | jsoup 70 | 1.10.2 71 | 72 | 73 | 74 | com.alibaba 75 | fastjson 76 | 1.2.31 77 | 78 | 79 | 80 | 81 | org.projectlombok 82 | lombok 83 | 1.16.14 84 | 85 | 86 | 87 | com.github.pengrad 88 | java-telegram-bot-api 89 | 4.1.0 90 | 91 | 92 | 93 | org.springframework.boot 94 | spring-boot-starter-websocket 95 | 1.3.5.RELEASE 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.springframework.boot 104 | spring-boot-maven-plugin 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MyVideoApi 2 | 3 | ## 这是什么 4 | MyVideoApi 是基于 Spring Boot 开发的应用,利用网络爬虫技术,实时解析互联网资源,提供api给前端 5 | 6 | ## 补充说明 7 | 本项目仅用于学习,任何单位或个人不得将项目用于商业目的。 8 | 9 | ## api 10 | 11 | ### 查询视频源 12 | /video/source 13 | 接口返回 14 | ``` 15 | { 16 | "data":[ 17 | { 18 | "source":0, 19 | "type":"film", 20 | "name":"电影1" 21 | }, 22 | { 23 | "source":0, 24 | "type":"teleplay", 25 | "name":"剧集1" 26 | } 27 | ], 28 | "success":true 29 | } 30 | ``` 31 | 32 | ### 根据类别分页查询视频 33 | /video/page?source=0&type=film&pageIndex=1 34 | 参数 35 | ``` 36 | * @param source 源id 37 | * @param type 类型 38 | * @param pageIndex 页数 39 | ``` 40 | 41 | 接口返回 42 | ``` 43 | { 44 | "data":{ 45 | "pageSize":25, 46 | "data":[ 47 | { 48 | "title":"碟中谍5", 49 | "thumb":"https://img31.mtime.cn/mg/2015/08/26/172358.77099492_270X405X4.jpg", 50 | "videoId":"3704", 51 | "source":0 52 | }, 53 | { 54 | "title":"碟中谍4", 55 | "thumb":"https://img5.mtime.cn/mg/2016/11/09/144341.13183872_270X405X4.jpg", 56 | "videoId":"3703", 57 | "source":0 58 | } 59 | ] 60 | }, 61 | "success":true 62 | } 63 | ``` 64 | 65 | ### 查询视频详情 66 | /video/detail?source=0&videoId=3704 67 | 参数 68 | ``` 69 | * @param source 源id 70 | * @param videoId 视频id 71 | ``` 72 | 73 | 接口返回 74 | ``` 75 | { 76 | "data": { 77 | "videoSeries": [ 78 | { 79 | "seriesId": "Ac_01", 80 | "name": "天翼高清" 81 | } 82 | ], 83 | "desc": "  资深特工伊森·亨特(汤姆·克鲁斯 Tom Cruise 饰)也有百密一疏时刻,他在接收最新任务时遭到神秘组织“辛迪加”的暗算落入对方手中。辛迪加是一支由全球各地前特工组成的秘密组织,此前一直被CIA视为空穴来风。在对方成员伊莎·福斯特(丽贝卡·弗格森 Rebecca Ferguson 饰)的帮助下,伊森逃出生天,并及时向威廉•布莱德(杰瑞米·雷纳 Jeremy Renner 饰)汇报了辛迪加确切存在的消息。然而此时布莱德的日子并不好过,他负责的IMF机构因俄罗斯核弹头等事件遭到CIA指控并责令解散。在得知该组织密谋刺杀奥地利总统时,伊森联系上了老搭档班吉·邓恩(西蒙·佩吉 Simon Pegg 饰)展开行动,并于谜样女郎伊莎再度相会。   接下来的一连串事件中,辛迪加的真面目逐渐揭开,而他们也终于显露出真实的目的……", 84 | "previewImgs": [ 85 | "https://img31.mtime.cn/pi/2015/07/21/094148.72440524_1000X1000.jpg", 86 | "https://img31.mtime.cn/pi/2015/07/21/094014.34414121_1000X1000.jpg", 87 | "https://img31.mtime.cn/pi/2015/07/21/094009.38871136_1000X1000.jpg", 88 | "https://img31.mtime.cn/pi/2015/07/21/094041.54516242_1000X1000.jpg" 89 | ] 90 | }, 91 | "success": true 92 | } 93 | ``` 94 | 95 | ### 查询视频播放地址 96 | /video/playUrl?source=0&videoId=3704&seriesId=Ac_01 97 | 参数 98 | ``` 99 | * @param source 源id 100 | * @param videoId 视频id 101 | * @param seriesId 集数id 102 | ``` 103 | 104 | 接口返回 105 | ``` 106 | { 107 | "data": "http://vd3.bdstatic.com/mda-ih5un792ceu09ya0/mda-ih5un792ceu09ya0.mp4", 108 | "success": true 109 | } 110 | ``` 111 | 112 | ### 根据名称查视频 113 | /video/search?videoName=复仇 114 | 参数 115 | ``` 116 | * @param source 源id 117 | * @param videoId 视频id 118 | * @param seriesId 集数id 119 | ``` 120 | 121 | 接口返回 122 | ``` 123 | { 124 | "data": [ 125 | { 126 | "title": "复仇", 127 | "thumb": "https://img5.mtime.cn/mg/2018/04/03/182824.74161297_270X405X4.jpg", 128 | "videoId": "3509", 129 | "source": 0 130 | }, 131 | { 132 | "title": "《政宗君的复仇》高清全集在线观看", 133 | "thumb": "https://ww2.sinaimg.cn/large/87c01ec7gy1fq6nwyonxej20qo0goaxf.jpg", 134 | "videoId": "anime+av449", 135 | "source": 4 136 | }, 137 | { 138 | "title": "复仇者联盟2:奥创纪元", 139 | "thumb": "https://img31.mtime.cn/mg/2015/03/27/120537.13212993_270X405X4.jpg", 140 | "videoId": "movies+list02+3030.html", 141 | "source": 3 142 | }, 143 | { 144 | "title": "复仇者联盟3", 145 | "thumb": "https://img5.mtime.cn/mg/2018/03/30/101318.97845092_270X405X4.jpg", 146 | "videoId": "3682", 147 | "source": 0 148 | }, 149 | { 150 | "title": "复仇者联盟", 151 | "thumb": "https://img5.mtime.cn/mg/2017/01/24/105507.20986123_270X405X4.jpg", 152 | "videoId": "2585", 153 | "source": 0 154 | } 155 | ], 156 | "success": true 157 | } 158 | ``` 159 | 160 | ### 查询站点信息 161 | /video/source 162 | 接口返回 163 | ``` 164 | { 165 | "data": { 166 | "siteName": "xxxxx", 167 | "contactEmail": "xxxx@xx.com" 168 | }, 169 | "success": true 170 | } 171 | ``` -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/util/JsoupUtils.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.util; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.suda.MyVideoApi.constant.BizErrorCodeConstants; 5 | import com.suda.MyVideoApi.domian.BizException; 6 | import lombok.extern.log4j.Log4j2; 7 | import okhttp3.Call; 8 | import okhttp3.OkHttpClient; 9 | import okhttp3.Request; 10 | import org.jsoup.Connection; 11 | import org.jsoup.Jsoup; 12 | import org.jsoup.nodes.Document; 13 | 14 | import java.io.IOException; 15 | import java.util.ArrayList; 16 | import java.util.Collections; 17 | import java.util.List; 18 | 19 | /** 20 | * Jsoup工具类 21 | * 22 | * @author guhaibo 23 | * @date 2018/7/22 24 | */ 25 | @Log4j2 26 | public class JsoupUtils { 27 | private static final String UA_PHONE = "Mozilla/5.0 (Linux; Android 4.3; Nexus 10 Build/JSS15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.23 Safari/537.36"; 28 | private static final String UA_PC = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"; 29 | 30 | private static final int TIME_OUT = 30 * 1000; 31 | 32 | private static final String ERROR_DESC = "网址请求失败:"; 33 | 34 | static OkHttpClient okHttpClient = new OkHttpClient(); 35 | 36 | /** 37 | * 上一次代理刷新时间 38 | */ 39 | static long lastProxyTime = 0; 40 | 41 | /** 42 | * 代理池 43 | */ 44 | static List httpProxies = new ArrayList<>(); 45 | 46 | public static Connection getConnection(String url) { 47 | return Jsoup.connect(url).userAgent(UA_PC).timeout(TIME_OUT).ignoreContentType(true); 48 | } 49 | 50 | public static Document getDocWithPC(String url) { 51 | int tryTime = 5; 52 | while (tryTime > 0) { 53 | try { 54 | HttpProxy httpProxy = getProxy(); 55 | if (httpProxy != null) { 56 | System.setProperty("http.proxySet", "true"); 57 | System.getProperties().put("http.proxyHost", httpProxy.host); 58 | System.getProperties().put("http.proxyPort", httpProxy.port); 59 | } else { 60 | System.setProperty("http.proxySet", "false"); 61 | } 62 | 63 | Document document = Jsoup.connect(url).userAgent(UA_PC).timeout(TIME_OUT) 64 | .ignoreHttpErrors(true) 65 | .ignoreContentType(true).get(); 66 | tryTime = 0; 67 | return document; 68 | } catch (IOException e) { 69 | tryTime--; 70 | log.error(ERROR_DESC + url); 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | public static Document getDocWithPhone(String url) { 77 | try { 78 | return Jsoup.connect(url).userAgent(UA_PHONE).timeout(TIME_OUT).ignoreContentType(true).validateTLSCertificates(false).get(); 79 | } catch (IOException e) { 80 | log.error(ERROR_DESC + url); 81 | throw new BizException(BizErrorCodeConstants.S0002, e); 82 | } 83 | } 84 | 85 | 86 | static class HttpProxy { 87 | public String host; 88 | public String port; 89 | 90 | @Override 91 | public String toString() { 92 | return "HttpProxy{" + 93 | "host='" + host + '\'' + 94 | ", port='" + port + '\'' + 95 | '}'; 96 | } 97 | } 98 | 99 | /** 100 | * 获取代理 101 | * 102 | * @return 103 | */ 104 | private synchronized static HttpProxy getProxy() { 105 | try { 106 | 107 | //每10分钟刷新代理池 108 | if (System.currentTimeMillis() - lastProxyTime > 60 * 10 * 1000) { 109 | httpProxies.clear(); 110 | } 111 | 112 | if (httpProxies.size() <= 0) { 113 | Request request = new Request.Builder().url("https://raw.githubusercontent.com/fate0/proxylist/master/proxy.list").get().build(); 114 | Call call = okHttpClient.newCall(request); 115 | String result = call.execute().body().string(); 116 | 117 | for (String proxy : result.split("\n")) { 118 | if (httpProxies.size() > 10) { 119 | break; 120 | } 121 | httpProxies.add(JSON.parseObject(proxy, HttpProxy.class)); 122 | } 123 | lastProxyTime = System.currentTimeMillis(); 124 | } 125 | 126 | Collections.shuffle(httpProxies); 127 | return httpProxies.get(0); 128 | } catch (Exception e) { 129 | return null; 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/VideoCartoonServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.BizException; 5 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 6 | import com.suda.MyVideoApi.domian.dos.VideoSeriesDO; 7 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 8 | import com.suda.MyVideoApi.util.JsoupUtils; 9 | import com.suda.MyVideoApi.util.SuplayerUtil; 10 | import org.jsoup.nodes.Document; 11 | import org.jsoup.nodes.Element; 12 | import org.jsoup.select.Elements; 13 | import org.springframework.data.redis.core.RedisTemplate; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.util.StringUtils; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | /** 22 | * 动漫 23 | * 24 | * @author guhaibo 25 | * @date 2018/7/29 26 | */ 27 | @Service("videoCartoonServiceImpl") 28 | public class VideoCartoonServiceImpl extends BaseVideoService { 29 | 30 | @Override 31 | public List queryVideosByType(String tag, int pageIndex, boolean useCache) throws BizException { 32 | return super.queryVideosByType(tag, pageIndex, useCache); 33 | } 34 | 35 | 36 | public VideoCartoonServiceImpl(RedisTemplate redisTemplate) { 37 | super(redisTemplate); 38 | } 39 | 40 | @Override 41 | protected Elements parseItems(Document document) { 42 | return document.getElementsByClass("item"); 43 | } 44 | 45 | @Override 46 | protected String parseName(Element item) { 47 | Element nameEl = item.getElementsByTag("a").first(); 48 | if (nameEl != null) { 49 | return nameEl.text(); 50 | } 51 | return ""; 52 | } 53 | 54 | @Override 55 | protected String parseThumb(Element item) { 56 | Element thumbEl = item.getElementsByTag("img").first(); 57 | if (thumbEl != null) { 58 | String thumb = thumbEl.attr("src"); 59 | if (thumb.indexOf("http") >= 0) { 60 | return thumb; 61 | } else { 62 | return "https:" + thumb; 63 | } 64 | } 65 | return ""; 66 | } 67 | 68 | @Override 69 | public API getApi() { 70 | return API.CARTOON; 71 | } 72 | 73 | @Override 74 | protected String parseVideoDesc(Document document) { 75 | Element descEl = document.getElementsByClass("jj").first(); 76 | if (descEl != null) { 77 | String desc = descEl.text(); 78 | if (!StringUtils.isEmpty(desc)) { 79 | return desc.trim(); 80 | } 81 | } 82 | return ""; 83 | } 84 | 85 | @Override 86 | protected List parsePreviewImgs(Document document) { 87 | List previewImgs = new ArrayList<>(); 88 | Element carouselInnersEl = document.getElementsByClass("carousel-inner").first(); 89 | if (carouselInnersEl != null) { 90 | Elements previewImgEl = carouselInnersEl.getElementsByTag("img"); 91 | for (Element element : previewImgEl) { 92 | String thumb = element.attr("src"); 93 | if (thumb.indexOf("http") >= 0) { 94 | previewImgs.add(thumb); 95 | } else { 96 | previewImgs.add("https:" + thumb); 97 | } 98 | } 99 | } 100 | return previewImgs; 101 | } 102 | 103 | @Override 104 | protected List parseVideoSeries(Document document) { 105 | Element xuanji = document.getElementById("xuanji"); 106 | if (xuanji == null) { 107 | return Collections.emptyList(); 108 | } 109 | Elements aTagEls = xuanji.getElementsByTag("a"); 110 | if (aTagEls.size() < 0) { 111 | return Collections.emptyList(); 112 | } 113 | List videoSeriesDOS = new ArrayList<>(); 114 | for (Element aTagEl : aTagEls) { 115 | VideoSeriesDO videoSeriesDO = new VideoSeriesDO(); 116 | videoSeriesDO.setName(aTagEl.attr("title")); 117 | videoSeriesDO.setSeriesId(aTagEl.attr("href") 118 | .replace("?Play-", "") 119 | .replace(".html", "")); 120 | videoSeriesDOS.add(videoSeriesDO); 121 | } 122 | return videoSeriesDOS; 123 | } 124 | 125 | @Override 126 | protected VideoPlayDO parsePlayUrl(String refererUrl) { 127 | Document pcDocument = JsoupUtils.getDocWithPC(refererUrl); 128 | String playerUrl = pcDocument.getElementsByClass("player").first().attr("src"); 129 | return SuplayerUtil.getPlayUrl(refererUrl, playerUrl, "https://api.1suplayer.me/player/api.php"); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/controller/VideoController.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.controller; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.constant.Source; 5 | import com.suda.MyVideoApi.domian.ResultDTO; 6 | import com.suda.MyVideoApi.domian.dos.VideoDetailDO; 7 | import com.suda.MyVideoApi.domian.dto.SiteInfoDTO; 8 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 9 | import com.suda.MyVideoApi.domian.dto.VideoSourceDTO; 10 | import com.suda.MyVideoApi.service.VideoService; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * @author guhaibo 18 | * @date 2018/7/29 19 | */ 20 | @RestController 21 | @RequestMapping("/video") 22 | public class VideoController { 23 | 24 | @Value("${spring.siteName}") 25 | private String siteName; 26 | 27 | @Value("${spring.contactEmail}") 28 | private String contactEmail; 29 | 30 | private final VideoService videoDiliServiceImpl; 31 | private final VideoService videoMjwServiceImpl; 32 | private final VideoService videoHjServiceImpl; 33 | private final VideoService videoLZServiceImpl; 34 | private final VideoService videoCartoonServiceImpl; 35 | 36 | public VideoController(VideoService videoDiliServiceImpl, VideoService videoMjwServiceImpl, VideoService videoHjServiceImpl, VideoService videoLZServiceImpl, VideoService videoCartoonServiceImpl) { 37 | this.videoDiliServiceImpl = videoDiliServiceImpl; 38 | this.videoMjwServiceImpl = videoMjwServiceImpl; 39 | this.videoHjServiceImpl = videoHjServiceImpl; 40 | this.videoLZServiceImpl = videoLZServiceImpl; 41 | this.videoCartoonServiceImpl = videoCartoonServiceImpl; 42 | } 43 | 44 | private VideoService getVideoService(int source) { 45 | if (source == API.DILI.sourceId) { 46 | return videoDiliServiceImpl; 47 | } else if (source == API.MJW.sourceId) { 48 | return videoMjwServiceImpl; 49 | } else if (source == API.HJ.sourceId) { 50 | return videoHjServiceImpl; 51 | } else if (source == API.LZ.sourceId) { 52 | return videoLZServiceImpl; 53 | } else if (source == API.CARTOON.sourceId) { 54 | return videoCartoonServiceImpl; 55 | } 56 | return null; 57 | } 58 | 59 | /** 60 | * 查询视频源 61 | * 62 | * @return 63 | */ 64 | @GetMapping("/source") 65 | public ResultDTO> qureySource() { 66 | //film teleplay cartoon variety_show 67 | ResultDTO resultDTO = new ResultDTO(); 68 | resultDTO.setSuccess(true); 69 | resultDTO.setData(Source.videoSourceDTOS); 70 | return resultDTO; 71 | } 72 | 73 | 74 | /** 75 | * 根据类别查询视频 76 | * 77 | * @param source 源id 78 | * @param type 类型 79 | * @param pageIndex 页数 80 | * @return 81 | */ 82 | @GetMapping("/page") 83 | public ResultDTO> queryVideosByType(@RequestParam int source, @RequestParam String type, @RequestParam int pageIndex) { 84 | //film teleplay cartoon variety_show 85 | ResultDTO resultDTO = new ResultDTO(); 86 | resultDTO.setSuccess(true); 87 | resultDTO.setData(getVideoService(source).queryVideosByTypeNew(type, pageIndex, true)); 88 | return resultDTO; 89 | } 90 | 91 | /** 92 | * 查询视频详情 93 | * 94 | * @param videoId 视频id 95 | * @param source 源id 96 | * @return 97 | */ 98 | @GetMapping("/detail") 99 | public ResultDTO queryVideosDetail(@RequestParam String videoId, @RequestParam int source) { 100 | ResultDTO resultDTO = new ResultDTO(); 101 | resultDTO.setSuccess(true); 102 | resultDTO.setData(getVideoService(source).queryVideosDetail(videoId, true)); 103 | return resultDTO; 104 | } 105 | 106 | /** 107 | * 根据名称查视频 108 | * 109 | * @param videoName 视频名称 110 | * @return 111 | */ 112 | @GetMapping("/search") 113 | public ResultDTO> queryVideoByKey(@RequestParam String videoName) { 114 | ResultDTO resultDTO = new ResultDTO(); 115 | resultDTO.setSuccess(true); 116 | resultDTO.setData(videoDiliServiceImpl.queryVideoByKey(videoName)); 117 | return resultDTO; 118 | } 119 | 120 | /** 121 | * 查询视频播放地址 122 | * 123 | * @param videoId 视频id 124 | * @param seriesId 集数id 125 | * @param source 源id 126 | * @return 127 | */ 128 | @GetMapping("/playUrl") 129 | public ResultDTO queryVideoPlayUrl(@RequestParam String videoId, @RequestParam String seriesId, @RequestParam int source) { 130 | ResultDTO resultDTO = new ResultDTO(); 131 | resultDTO.setSuccess(true); 132 | resultDTO.setData(getVideoService(source).queryPlayUrl(videoId, seriesId)); 133 | return resultDTO; 134 | } 135 | 136 | /** 137 | * 查询站点信息 138 | */ 139 | @GetMapping("/siteInfo") 140 | public ResultDTO querySiteInfo() { 141 | ResultDTO resultDTO = new ResultDTO(); 142 | resultDTO.setSuccess(true); 143 | SiteInfoDTO siteInfoDTO = new SiteInfoDTO(); 144 | siteInfoDTO.setSiteName(siteName); 145 | siteInfoDTO.setContactEmail(contactEmail); 146 | resultDTO.setData(siteInfoDTO); 147 | return resultDTO; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Migwn, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 204 | echo $MAVEN_PROJECTBASEDIR 205 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 206 | 207 | # For Cygwin, switch paths to Windows format before running java 208 | if $cygwin; then 209 | [ -n "$M2_HOME" ] && 210 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 211 | [ -n "$JAVA_HOME" ] && 212 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 213 | [ -n "$CLASSPATH" ] && 214 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 215 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 216 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 217 | fi 218 | 219 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 220 | 221 | exec "$JAVACMD" \ 222 | $MAVEN_OPTS \ 223 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 224 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 225 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 226 | -------------------------------------------------------------------------------- /src/main/java/com/suda/MyVideoApi/service/BaseVideoService.java: -------------------------------------------------------------------------------- 1 | package com.suda.MyVideoApi.service; 2 | 3 | import com.suda.MyVideoApi.constant.API; 4 | import com.suda.MyVideoApi.domian.BizException; 5 | import com.suda.MyVideoApi.domian.PageDTO; 6 | import com.suda.MyVideoApi.domian.converter.VideoConverter; 7 | import com.suda.MyVideoApi.domian.dos.VideoDO; 8 | import com.suda.MyVideoApi.domian.dos.VideoDetailDO; 9 | import com.suda.MyVideoApi.domian.dos.VideoPlayDO; 10 | import com.suda.MyVideoApi.domian.dos.VideoSeriesDO; 11 | import com.suda.MyVideoApi.domian.dto.VideoDTO; 12 | import com.suda.MyVideoApi.domian.dto.VideoPlayDTO; 13 | import com.suda.MyVideoApi.util.JsoupUtils; 14 | import lombok.AllArgsConstructor; 15 | import org.jsoup.nodes.Document; 16 | import org.jsoup.nodes.Element; 17 | import org.jsoup.select.Elements; 18 | import org.springframework.beans.BeanUtils; 19 | import org.springframework.data.redis.core.RedisTemplate; 20 | import org.springframework.util.StringUtils; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.List; 25 | import java.util.Set; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | import static com.suda.MyVideoApi.domian.converter.VideoConverter.PLAY_SPLITE; 29 | 30 | /** 31 | * @author guhaibo 32 | * @date 2018/7/29 33 | */ 34 | @AllArgsConstructor 35 | public abstract class BaseVideoService implements VideoService { 36 | 37 | public abstract API getApi(); 38 | 39 | protected RedisTemplate redisTemplate; 40 | 41 | @Override 42 | public PageDTO queryVideosByTypeNew(String tag, int pageIndex, boolean useCache) throws BizException { 43 | PageDTO pageDTO = new PageDTO(); 44 | String pageKey = getApi().name() + ":" + "VideoTypeSize:" + tag; 45 | Integer allPage = (Integer) redisTemplate.opsForValue().get(pageKey); 46 | if (allPage == null) { 47 | allPage = 0; 48 | } 49 | pageDTO.setPageSize(allPage); 50 | pageDTO.setData(queryVideosByType(tag, pageIndex, useCache)); 51 | return pageDTO; 52 | } 53 | 54 | 55 | protected int maxVideo() { 56 | return 30; 57 | } 58 | 59 | @Override 60 | public List queryVideosByType(String tag, int pageIndex, boolean useCache) throws BizException { 61 | String key = getApi().name() + ":" + "VideoType:" + tag + ":" + pageIndex; 62 | List videoDOS = (List) redisTemplate.opsForValue().get(key); 63 | if (!useCache) { 64 | videoDOS = null; 65 | } 66 | 67 | if (videoDOS == null) { 68 | videoDOS = new ArrayList<>(); 69 | String video = getApi().baseUrl + tag; 70 | if (pageIndex > 1) { 71 | video = video + "/page/" + pageIndex; 72 | } 73 | Document pcDocument = JsoupUtils.getDocWithPC(video); 74 | 75 | int i = 0; 76 | for (Element article : parseItems(pcDocument)) { 77 | if (i >= maxVideo()) { 78 | break; 79 | } 80 | 81 | VideoDO videoDO = new VideoDO(); 82 | videoDO.setSource(getApi().sourceId); 83 | videoDO.setTitle(parseName(article)); 84 | videoDO.setOriginUrl(parseOriginUrl(article)); 85 | videoDO.setThumb(parseThumb(article)); 86 | if (StringUtils.isEmpty(videoDO.getThumb())) { 87 | continue; 88 | } 89 | videoDOS.add(videoDO); 90 | 91 | String key2 = "VideoTitle:" + videoDO.getTitle() + "(源" + getApi().sourceId + ")"; 92 | redisTemplate.opsForValue().set(key2, videoDO); 93 | i++; 94 | } 95 | redisTemplate.opsForValue().set(key, videoDOS); 96 | } 97 | 98 | List videoDTOS = new ArrayList<>(); 99 | for (VideoDO videoDO : videoDOS) { 100 | try { 101 | VideoConverter videoConverter = new VideoConverter(); 102 | VideoDTO videoDTO = new VideoDTO(); 103 | videoConverter.convert(videoDO, videoDTO); 104 | String key3 = "VideoId:" + videoDTO.getVideoId(); 105 | redisTemplate.opsForValue().set(key3, videoDO.getTitle()); 106 | videoDTOS.add(videoDTO); 107 | } catch (Exception e) { 108 | continue; 109 | } 110 | } 111 | 112 | return videoDTOS; 113 | } 114 | 115 | @Override 116 | public VideoDetailDO queryVideosDetail(String videoId, boolean useCache) { 117 | String key = getApi().name() + ":" + "VideoDetailDO:" + videoId; 118 | VideoDetailDO videoDetailDO = (VideoDetailDO) redisTemplate.opsForValue().get(key); 119 | 120 | if (!useCache) { 121 | videoDetailDO = null; 122 | } 123 | 124 | if (videoDetailDO == null) { 125 | videoDetailDO = new VideoDetailDO(); 126 | 127 | Document pcDocument = JsoupUtils.getDocWithPC(getApi().resUrl + videoId.replace(PLAY_SPLITE, "/")); 128 | videoDetailDO.setDesc(parseVideoDesc(pcDocument)); 129 | videoDetailDO.setPreviewImgs(parsePreviewImgs(pcDocument)); 130 | videoDetailDO.setVideoSeries(parseVideoSeries(pcDocument)); 131 | 132 | redisTemplate.opsForValue().set(key, videoDetailDO); 133 | } 134 | if (videoDetailDO!=null){ 135 | String key2 = "VideoId:" + videoId; 136 | videoDetailDO.setTitle((String) redisTemplate.opsForValue().get(key2)); 137 | } 138 | 139 | return videoDetailDO; 140 | } 141 | 142 | @Override 143 | public VideoPlayDTO queryPlayUrl(String videoId, String seriesId) throws BizException { 144 | String key = getApi().name() + ":" + "VideoPlayUrl:" + videoId + ":" + seriesId; 145 | VideoPlayDO videoPlayDO = (VideoPlayDO) redisTemplate.opsForValue().get(key); 146 | if (videoPlayDO == null) { 147 | String seriesUrl = String.format(getApi().resSeriresUrl, videoId, seriesId).replace(PLAY_SPLITE, "/"); 148 | videoPlayDO = parsePlayUrl(seriesUrl); 149 | if (videoPlayDO != null) { 150 | redisTemplate.opsForValue().set(key, videoPlayDO); 151 | redisTemplate.expire(key, 1, TimeUnit.HOURS); 152 | } 153 | } 154 | VideoPlayDTO videoPlayDTO = new VideoPlayDTO(); 155 | BeanUtils.copyProperties(videoPlayDO, videoPlayDTO); 156 | return videoPlayDTO; 157 | } 158 | 159 | @Override 160 | public List queryVideoByKey(String search) { 161 | String pattern = "VideoTitle:*" + search + "*"; 162 | Set keys = redisTemplate.keys(pattern); 163 | List videoDTOS = new ArrayList<>(); 164 | int max = Math.min(5, keys.size()); 165 | int i = 0; 166 | for (String key : keys) { 167 | if (i == max) { 168 | break; 169 | } 170 | VideoDO videoDO = (VideoDO) redisTemplate.opsForValue().get(key); 171 | try { 172 | VideoDTO videoDTO = new VideoDTO(); 173 | VideoConverter videoConverter = new VideoConverter(); 174 | videoConverter.convert(videoDO, videoDTO); 175 | videoDTOS.add(videoDTO); 176 | i++; 177 | } catch (Exception e) { 178 | continue; 179 | } 180 | } 181 | return videoDTOS; 182 | } 183 | 184 | /** 185 | * 解析条目 186 | * 187 | * @param document 188 | * @return 189 | */ 190 | protected Elements parseItems(Document document) { 191 | return document.getElementsByTag("article"); 192 | } 193 | 194 | /** 195 | * 解析标题 196 | * 197 | * @param item 198 | * @return 199 | */ 200 | protected String parseName(Element item) { 201 | Element nameEl = item.getElementsByTag("h2").first(); 202 | if (nameEl != null) { 203 | return nameEl.text(); 204 | } 205 | return ""; 206 | } 207 | 208 | /** 209 | * 解析封面 210 | * 211 | * @param item 212 | * @return 213 | */ 214 | protected String parseThumb(Element item) { 215 | Element thumbEl = item.getElementsByClass("thumb").first(); 216 | if (thumbEl != null) { 217 | String thumb = thumbEl.attr("data-original"); 218 | if (thumb.indexOf("http") >= 0) { 219 | return thumb; 220 | } else if (StringUtils.isEmpty(thumb)) { 221 | return ""; 222 | } else { 223 | return "https:" + thumb; 224 | } 225 | } 226 | return ""; 227 | } 228 | 229 | /** 230 | * 解析原始地址 231 | * 232 | * @param item 233 | * @return 234 | */ 235 | protected String parseOriginUrl(Element item) { 236 | Elements urls = item.getElementsByTag("a"); 237 | if (urls.size() > 0) { 238 | return urls.get(0).attr("href"); 239 | } 240 | return ""; 241 | } 242 | 243 | /** 244 | * 解析介绍 245 | * 246 | * @param document 247 | * @return 248 | */ 249 | protected String parseVideoDesc(Document document) { 250 | Element descEl = document.getElementsByClass("jianjie").first(); 251 | if (descEl != null) { 252 | descEl = descEl.getElementsByTag("span").first(); 253 | if (descEl != null) { 254 | String desc = descEl.text(); 255 | if (!StringUtils.isEmpty(desc)) { 256 | return desc.trim(); 257 | } 258 | } 259 | } 260 | return ""; 261 | } 262 | 263 | /** 264 | * 解析预览图 265 | * 266 | * @param document 267 | * @return 268 | */ 269 | protected List parsePreviewImgs(Document document) { 270 | List previewImgs = new ArrayList<>(); 271 | Element jianjiesEl = document.getElementsByClass("jianjie").first(); 272 | if (jianjiesEl != null) { 273 | Elements previewImgEl = jianjiesEl.getElementsByTag("img"); 274 | for (Element element : previewImgEl) { 275 | String thumb = element.attr("data-original"); 276 | if (thumb.indexOf("http") >= 0) { 277 | previewImgs.add(thumb); 278 | } else { 279 | previewImgs.add("https:" + thumb); 280 | } 281 | } 282 | } 283 | return previewImgs; 284 | } 285 | 286 | /** 287 | * 解析剧集 288 | * 289 | * @param document 290 | * @return 291 | */ 292 | protected List parseVideoSeries(Document document) { 293 | List elementsList = new ArrayList<>(); 294 | elementsList.add(document.getElementsByClass("mplay-list")); 295 | elementsList.add(document.getElementsByClass("article-paging")); 296 | elementsList.add(document.getElementsByClass("video_list_li")); 297 | Elements serieses = null; 298 | for (Elements elements : elementsList) { 299 | if (elements.size() > 0) { 300 | serieses = elements; 301 | break; 302 | } 303 | } 304 | if (serieses == null) { 305 | return Collections.emptyList(); 306 | } 307 | 308 | List videoSeriesDOS = new ArrayList<>(); 309 | if (serieses.size() > 0) { 310 | for (Element seriese : serieses) { 311 | for (Element element : seriese.getElementsByTag("a")) { 312 | VideoSeriesDO videoSeriesDO = new VideoSeriesDO(); 313 | videoSeriesDO.setName(element.text()); 314 | videoSeriesDO.setSeriesId(element.attr("href").replace("?Play=", "")); 315 | videoSeriesDOS.add(videoSeriesDO); 316 | } 317 | } 318 | } 319 | return videoSeriesDOS; 320 | } 321 | 322 | /** 323 | * 解析视频播放链接 324 | * 325 | * @param refererUrl 326 | * @return 327 | */ 328 | protected abstract VideoPlayDO parsePlayUrl(String refererUrl); 329 | } 330 | --------------------------------------------------------------------------------