feat(playback): 播放票据返回各清晰度地址
This commit is contained in:
1 parent
ab58641bbe
commit
5a80e6693f
4 files changed
+68
No files matched your search
+25
@@ -5,6 +5,7 @@ import com.fruitshorts.content.dal.entity.Drama;
|
||||
import com.fruitshorts.content.dal.entity.DramaEpisode;
|
||||
import com.fruitshorts.content.service.DramaService;
|
||||
import com.fruitshorts.content.service.EpisodeService;
|
||||
import com.fruitshorts.media.dal.entity.MediaRendition;
|
||||
import com.fruitshorts.media.service.PlaybackSigner;
|
||||
import com.fruitshorts.media.service.ThumbnailUrlResolver;
|
||||
import com.fruitshorts.media.service.TranscodeService;
|
||||
@@ -147,6 +148,30 @@ class PlaybackServiceTest {
|
||||
verify(signer).sign("/files/episode/15/master.m3u8");
|
||||
}
|
||||
|
||||
@Test
|
||||
void should_sign_each_ready_rendition_when_episode_has_multiple_qualities() {
|
||||
DramaEpisode episode = episode(16L, 1, 0, "/files/episode/origin.mp4");
|
||||
episode.setHlsUrl("/files/transcoded/16/master.m3u8");
|
||||
given(episodeService.getPublished(16L)).willReturn(episode);
|
||||
given(signer.sign(anyString())).willAnswer(invocation -> invocation.getArgument(0) + "?s=1");
|
||||
MediaRendition ready = new MediaRendition();
|
||||
ready.setQuality("720p");
|
||||
ready.setPlayUrl("/files/transcoded/16/720p/index.m3u8");
|
||||
ready.setStatus("ready");
|
||||
MediaRendition failed = new MediaRendition();
|
||||
failed.setQuality("1080p");
|
||||
failed.setPlayUrl("/files/transcoded/16/1080p/index.m3u8");
|
||||
failed.setStatus("failed");
|
||||
given(transcodeService.listRenditions(16L)).willReturn(List.of(ready, failed));
|
||||
|
||||
PlayRespVO resp = playbackService.play(null, 16L, "device-9", "203.0.113.8");
|
||||
|
||||
assertThat(resp.getTracks()).extracting(track -> track.getQuality())
|
||||
.containsExactly("AUTO", "720p");
|
||||
assertThat(resp.getTracks().get(1).getPlayUrl())
|
||||
.isEqualTo("/files/transcoded/16/720p/index.m3u8?s=1");
|
||||
}
|
||||
|
||||
private DramaEpisode episode(Long id, int isFree, int unlockCoin, String videoUrl) {
|
||||
DramaEpisode episode = new DramaEpisode();
|
||||
episode.setId(id);
|
||||
|
||||
+25
@@ -13,6 +13,7 @@ import com.fruitshorts.media.service.TranscodeService;
|
||||
import com.fruitshorts.playback.dal.entity.PlaybackTicket;
|
||||
import com.fruitshorts.playback.dal.mapper.PlaybackTicketMapper;
|
||||
import com.fruitshorts.playback.vo.PlayRespVO;
|
||||
import com.fruitshorts.playback.vo.PlayTrackVO;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -87,6 +88,7 @@ public class PlaybackService {
|
||||
vo.setAccessible(true);
|
||||
vo.setAccessType(entitled && !free ? ACCESS_ENTITLED : ACCESS_FREE);
|
||||
vo.setPlayUrl(signedUrl);
|
||||
vo.setTracks(tracksOf(episode.getId(), signedUrl));
|
||||
|
||||
if (userId != null) {
|
||||
recordTicket(userId, drama.getId(), episode.getId(), signedUrl, deviceId, clientIp);
|
||||
@@ -115,6 +117,29 @@ public class PlaybackService {
|
||||
return qualities;
|
||||
}
|
||||
|
||||
/** AUTO 用主列表,其余档位用各自的 index.m3u8,地址都签名。 */
|
||||
private List<PlayTrackVO> tracksOf(Long episodeId, String signedAuto) {
|
||||
List<PlayTrackVO> tracks = new ArrayList<>();
|
||||
tracks.add(track("AUTO", signedAuto));
|
||||
for (MediaRendition rendition : transcodeService.listRenditions(episodeId)) {
|
||||
if (!StringUtils.hasText(rendition.getPlayUrl())) {
|
||||
continue;
|
||||
}
|
||||
if (!"ready".equalsIgnoreCase(rendition.getStatus())) {
|
||||
continue;
|
||||
}
|
||||
tracks.add(track(rendition.getQuality(), signer.sign(rendition.getPlayUrl())));
|
||||
}
|
||||
return tracks;
|
||||
}
|
||||
|
||||
private PlayTrackVO track(String quality, String playUrl) {
|
||||
PlayTrackVO track = new PlayTrackVO();
|
||||
track.setQuality(quality);
|
||||
track.setPlayUrl(playUrl);
|
||||
return track;
|
||||
}
|
||||
|
||||
private void recordTicket(Long userId, Long dramaId, Long episodeId, String playUrl,
|
||||
String deviceId, String clientIp) {
|
||||
PlaybackTicket ticket = new PlaybackTicket();
|
||||
|
||||
@@ -23,6 +23,8 @@ public class PlayRespVO {
|
||||
private String playUrl;
|
||||
/** 可用的清晰度档位 */
|
||||
private List<String> qualities;
|
||||
/** 各档签名地址。AUTO 是主播放列表,其余是单档 HLS。 */
|
||||
private List<PlayTrackVO> tracks;
|
||||
/** 雪碧图 WebVTT 时间轴地址;没有产物时为 null */
|
||||
private String thumbnailsUrl;
|
||||
/** 需要多少金币解锁 */
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
package com.fruitshorts.playback.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/** 一档可切换的清晰度及其签名播放地址。 */
|
||||
@Data
|
||||
public class PlayTrackVO {
|
||||
|
||||
/** AUTO 或 360p/480p/720p/1080p。 */
|
||||
private String quality;
|
||||
|
||||
/** 签名后的播放地址,客户端原样使用。 */
|
||||
private String playUrl;
|
||||
}
|
||||
Reference in new issue
Block a user