init
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.ping.study.controller.tx;
|
package com.ping.study.controller.tx;
|
||||||
|
|
||||||
import com.ping.study.service.tx.LiveInfoService;
|
import com.ping.study.service.tx.LiveInfoService;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -21,4 +22,12 @@ public class LiveInfoController {
|
|||||||
public String getLiveInfo(@PathVariable String cnlid) {
|
public String getLiveInfo(@PathVariable String cnlid) {
|
||||||
return liveInfoService.getLiveInfo(cnlid);
|
return liveInfoService.getLiveInfo(cnlid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//定时执行更新直播链接
|
||||||
|
//定时任务 从北京时间凌晨到12点,每过半个小时执行一次
|
||||||
|
@Scheduled(cron = "0 0/30 0-11 * * ?")
|
||||||
|
@RequestMapping("/live/refresh")
|
||||||
|
public String refreshLiveInfo() {
|
||||||
|
return liveInfoService.refreshLiveInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.ping.study.controller.tx;
|
|||||||
|
|
||||||
import com.ping.study.service.tx.TxSportTokenRefreshService;
|
import com.ping.study.service.tx.TxSportTokenRefreshService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -14,7 +15,8 @@ public class TokenRefreshController {
|
|||||||
private TxSportTokenRefreshService txSportTokenRefreshService;
|
private TxSportTokenRefreshService txSportTokenRefreshService;
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping("/refresh")
|
|
||||||
|
@RequestMapping("/cookie/refresh")
|
||||||
public Boolean refreshToken()
|
public Boolean refreshToken()
|
||||||
{
|
{
|
||||||
return txSportTokenRefreshService.refreshCookies();
|
return txSportTokenRefreshService.refreshCookies();
|
||||||
|
|||||||
@@ -36,4 +36,7 @@ public interface UrlsMapper {
|
|||||||
void deleteUrlById(Integer id);
|
void deleteUrlById(Integer id);
|
||||||
//删除所有url
|
//删除所有url
|
||||||
void deleteAllUrls();
|
void deleteAllUrls();
|
||||||
|
|
||||||
|
//更新比赛直播链接
|
||||||
|
void updateUrlsWithGameId(String gameId, String s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.ping.study.model.vo.live;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class LiveInfoResponse {
|
||||||
|
private int retcode;
|
||||||
|
private List<PlayItem> play_list;
|
||||||
|
// 其他需要的字段...
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class PlayItem {
|
||||||
|
private List<String> urls;
|
||||||
|
// 其他需要的字段...
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
package com.ping.study.service.tx;
|
package com.ping.study.service.tx;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.ping.study.mapper.GamesMapper;
|
||||||
|
import com.ping.study.mapper.UrlsMapper;
|
||||||
import com.ping.study.model.dto.tx.LiveInfoRequest;
|
import com.ping.study.model.dto.tx.LiveInfoRequest;
|
||||||
|
import com.ping.study.model.vo.live.LiveInfoResponse;
|
||||||
|
import com.ping.study.pojo.Games;
|
||||||
import com.ping.study.utils.tx.CKeyGenerator;
|
import com.ping.study.utils.tx.CKeyGenerator;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
@@ -14,6 +20,8 @@ import reactor.core.publisher.Mono;
|
|||||||
import java.time.DayOfWeek;
|
import java.time.DayOfWeek;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -23,6 +31,10 @@ public class LiveInfoService {
|
|||||||
|
|
||||||
private final WebClient webClient;
|
private final WebClient webClient;
|
||||||
private final CKeyGenerator cKeyGenerator;
|
private final CKeyGenerator cKeyGenerator;
|
||||||
|
@Autowired
|
||||||
|
private GamesMapper gamesMapper;
|
||||||
|
@Autowired
|
||||||
|
private UrlsMapper urlsMapper;
|
||||||
|
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
@@ -69,9 +81,45 @@ public class LiveInfoService {
|
|||||||
.queryParam("tm", Instant.now().getEpochSecond())
|
.queryParam("tm", Instant.now().getEpochSecond())
|
||||||
.queryParam("cnlid", cnlid)
|
.queryParam("cnlid", cnlid)
|
||||||
.build())
|
.build())
|
||||||
.header("Cookie", "pgv_pvid=67939534; fqm_pvqid=5e0a1a80-2ffb-42c9-8878-99ef08c0bd21; RK=dudkRjOGUW; ptcz=d9e319494b15cb621d9f54a8e03a1b55fb6c6c3564ff6c4985d1ff7b4797ce4e; video_platform=2; qq_domain_video_guid_verify=119f9c981e9ffd34; _qimei_uuid42=184170b170810037e9eacb93c4fe35a57c7df28f38; _qimei_q36=; _qimei_h38=215914e0566ee066f810eb4b0200000e81840b; video_guid=119f9c981e9ffd34; tvfe_boss_uuid=d41b10b066856291; eas_sid=E1i7L1k4j4D7q1D0Q5J894p636; livelink_pvid=7900946432; check_16=584445b5d815fd331bb7206205f8c603; o2_uin=941039061; uin_cookie=o0941039061; ied_qq=o0941039061; o_cookie=941039061; pac_uid=0_ePEHB57t75p7c; LW_sid=31h7T2M8f3O5O280q0K0L365H2; LW_uid=P1J7P2i8S3R5a2r0j020t3n543; ptui_loginuin=1131302745; qq_nick=A; qq_head=https://tvpic.gtimg.cn/head/dbb128053992612c0c1327e8e32e95f8da39a3ee5e6b4b0d3255bfef95601890afd80709/346; vqq_refresh_token=; vqq_next_refresh_time=6505; vqq_login_time_init=1745154214; _qimei_fingerprint=829539b60aaa7ed28f0e5f604c391a63; main_login=qq; vqq_appid=101481799; vqq_openid=2E6A77E3002C87A9CA68908FB058D4E5; vqq_access_token=308C7CAFBC867AF65212034AC90E71D9; vqq_vuserid=3468482246; vqq_vusession=H1-ZxeRN0vDUVk4_wKLkyA.N")
|
.header("Cookie", "_qimei_q32=b6ff8aedc45c2d93874747a3e7f5e3dd; ptui_loginuin=1131302745; vqq_access_token=308C7CAFBC867AF65212034AC90E71D9; lcad_appuser=CCC4A35B6047F544; tvfe_boss_uuid=d41b10b066856291; _qimei_uuid42=184170b170810037e9eacb93c4fe35a57c7df28f38; LW_sid=31h7T2M8f3O5O280q0K0L365H2; o_cookie=941039061; lcad_LDERturn=503; last_nick=%E8%85%BE%E8%AE%AF%E7%BD%91%E5%8F%8B; uin_cookie=o0941039061; lcad_LPDFturn=43; spt_uid=1829817828670177361; RK=dudkRjOGUW; ied_qq=o0941039061; lcad_LPVLturn=470; lcad_o_minduid=OCcytZR6an_BFYcypXv6ZzuBX65okAHc; pgv_pvid=67939534; lcad_LVINturn=636; pgv_info=ssid=s6516917000; last_main_login=qq; eas_sid=E1i7L1k4j4D7q1D0Q5J894p636; fqm_pvqid=5e0a1a80-2ffb-42c9-8878-99ef08c0bd21; last_avatar=https://mat1.gtimg.com/sports/sportapp/default_profile_big_after_v1_4.png; lcad_LPPBturn=28; lcad_LPSJturn=135; livelink_pvid=7900946432; LW_uid=P1J7P2i8S3R5a2r0j020t3n543; o2_uin=941039061; pac_uid=0_ePEHB57t75p7c; ptcz=d9e319494b15cb621d9f54a8e03a1b55fb6c6c3564ff6c4985d1ff7b4797ce4e; qq_domain_video_guid_verify=119f9c981e9ffd34; vqq_appid=101481799; vqq_openid=2E6A77E3002C87A9CA68908FB058D4E5; vqq_vuserid=3468482246; _qimei_fingerprint=aaf3a442da56251083fbe4352a04dc72; _qimei_q36=09d3b683102573c400e0b201300015518b19; _qimei_h38=fdedada2b12cacb7c731c46802000004418b19; vqq_vusession=jjtKD6NC-SPkVdItUGl3Sg.N; ts_last=sports.qq.com/kbsweb/mycenter.htm; ts_uid=9292458684; ukey=174520991101356433; boss_user=loginType=1&nbaVipType=0&qq=&wxOpenId=2E6A77E3002C87A9CA68908FB058D4E5&wxAppId=101481799&uKey=174520991101356433; ptag=film_video_qq_com|; main_login=qq; spt_session=CLuygiIQ7MONwAYY8ZWXwAYg1wE; spt_token=roxqAw5zstnrHn4H7DMOSbTeSkCVC0JjQGPz6Lp5Jx1-rxtW6mfo-wgg8jq41xEi; next_refresh_time=1745216492421; bossv2_isvip=-1")
|
||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToMono(String.class)
|
.bodyToMono(String.class)
|
||||||
.block();
|
.block();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> extractPlayUrls(String liveInfoJson) throws Exception {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
LiveInfoResponse response = mapper.readValue(liveInfoJson, LiveInfoResponse.class);
|
||||||
|
|
||||||
|
List<String> urls = new ArrayList<>();
|
||||||
|
if (response.getPlay_list() != null) {
|
||||||
|
for (LiveInfoResponse.PlayItem item : response.getPlay_list()) {
|
||||||
|
if (item.getUrls() != null) {
|
||||||
|
urls.addAll(item.getUrls());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String refreshLiveInfo() {
|
||||||
|
List<Games> gamesList = gamesMapper.selectAll();
|
||||||
|
for (Games games : gamesList) {
|
||||||
|
String playId = games.getPlayId();
|
||||||
|
String liveInfo = getLiveInfo(playId);
|
||||||
|
log.info("获取到的直播信息: {}", liveInfo);
|
||||||
|
try {
|
||||||
|
List<String> urls = extractPlayUrls(liveInfo);
|
||||||
|
log.info("提取到的播放URL: {}", urls);
|
||||||
|
urlsMapper.updateUrlsWithGameId(games.getGameId(), urls.get(0));
|
||||||
|
// 这里可以保存urls到数据库或其他处理
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("解析直播信息失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "刷新完成";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.ping.study.model.vo.live.LiveInfoResponse;
|
||||||
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
@@ -30,6 +32,12 @@ public class TxSportTokenRefreshService {
|
|||||||
this.redisTemplate = redisTemplate;
|
this.redisTemplate = redisTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void initBaseCookies() {
|
||||||
|
String baseCookies = "pgv_pvid=67939534; fqm_pvqid=5e0a1a80-2ffb-42c9-8878-99ef08c0bd21; RK=dudkRjOGUW; ptcz=d9e319494b15cb621d9f54a8e03a1b55fb6c6c3564ff6c4985d1ff7b4797ce4e; video_platform=2; qq_domain_video_guid_verify=119f9c981e9ffd34; _qimei_uuid42=184170b170810037e9eacb93c4fe35a57c7df28f38; _qimei_q36=; _qimei_h38=215914e0566ee066f810eb4b0200000e81840b; video_guid=119f9c981e9ffd34; tvfe_boss_uuid=d41b10b066856291; eas_sid=E1i7L1k4j4D7q1D0Q5J894p636; livelink_pvid=7900946432; check_16=584445b5d815fd331bb7206205f8c603; _qimei_fingerprint=3c06d28104a1e802703470a164d81dca; ptui_loginuin=1131302745";
|
||||||
|
redisTemplate.opsForValue().set(REDIS_COOKIE_KEY, baseCookies, 30, TimeUnit.DAYS);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 刷新Cookie并存储到Redis
|
* 刷新Cookie并存储到Redis
|
||||||
*/
|
*/
|
||||||
@@ -37,11 +45,8 @@ public class TxSportTokenRefreshService {
|
|||||||
public boolean refreshCookies() {
|
public boolean refreshCookies() {
|
||||||
try {
|
try {
|
||||||
// 1. 从Redis获取基础登录态Cookie(首次需手动初始化)
|
// 1. 从Redis获取基础登录态Cookie(首次需手动初始化)
|
||||||
String baseCookie = redisTemplate.opsForValue().get("tx_base_cookies");
|
String baseCookie = redisTemplate.opsForValue().get(REDIS_COOKIE_KEY);
|
||||||
if (baseCookie == null) {
|
log.info("当前Cookie: {}", baseCookie);
|
||||||
baseCookie = "pgv_pvid=67939534; ptui_loginuin=1131302745; ..."; // 你的浏览器完整Cookie
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 携带基础Cookie发起请求
|
// 2. 携带基础Cookie发起请求
|
||||||
String response = webClient.get()
|
String response = webClient.get()
|
||||||
.uri("/init/refresh?os=web")
|
.uri("/init/refresh?os=web")
|
||||||
@@ -49,7 +54,6 @@ public class TxSportTokenRefreshService {
|
|||||||
.retrieve()
|
.retrieve()
|
||||||
.bodyToMono(String.class)
|
.bodyToMono(String.class)
|
||||||
.block();
|
.block();
|
||||||
|
|
||||||
// 3. 合并新旧Cookie(保留基础登录态)
|
// 3. 合并新旧Cookie(保留基础登录态)
|
||||||
JsonNode data = new ObjectMapper().readTree(response).path("data");
|
JsonNode data = new ObjectMapper().readTree(response).path("data");
|
||||||
String newCookie = extractNewCookies(data); // 提取spt_token等
|
String newCookie = extractNewCookies(data); // 提取spt_token等
|
||||||
@@ -57,7 +61,7 @@ public class TxSportTokenRefreshService {
|
|||||||
|
|
||||||
// 4. 存储到Redis
|
// 4. 存储到Redis
|
||||||
redisTemplate.opsForValue().set(
|
redisTemplate.opsForValue().set(
|
||||||
"tx_sports_cookies",
|
REDIS_COOKIE_KEY,
|
||||||
finalCookie,
|
finalCookie,
|
||||||
Long.parseLong(data.path("tokenTTL").asText()),
|
Long.parseLong(data.path("tokenTTL").asText()),
|
||||||
TimeUnit.SECONDS
|
TimeUnit.SECONDS
|
||||||
@@ -68,6 +72,27 @@ public class TxSportTokenRefreshService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 从腾讯API响应中提取新的Cookie信息
|
||||||
|
*/
|
||||||
|
private String extractNewCookies(JsonNode dataNode) {
|
||||||
|
if (dataNode == null || !dataNode.has("cookie")) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder cookieStr = new StringBuilder();
|
||||||
|
Iterator<JsonNode> cookies = dataNode.path("cookie").elements();
|
||||||
|
|
||||||
|
while (cookies.hasNext()) {
|
||||||
|
JsonNode cookie = cookies.next();
|
||||||
|
cookieStr.append(cookie.path("name").asText())
|
||||||
|
.append("=")
|
||||||
|
.append(cookie.path("value").asText())
|
||||||
|
.append("; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookieStr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 合并Cookie的工具方法
|
// 合并Cookie的工具方法
|
||||||
@@ -94,15 +119,19 @@ public class TxSportTokenRefreshService {
|
|||||||
/**
|
/**
|
||||||
* 定时刷新任务(每1小时执行一次)
|
* 定时刷新任务(每1小时执行一次)
|
||||||
*/
|
*/
|
||||||
// @Scheduled(fixedRate = 3600000) // 单位:毫秒
|
// @Scheduled(fixedRate = 3600000)
|
||||||
// public void scheduledRefresh() {
|
public void scheduledRefresh() {
|
||||||
// if (!refreshCookies()) {
|
if (!refreshCookies()) {
|
||||||
// log.warn("定时刷新Cookie失败,将重试...");
|
log.warn("首次刷新失败,尝试重新初始化基础Cookie...");
|
||||||
// // 失败后延迟5分钟重试
|
initBaseCookies();
|
||||||
// try {
|
// try {
|
||||||
// Thread.sleep(300000);
|
// Thread.sleep(30000); // 等待30秒后重试
|
||||||
// refreshCookies();
|
// if (!refreshCookies()) {
|
||||||
// } catch (InterruptedException ignored) {}
|
// log.error("重试刷新仍然失败");
|
||||||
// }
|
// }
|
||||||
// }
|
// } catch (InterruptedException e) {
|
||||||
|
// Thread.currentThread().interrupt();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -128,4 +128,5 @@
|
|||||||
<delete id="deleteAllGames" >
|
<delete id="deleteAllGames" >
|
||||||
delete from games
|
delete from games
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -94,4 +94,8 @@
|
|||||||
<delete id="deleteAllUrls">
|
<delete id="deleteAllUrls">
|
||||||
delete from urls
|
delete from urls
|
||||||
</delete>
|
</delete>
|
||||||
|
|
||||||
|
<update id="updateUrlsWithGameId" parameterType="map">
|
||||||
|
update urls set url = #{s},type = 'tx' where game_id = #{gameId}
|
||||||
|
</update>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user