播放页面Ui

This commit is contained in:
litingting 2024-09-25 10:31:03 +08:00
parent 7f065a1835
commit 96eecd07e7
18 changed files with 676 additions and 163 deletions

View File

@ -59,6 +59,8 @@ dependencies {
//----------media3
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
implementation("androidx.media3:media3-session:1.4.1")
implementation("androidx.media3:media3-ui:1.4.1")
//----------media3
}

View File

@ -0,0 +1,9 @@
package com.hi.music.player.api;
public interface MediaControllerListener {
void onMediaControllerComplete(boolean isOk);
void onPlayStatus(int playStatus);
}

View File

@ -62,4 +62,22 @@ public class CommonUtils {
}
return statusBarHeight;
}
//time 3:45
public static long convertToMilliseconds(String time) {
String[] parts = time.split(":");
int minutes = Integer.parseInt(parts[0]);
int seconds = Integer.parseInt(parts[1]);
return (minutes * 60 + seconds) * 1000; // 转换为毫秒
}
public static String convertMillisToTime(long millis) {
long seconds = (millis / 1000) % 60;
long minutes = (millis / (1000 * 60)) % 60;
return String.format("%d:%02d", minutes, seconds); // 格式化为 mm:ss
}
}

View File

@ -0,0 +1,39 @@
package com.hi.music.player.javabean.requestbody;
import com.hi.music.player.javabean.requestbody.child.ContextBody;
import java.io.Serializable;
/**
* 首页接口请求体
*/
public class BodyPlayUrl implements Serializable {
private String videoId ;
private ContextBody context = new ContextBody();
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public ContextBody getContext() {
return context;
}
public void setContext(ContextBody context) {
this.context = context;
}
}

View File

@ -7,6 +7,13 @@ public class ContextBody {
private Client client = new Client();
private ThirdParty thirdParty = new ThirdParty();
public ThirdParty getThirdParty() {
return thirdParty;
}
public Client getClient() {
return client;
}
@ -16,7 +23,7 @@ public class ContextBody {
}
public class Client implements Serializable {
public static class Client implements Serializable {
private String clientName = "WEB_REMIX";
private String clientVersion = "1.20220918";
private String hl = Locale.getDefault().getLanguage();
@ -32,5 +39,27 @@ public class ContextBody {
public void setVisitorData(String visitorData) {
this.visitorData = visitorData;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setClientVersion(String clientVersion) {
this.clientVersion = clientVersion;
}
public void setPlatform(String platform) {
this.platform = platform;
}
}
public static class ThirdParty{
//https://www.youtube.com/watch?v=UqyT8IEBkvY
private String embedUrl;
public void setEmbedUrl(String embedUrl) {
this.embedUrl = embedUrl;
}
}
}

View File

@ -16,7 +16,7 @@ public class ResponseHome {
private String continuation;
@Nullable
//用于更多数据请求的visitorData(只有第一个接口会返回该值)
//用于更多数据请求的visitorData(只有第一个接口会返回该值,youtubei/v1/browse?prettyPrint=false)
private String visitorData;
@Nullable

View File

@ -1,6 +1,6 @@
package com.hi.music.player.javabean.response;
public class ResponsePlay {
public class ResponsePlayListInfo {
//封面
private String covert;
@ -20,11 +20,22 @@ public class ResponsePlay {
private String Duration;
//歌曲时长 毫秒
private long DurationMs;
private String videoId;
private String playlistId;
private String params;
private String musicVideoType;
public long getDurationMs() {
return DurationMs;
}
public void setDurationMs(long durationMs) {
DurationMs = durationMs;
}
public String getDuration() {
return Duration;

View File

@ -0,0 +1,55 @@
package com.hi.music.player.javabean.response;
public class ResponsePlayUrl {
private String status;
private String audioUrlLow;
private String audioUrlMedium;
private String videoId;
public String getAudioUrlMedium() {
return audioUrlMedium;
}
public void setAudioUrlMedium(String audioUrlMedium) {
this.audioUrlMedium = audioUrlMedium;
}
///AUDIO_QUALITY_MEDIUMAUDIO_QUALITY_LOW
// private String audioQuality;
//
//
// public String getAudioQuality() {
// return audioQuality;
// }
//
// public void setAudioQuality(String audioQuality) {
// this.audioQuality = audioQuality;
// }
public String getVideoId() {
return videoId;
}
public void setVideoId(String videoId) {
this.videoId = videoId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getAudioUrlLow() {
return audioUrlLow;
}
public void setAudioUrlLow(String audioUrlLow) {
this.audioUrlLow = audioUrlLow;
}
}

View File

@ -0,0 +1,13 @@
package com.hi.music.player.media3;
import android.content.Context;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.ui.PlayerControlView;
@UnstableApi
public class MyControllerView extends PlayerControlView{
public MyControllerView(Context context) {
super(context);
}
}

View File

@ -2,16 +2,22 @@ package com.hi.music.player.media3;
import android.content.ComponentName;
import androidx.annotation.OptIn;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaController;
import androidx.media3.session.SessionToken;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.api.MediaControllerListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlay;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.network.RetrofitManager;
import java.util.ArrayList;
@ -34,7 +40,7 @@ public class MyMediaControllerManager {
return myMediaControllerManagerInstance;
}
public void init(){
public void init(MediaControllerListener mediaControllerListener){
SessionToken sessionToken =
new SessionToken(MusicApplication.myApplication, new ComponentName(MusicApplication.myApplication, PlaybackService.class));
ListenableFuture<MediaController> controllerFuture =
@ -46,8 +52,30 @@ public class MyMediaControllerManager {
// playerView.setPlayer(controllerFuture.get());
try {
mediaController = controllerFuture.get();
mediaController.addListener(new Player.Listener() {
@Override
public void onPlayerError(PlaybackException error) {
CommonUtils.LogMsg("=-----PlaybackException+"+error.getMessage());
}
@Override
public void onPlaybackStateChanged(int playbackState) {
CommonUtils.LogMsg("=-----playbackState+"+playbackState);
mediaControllerListener.onPlayStatus(playbackState);
}
@Override
public void onPositionDiscontinuity(Player.PositionInfo oldPosition, Player.PositionInfo newPosition, int reason) {
// 快进快退等操作
CommonUtils.LogMsg("=-----newPosition+"+newPosition.positionMs);
// mediaControllerListener.onPlayStatus(playbackState);
}
});
mediaControllerListener.onMediaControllerComplete(true);
CommonUtils.LogMsg("=-----mediaController+"+mediaController);
} catch (ExecutionException | InterruptedException e) {
CommonUtils.LogErrorMsg(e.getMessage());
mediaControllerListener.onMediaControllerComplete(false);
}
}, MoreExecutors.directExecutor());
@ -55,17 +83,33 @@ public class MyMediaControllerManager {
}
public void addMusicPlay(ResponsePlay responsePlay){
public MediaController getMediaController() {
return mediaController;
}
public long getContentPos() {
return mediaController.getContentPosition();
}
@OptIn(markerClass = UnstableApi.class)
public void addMusicPlay(ResponsePlayListInfo playInfo, ResponsePlayUrl responsePlay){
List<MediaItem> mediaItems = new ArrayList<>();
MediaItem.Builder builder = new MediaItem.Builder();
builder.setUri(responsePlay.getVideoId());
if(responsePlay.getAudioUrlMedium()!= null){
builder.setUri(responsePlay.getAudioUrlMedium());
}else {
builder.setUri(responsePlay.getAudioUrlLow());
}
builder.setMediaId(responsePlay.getVideoId());
MediaMetadata.Builder MediaMetadata_builder = new MediaMetadata.Builder();
// MediaMetadata_builder.setMediaType();
// MediaMetadata_builder.setAlbumTitle();
// MediaMetadata_builder.setArtist();
// MediaMetadata_builder.setDurationMs();
// MediaMetadata_builder.setTitle();
// MediaMetadata_builder.setRecordingYear();
MediaMetadata_builder.setArtist(playInfo.getSingerName());
// MediaMetadata_builder.setDurationMs( );
MediaMetadata_builder.setAlbumArtist(playInfo.getCovert());
MediaMetadata_builder.setTitle(playInfo.getSongTitle());
// MediaMetadata_builder.setRecordingYear(Integer.parseInt(playInfo.getYear()));
builder.setMediaMetadata(MediaMetadata_builder.build());
mediaItems.add(builder.build());
@ -76,4 +120,6 @@ public class MyMediaControllerManager {
public void play(){
mediaController.play();
}
}

View File

@ -1,27 +1,83 @@
package com.hi.music.player.media3;
import android.content.Intent;
import androidx.annotation.Nullable;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Player;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.session.MediaSession;
import androidx.media3.session.MediaSessionService;
import com.google.common.util.concurrent.ListenableFuture;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import java.util.ArrayList;
import java.util.List;
public class PlaybackService extends MediaSessionService {
private MediaSession mediaSession = null;
private ExoPlayer player;
@Override
public void onCreate() {
super.onCreate();
ExoPlayer player = new ExoPlayer.Builder(this).build();
mediaSession = new MediaSession.Builder(this, player).build();
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(AudioAttributes.DEFAULT.usage)
.setContentType(AudioAttributes.DEFAULT.contentType)
.build();
// MediaSource.Factory mediaSourceFactory =
// new DefaultMediaSourceFactory(context)
// .setDataSourceFactory(cacheDataSourceFactory)
// .setLocalAdInsertionComponents(adsLoaderProvider);
player = new ExoPlayer.Builder(this)
.setWakeMode(C.WAKE_MODE_LOCAL)
// .setMediaSourceFactory(mediaSourceFactory)
.setAudioAttributes(audioAttributes, true).build();
mediaSession = new MediaSession.Builder(this, player).setCallback(new MediaSession.Callback() {
@Override
public ListenableFuture<List<MediaItem>> onAddMediaItems(MediaSession mediaSession, MediaSession.ControllerInfo controller, List<MediaItem> mediaItems) {
CommonUtils.LogMsg("--------mediaItems="+mediaItems.get(0).mediaMetadata.title);
// player.addMediaItems(mediaItems);
return MediaSession.Callback.super.onAddMediaItems(mediaSession, controller, mediaItems);
}
})
.build();
}
@Nullable
@Override
public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
return mediaSession;
}
@Override
public void onTaskRemoved(@Nullable Intent rootIntent) {
Player player = mediaSession.getPlayer();
// if (player.getPlayWhenReady()) {
// // Make sure the service is not in foreground.
// player.pause();
// }
// stopSelf();
}
@Override
public void onDestroy() {
mediaSession.getPlayer().release();

View File

@ -1,7 +1,8 @@
package com.hi.music.player.network;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponsePlay;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.javabean.response.child.ResponseCategory;
import com.hi.music.player.javabean.response.ResponseHome;
import com.hi.music.player.javabean.response.child.ResponseHomeChild;
@ -21,20 +22,11 @@ public class JsonHelper {
public static ResponseHome ResolveHomeJson(JSONObject jsonObject) {
ResponseHome responseHome = new ResponseHome();
try {
String bgUrl = getJsonUrl(jsonObject
.getJSONObject("background"));
String bgUrl = getJsonUrl(jsonObject.getJSONObject("background"));
JSONObject sectionListRenderer = jsonObject
.getJSONObject("contents")
.getJSONObject("singleColumnBrowseResultsRenderer")
.getJSONArray("tabs")
.getJSONObject(0)
.getJSONObject("tabRenderer")
.getJSONObject("content")
.getJSONObject("sectionListRenderer");
JSONObject sectionListRenderer = jsonObject.getJSONObject("contents").getJSONObject("singleColumnBrowseResultsRenderer").getJSONArray("tabs").getJSONObject(0).getJSONObject("tabRenderer").getJSONObject("content").getJSONObject("sectionListRenderer");
JSONObject responseContext = jsonObject
.getJSONObject("responseContext");
JSONObject responseContext = jsonObject.getJSONObject("responseContext");
JSONArray serviceTrackingParams = responseContext.getJSONArray("serviceTrackingParams");
String visitorData = responseContext.getString("visitorData");
CommonUtils.LogMsg("---------参数-visitorData=" + visitorData);
@ -58,13 +50,10 @@ public class JsonHelper {
ResponseHome responseHome = new ResponseHome();
try {
JSONObject sectionListContinuation = jsonObject
.getJSONObject("continuationContents")
.getJSONObject("sectionListContinuation");
JSONObject sectionListContinuation = jsonObject.getJSONObject("continuationContents").getJSONObject("sectionListContinuation");
JSONObject responseContext = jsonObject
.getJSONObject("responseContext");
JSONObject responseContext = jsonObject.getJSONObject("responseContext");
JSONArray serviceTrackingParams = responseContext.getJSONArray("serviceTrackingParams");
getCommonHome(sectionListContinuation, responseHome);
@ -77,73 +66,10 @@ public class JsonHelper {
return responseHome;
}
public static List<ResponsePlay> ResolvePlayJson(JSONObject jsonObject) {
List<ResponsePlay> list = new ArrayList<>();
try {
JSONObject playlistPanelRenderer = jsonObject.getJSONObject("contents")
.getJSONObject("singleColumnMusicWatchNextResultsRenderer")
.getJSONObject("tabbedRenderer")
.getJSONObject("watchNextTabbedResultsRenderer")
.getJSONArray("tabs")
.getJSONObject(0)
.getJSONObject("tabRenderer")
.getJSONObject("content")
.getJSONObject("musicQueueRenderer")
.getJSONObject("content")
.getJSONObject("playlistPanelRenderer");
JSONArray contents = playlistPanelRenderer.getJSONArray("contents");
for (int i = 0; i < contents.length(); i++) {
ResponsePlay responsePlay = new ResponsePlay();
JSONObject playlistPanelVideoRenderer = contents.getJSONObject(i)
.getJSONObject("playlistPanelVideoRenderer");
String jsonUrl = getJsonUrl(playlistPanelVideoRenderer);
String songName = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("title"), 0);
JSONObject longBylineText = playlistPanelVideoRenderer.getJSONObject("longBylineText");
String singerName = getJsonTitle(longBylineText, 0);
String AlbumTitle = getJsonTitle(longBylineText, 2);
String YearRelease = getJsonTitle(longBylineText, 4);
String SongDuration = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("lengthText"), 0);
String[] watchEndPoint = getWatchEndPoint(playlistPanelVideoRenderer);
responsePlay.setCovert(jsonUrl);
responsePlay.setSongTitle(songName);
responsePlay.setSingerName(singerName);
responsePlay.setAlbumTitle(AlbumTitle);
responsePlay.setYear(YearRelease);
responsePlay.setDuration(SongDuration);
responsePlay.setVideoId(watchEndPoint[0]);
responsePlay.setPlaylistId(watchEndPoint[1]);
responsePlay.setParams(watchEndPoint[2]);
responsePlay.setMusicVideoType(watchEndPoint[3]);
list.add(responsePlay);
CommonUtils.LogMsg("----------歌曲名字=" + songName + "---" + Arrays.toString(watchEndPoint));
}
} catch (JSONException exception) {
CommonUtils.LogMsg("----------exception=");
exception.printStackTrace();
}
return list;
}
private static void getCommonHome(JSONObject sectionListRenderer, ResponseHome responseHome) throws JSONException {
JSONObject nextContinuationData = sectionListRenderer
.getJSONArray("continuations")
.getJSONObject(0)
.getJSONObject("nextContinuationData");
JSONObject nextContinuationData = sectionListRenderer.getJSONArray("continuations").getJSONObject(0).getJSONObject("nextContinuationData");
//token=continuation,cit= clickTrackingParams
@ -154,8 +80,7 @@ public class JsonHelper {
responseHome.setClickTrackingParams(clickTrackingParams);
responseHome.setContinuation(continuation);
JSONArray contents = sectionListRenderer
.getJSONArray("contents");
JSONArray contents = sectionListRenderer.getJSONArray("contents");
List<ResponseHomeChild> childList = new ArrayList<>();
@ -167,14 +92,10 @@ public class JsonHelper {
if (musicCarouselShelfRenderer != null) {
//模块标题
String title = getJsonTitle(musicCarouselShelfRenderer
.getJSONObject("header")
.getJSONObject("musicCarouselShelfBasicHeaderRenderer")
.getJSONObject("title"), 0);
String title = getJsonTitle(musicCarouselShelfRenderer.getJSONObject("header").getJSONObject("musicCarouselShelfBasicHeaderRenderer").getJSONObject("title"), 0);
CommonUtils.LogMsg("----------headertitle=" + title);
responseHomeChild.setHeaderTitle(title);
JSONArray childContents = musicCarouselShelfRenderer
.getJSONArray("contents");
JSONArray childContents = musicCarouselShelfRenderer.getJSONArray("contents");
List<ResponseCategory> categoryList = new ArrayList<>();
@ -185,8 +106,7 @@ public class JsonHelper {
if (musicResponsiveListItemRenderer != null) {
ResponseSingle responseSingle = new ResponseSingle();
String SingerHead = getJsonUrl(musicResponsiveListItemRenderer
.getJSONObject("thumbnail"));
String SingerHead = getJsonUrl(musicResponsiveListItemRenderer.getJSONObject("thumbnail"));
JSONArray flexColumns = musicResponsiveListItemRenderer.getJSONArray("flexColumns");
@ -196,16 +116,12 @@ public class JsonHelper {
String SingerName = "";
String Description = "";
for (int g = 0; g < flexColumns.length(); g++) {
JSONObject jsonObject = musicResponsiveListItemRenderer.getJSONArray("flexColumns")
.getJSONObject(g)
.getJSONObject("musicResponsiveListItemFlexColumnRenderer")
.getJSONObject("text");
JSONObject jsonObject = musicResponsiveListItemRenderer.getJSONArray("flexColumns").getJSONObject(g).getJSONObject("musicResponsiveListItemFlexColumnRenderer").getJSONObject("text");
String text = getJsonTitle(jsonObject, 0);
if (g == 0) {
SongTitle = text;
JSONObject runs = jsonObject.getJSONArray("runs")
.getJSONObject(0);
JSONObject runs = jsonObject.getJSONArray("runs").getJSONObject(0);
String[] watchEndPoint = getWatchEndPoint(runs);
@ -229,8 +145,7 @@ public class JsonHelper {
JSONObject musicTwoRowItemRenderer = jsonList.optJSONObject("musicTwoRowItemRenderer");
if (musicTwoRowItemRenderer != null) {
ResponseCategory responseCategory = new ResponseCategory();
String covert = getJsonUrl(musicTwoRowItemRenderer
.getJSONObject("thumbnailRenderer"));
String covert = getJsonUrl(musicTwoRowItemRenderer.getJSONObject("thumbnailRenderer"));
JSONObject title1 = musicTwoRowItemRenderer.getJSONObject("title");
String twoTitle = getJsonTitle(title1, 0);
@ -259,14 +174,107 @@ public class JsonHelper {
}
public static List<ResponsePlayListInfo> ResolvePlayListJson(JSONObject jsonObject) {
List<ResponsePlayListInfo> list = new ArrayList<>();
try {
JSONObject playlistPanelRenderer = jsonObject.getJSONObject("contents").getJSONObject("singleColumnMusicWatchNextResultsRenderer").getJSONObject("tabbedRenderer").getJSONObject("watchNextTabbedResultsRenderer").getJSONArray("tabs").getJSONObject(0).getJSONObject("tabRenderer").getJSONObject("content").getJSONObject("musicQueueRenderer").getJSONObject("content").getJSONObject("playlistPanelRenderer");
JSONArray contents = playlistPanelRenderer.getJSONArray("contents");
for (int i = 0; i < contents.length(); i++) {
ResponsePlayListInfo responsePlayListInfo = new ResponsePlayListInfo();
JSONObject playlistPanelVideoRenderer = contents.getJSONObject(i).getJSONObject("playlistPanelVideoRenderer");
String jsonUrl = getJsonUrl(playlistPanelVideoRenderer);
String songName = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("title"), 0);
JSONObject longBylineText = playlistPanelVideoRenderer.getJSONObject("longBylineText");
String singerName = getJsonTitle(longBylineText, 0);
String AlbumTitle = getJsonTitle(longBylineText, 2);
String YearRelease = getJsonTitle(longBylineText, 4);
String SongDuration = getJsonTitle(playlistPanelVideoRenderer.getJSONObject("lengthText"), 0);
long ms = CommonUtils.convertToMilliseconds(SongDuration);
String[] watchEndPoint = getWatchEndPoint(playlistPanelVideoRenderer);
responsePlayListInfo.setCovert(jsonUrl);
responsePlayListInfo.setSongTitle(songName);
responsePlayListInfo.setSingerName(singerName);
responsePlayListInfo.setAlbumTitle(AlbumTitle);
responsePlayListInfo.setYear(YearRelease);
responsePlayListInfo.setDuration(SongDuration);
responsePlayListInfo.setDurationMs(ms);
responsePlayListInfo.setVideoId(watchEndPoint[0]);
responsePlayListInfo.setPlaylistId(watchEndPoint[1]);
responsePlayListInfo.setParams(watchEndPoint[2]);
responsePlayListInfo.setMusicVideoType(watchEndPoint[3]);
list.add(responsePlayListInfo);
CommonUtils.LogMsg("----------歌曲名字=" + songName + "---" + Arrays.toString(watchEndPoint));
}
} catch (JSONException exception) {
CommonUtils.LogMsg("----------exception=");
exception.printStackTrace();
}
return list;
}
public static ResponsePlayUrl ResolvePlayUrlJson(JSONObject jsonObject) {
try {
ResponsePlayUrl responsePlayUrl = new ResponsePlayUrl();
String status = jsonObject.getJSONObject("playabilityStatus").getString("status");
JSONArray jsonArray = jsonObject.getJSONObject("streamingData").getJSONArray("adaptiveFormats");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonIndex = jsonArray.getJSONObject(i);
String mimeType = jsonIndex.getString("mimeType");
String itag = jsonIndex.getString("itag");
if (mimeType.contains("audio/mp4")) {
String url = jsonIndex.getString("url");
boolean audioQuality1 = jsonIndex.has("audioQuality");
CommonUtils.LogMsg("------------itag="+itag+"---------audioQuality1="+audioQuality1);
if (jsonIndex.has("audioQuality")) {
String audioQuality = jsonIndex.getString("audioQuality");
if (audioQuality.equals("AUDIO_QUALITY_MEDIUM")) {
responsePlayUrl.setAudioUrlMedium(url);
} else {
responsePlayUrl.setAudioUrlLow(url);
}
}
}
}
String videoId = jsonObject.getJSONObject("videoDetails").getString("videoId");
responsePlayUrl.setVideoId(videoId);
responsePlayUrl.setStatus(status);
return responsePlayUrl;
} catch (JSONException e) {
e.printStackTrace();
return null;
}
}
private static String[] getWatchEndPoint(JSONObject job) {
String[] strings = new String[4];
try {
JSONObject watchEndpoint = job
.getJSONObject("navigationEndpoint")
.getJSONObject("watchEndpoint");
JSONObject watchEndpoint = job.getJSONObject("navigationEndpoint").getJSONObject("watchEndpoint");
if (watchEndpoint.has("videoId")) {
strings[0] = watchEndpoint.getString("videoId");
}
@ -277,9 +285,7 @@ public class JsonHelper {
strings[2] = watchEndpoint.getString("params");
}
if (watchEndpoint.has("watchEndpointMusicSupportedConfigs")) {
strings[3] = watchEndpoint.getJSONObject("watchEndpointMusicSupportedConfigs")
.getJSONObject("watchEndpointMusicConfig")
.getString("musicVideoType");
strings[3] = watchEndpoint.getJSONObject("watchEndpointMusicSupportedConfigs").getJSONObject("watchEndpointMusicConfig").getString("musicVideoType");
}
// String videoId = watchEndpoint.getString("videoId");
// String playlistId = watchEndpoint.getString("playlistId");
@ -300,13 +306,10 @@ public class JsonHelper {
if (b) {
jsonObject = jsonObject.getJSONObject("musicThumbnailRenderer");
}
JSONArray jsonArray = jsonObject
.getJSONObject("thumbnail")
.getJSONArray("thumbnails");
JSONArray jsonArray = jsonObject.getJSONObject("thumbnail").getJSONArray("thumbnails");
int length = jsonArray.length();
CommonUtils.LogMsg("----------length=" + (length - 1));
String pngUrl = jsonArray.getJSONObject(length - 1)
.getString("url");
String pngUrl = jsonArray.getJSONObject(length - 1).getString("url");
return pngUrl;
} catch (JSONException exception) {
@ -320,9 +323,7 @@ public class JsonHelper {
try {
JSONArray runs = jsonObject.getJSONArray("runs");
if (index < runs.length()) {
text = runs
.getJSONObject(index)
.getString("text");
text = runs.getJSONObject(index).getString("text");
}
} catch (JSONException exception) {

View File

@ -4,6 +4,7 @@ import io.reactivex.Observable;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
@ -31,10 +32,25 @@ public interface MusicApi {
// X-Goog-FieldMask: contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer(continuations,contents(automixPreviewVideoRenderer,playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)))
//获取播放列表
@POST("youtubei/v1/next?prettyPrint=false")
@Headers("X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
Observable<ResponseBody> getMusicPlayPage(@Header("X-Goog-FieldMask") String customHeader, @Body RequestBody requestBody);
@POST("youtubei/v1/player?prettyPrint=false")
@Headers({"X-Goog-Api-Key:AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8",
"X-Goog-FieldMask:playabilityStatus.status,playerConfig.audioConfig,streamingData.adaptiveFormats,videoDetails.videoId"})
Observable<ResponseBody> getMusicPlayUrl(@Body RequestBody requestBody);
@GET("videoplayback?expire=1727100941&ei=rSPxZoSXJ_eM2_gPgqT8mAs&ip=146.19.167.8&id=o-ADGacaLEhb3NOOc74tfR50VCTKy0vnUb2_GAm-tPlv9n&itag=137&source=youtube&requiressl=yes&xpc=EgVo2aDSNQ%3D%3D&mh=f8&mm=31%2C29&mn=sn-tt1e7nlz%2Csn-vgqsknzd&ms=au%2Crdu&mv=m&mvi=2&pl=24&gcr=us&initcwndbps=6450000&vprv=1&svpuc=1&mime=video%2Fmp4&rqh=1&gir=yes&clen=4444925&dur=293.280&lmt=1688643558849956&mt=1727078942&fvip=3&keepalive=yes&fexp=51299152&c=ANDROID_MUSIC&txp=4532434&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cgcr%2Cvprv%2Csvpuc%2Cmime%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&sig=AJfQdSswRgIhAKjjifMN7NMLqeoVXyqHPK1uHqev1PcnVMoycknt4QGfAiEAiCEcEYPDpQsCbE0tJ6MXjvPs4HmT0yM8Yoa26rWpc7M%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=ABPmVW0wRAIgeur5lMiKDgdWV5rrRTkmt0jbOQnifmVQwoTXk_Y17E0CIBfjGXpbdW2u3mtu1I-")
Observable<ResponseBody> getTest();
}

View File

@ -2,12 +2,10 @@ package com.hi.music.player.network;
import com.google.gson.Gson;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.requestbody.BodyHome;
import com.hi.music.player.javabean.requestbody.BodyPlay;
import com.hi.music.player.javabean.response.ResponseHome;
import org.json.JSONObject;
import com.hi.music.player.javabean.requestbody.BodyPlayUrl;
import com.hi.music.player.javabean.requestbody.child.ContextBody;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
@ -26,6 +24,8 @@ import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitManager {
private String base_Host = "https://music.youtube.com/";
private String base_Host_test = "https://rr2---sn-tt1e7nlz.googlevideo.com/";
private static volatile RetrofitManager REQUEST_MANAGER;
private Retrofit retrofit;
@ -110,7 +110,7 @@ public class RetrofitManager {
}
public void getNext(String params,String playlistId,String videoId,String musicVideoType,RequestListener<ResponseBody> requestListener) {
public void getPlayList(String params, String playlistId, String videoId, String musicVideoType, RequestListener<ResponseBody> requestListener) {
BodyPlay bodyPlay = new BodyPlay();
bodyPlay.setParams(params);
bodyPlay.setPlaylistId(playlistId);
@ -125,4 +125,29 @@ public class RetrofitManager {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<ResponseBody>(requestListener));
}
public void getPlayUrl(String videoId,RequestListener<ResponseBody> requestListener) {
BodyPlayUrl bodyPlay = new BodyPlayUrl();
bodyPlay.setVideoId(videoId);
ContextBody.Client client = bodyPlay.getContext().getClient();
client.setClientName("ANDROID_MUSIC");
client.setClientVersion("5.28.1");
client.setPlatform("MOBILE");
bodyPlay.getContext().getThirdParty().setEmbedUrl("https://www.youtube.com/watch?v="+videoId);
Gson gson = new Gson();
String s = gson.toJson(bodyPlay);
RequestBody requestBody = RequestBody.Companion.create(s, JSON);
musicApi.getMusicPlayUrl(requestBody)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverWrapper<ResponseBody>(requestListener));
}
}

View File

@ -2,11 +2,17 @@ package com.hi.music.player.ui.activity;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.Observer;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaController;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
@ -17,10 +23,12 @@ import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
import com.hi.music.player.MusicApplication;
import com.hi.music.player.R;
import com.hi.music.player.api.MediaControllerListener;
import com.hi.music.player.databinding.ActivityPlayBinding;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.helper.MyValue;
import com.hi.music.player.javabean.response.ResponsePlay;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.javabean.response.child.ResponseSingle;
import com.hi.music.player.media3.MyMediaControllerManager;
import com.hi.music.player.ui.activity.viewmodel.VMPlay;
@ -32,7 +40,13 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> {
private ResponseSingle responseSingle;
private VMPlay vmPlay;
private MyMediaControllerManager mediaController;
private List<ResponsePlayListInfo> mPlayList;
private ResponsePlayUrl mCurPlayInfo;
private ResponsePlayListInfo musicInfo;
private Handler mHandler;
private Runnable mRunnable;
private MyMediaControllerManager mediaControllerManager;
@Override
protected ActivityPlayBinding getViewBinding() {
@ -44,21 +58,78 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> {
Intent intent = getIntent();
responseSingle = (ResponseSingle) intent.getSerializableExtra(MyValue.KEY_PLAY_ACTIVITY_SINGER);
vmPlay = getActivityScopeViewModel(VMPlay.class);
vmPlay.getPlay(responseSingle);
vmPlay.getPlayUrl(responseSingle);
vmPlay.getPlayMusicList(responseSingle);
initPlayerView();
initMediaController();
vmPlay.data.observe(this, new Observer<List<ResponsePlay>>() {
vmPlay.data.observe(this, new Observer<List<ResponsePlayListInfo>>() {
@Override
public void onChanged(List<ResponsePlay> playList) {
public void onChanged(List<ResponsePlayListInfo> playList) {
if (playList.size() > 0) {
ResponsePlay responsePlay = playList.get(0);
loadCovert(responsePlay.getCovert());
loadInfo(responsePlay);
mPlayList = playList;
musicInfo = playList.get(0);
loadCovert(musicInfo.getCovert());
loadInfo(musicInfo);
startPlayMusic();
}
}
});
vmPlay.playUrlLiveData.observe(this, new Observer<ResponsePlayUrl>() {
@Override
public void onChanged(ResponsePlayUrl responsePlayUrl) {
mCurPlayInfo = responsePlayUrl;
CommonUtils.LogMsg("---------mCurPlayInfo=" + mCurPlayInfo.getAudioUrlLow());
startPlayMusic();
}
});
}
@OptIn(markerClass = UnstableApi.class)
private void initPlayerView() {
vb.playerView.setShowRewindButton(false);
vb.playerView.setShowPreviousButton(false);
vb.playerView.setDefaultArtwork(ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
}
private void initMediaController() {
mediaControllerManager = MyMediaControllerManager.getInstance();
mediaControllerManager.init(new MediaControllerListener() {
@Override
public void onMediaControllerComplete(boolean isOk) {
MediaController mediaController1 = mediaControllerManager.getMediaController();
vb.playerView.setPlayer(mediaController1);
startPlayMusic();
}
@Override
public void onPlayStatus(int playStatus) {
switch (playStatus) {
case Player.STATE_READY:
mHandler.post(mRunnable);
vb.progressBar.setVisibility(View.GONE);
break;
case Player.STATE_ENDED:
mHandler.removeCallbacks(mRunnable); // 停止更新
break;
case Player.STATE_BUFFERING:
break;
}
}
});
}
@Override
@ -66,14 +137,49 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> {
vb.btnPlay.setOnClickListener(this::onClick);
}
private void loadInfo(ResponsePlay data) {
/**
* 更新播放进度Ui
*/
private void updatePlaybackProgress() {
// 获取当前播放位置
long contentPos = mediaControllerManager.getContentPos();
long currentPosition = mediaControllerManager.getMediaController().getCurrentPosition();
long duration = mediaControllerManager.getMediaController().getContentDuration();
long durationMs = musicInfo.getDurationMs();
String s = CommonUtils.convertMillisToTime(contentPos);
CommonUtils.LogMsg("---------播放进度-----contentPos=" + contentPos+"----currentPosition="+currentPosition+"------duration="+duration);
vb.tvCurrent.setText(s);
vb.seekbar.setValue(contentPos);
}
/**
* 初始化当前播放歌曲信息
* @param data
*/
private void loadInfo(ResponsePlayListInfo data) {
vb.tvSongName.setText(data.getSongTitle());
vb.tvSingerName.setText(data.getSingerName());
vb.tvDuration.setText(data.getDuration());
vb.seekbar.setValueTo(data.getDurationMs());
}
private void startPlayMusic() {
initProgressHandler();
CommonUtils.LogMsg("00000");
MediaController mediaController = mediaControllerManager.getMediaController();
if (mCurPlayInfo != null && mediaController != null && mPlayList != null) {
mediaControllerManager.addMusicPlay(mPlayList.get(0), mCurPlayInfo);
mediaControllerManager.play();
CommonUtils.LogMsg("111");
}
mediaController = MyMediaControllerManager.getInstance();
mediaController.init();
mediaController.addMusicPlay(data);
}
private void loadCovert(String url) {
@ -89,8 +195,11 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> {
return false;
}
@OptIn(markerClass = UnstableApi.class)
@Override
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
vb.playerView.setDefaultArtwork(resource);
return false;
}
})
@ -98,11 +207,16 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> {
}
@Override
public void onStart() {
super.onStart();
private void initProgressHandler() {
mHandler = new Handler();
mRunnable = new Runnable() {
@Override
public void run() {
updatePlaybackProgress();
// 继续定时更新
mHandler.postDelayed(this, 1000); // 每秒更新一次
}
};
}
@Override
@ -117,9 +231,16 @@ public class PlayActivity extends BaseActivity<ActivityPlayBinding> {
@Override
public void onClick(View v) {
if(v.equals(vb.btnPlay)){
mediaController.play();
if (v.equals(vb.btnPlay)) {
vb.btnPlay.setSelected(!vb.btnPlay.isSelected());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHandler != null && mRunnable != null)
mHandler.removeCallbacks(mRunnable);
}
}

View File

@ -6,8 +6,8 @@ import androidx.lifecycle.ViewModel;
import com.hi.music.player.api.RequestListener;
import com.hi.music.player.helper.CommonUtils;
import com.hi.music.player.javabean.response.ResponseHome;
import com.hi.music.player.javabean.response.ResponsePlay;
import com.hi.music.player.javabean.response.ResponsePlayListInfo;
import com.hi.music.player.javabean.response.ResponsePlayUrl;
import com.hi.music.player.javabean.response.child.ResponseSingle;
import com.hi.music.player.network.JsonHelper;
import com.hi.music.player.network.RetrofitManager;
@ -21,18 +21,22 @@ import okhttp3.ResponseBody;
public class VMPlay extends ViewModel {
private MutableLiveData<List<ResponsePlay>> _data = new MutableLiveData<List<ResponsePlay>>();
public LiveData<List<ResponsePlay>> data = _data;
private MutableLiveData<List<ResponsePlayListInfo>> _data = new MutableLiveData<List<ResponsePlayListInfo>>();
public LiveData<List<ResponsePlayListInfo>> data = _data;
private MutableLiveData<ResponsePlayUrl> _playUrlMutableLiveData = new MutableLiveData<ResponsePlayUrl>();
public LiveData<ResponsePlayUrl> playUrlLiveData = _playUrlMutableLiveData;
private String continuation, clickTrackingParams, visitorData;
public void getPlay(ResponseSingle responseSingle) {
public void getPlayMusicList(ResponseSingle responseSingle) {
String playlistId = responseSingle.getPlaylistId();
String videoId = responseSingle.getVideoId();
String params = responseSingle.getParams();
String musicVideoType = responseSingle.getMusicVideoType();
RetrofitManager.getInstance().getNext(params, playlistId, videoId, musicVideoType, new RequestListener<ResponseBody>() {
RetrofitManager.getInstance().getPlayList(params, playlistId, videoId, musicVideoType, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
@ -43,8 +47,8 @@ public class VMPlay extends ViewModel {
public void onSuccess(ResponseBody data) {
JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (jsonObject != null) {
List<ResponsePlay> responsePlays = JsonHelper.ResolvePlayJson(jsonObject);
_data.setValue(responsePlays);
List<ResponsePlayListInfo> responsePlayListInfos = JsonHelper.ResolvePlayListJson(jsonObject);
_data.setValue(responsePlayListInfos);
} else {
_data.setValue(null);
}
@ -53,5 +57,28 @@ public class VMPlay extends ViewModel {
});
}
public void getPlayUrl(ResponseSingle responseSingle) {
String videoId = responseSingle.getVideoId();
RetrofitManager.getInstance().getPlayUrl(videoId, new RequestListener<ResponseBody>() {
@Override
public void onFail(String errorMsg) {
_playUrlMutableLiveData.setValue(null);
}
@Override
public void onSuccess(ResponseBody data) {
JSONObject jsonObject = CommonUtils.toJsonObject(data);
if (jsonObject != null) {
ResponsePlayUrl responsePlayUrl = JsonHelper.ResolvePlayUrlJson(jsonObject);
_playUrlMutableLiveData.setValue(responsePlayUrl);
} else {
_playUrlMutableLiveData.setValue(null);
}
}
});
}
}

View File

@ -54,9 +54,11 @@ public class HomeFragment extends BaseFragment<FragmentHomeBinding> implements C
vmHome.getHome();
vmHome.data.observe(getViewLifecycleOwner(), new Observer<ResponseHome>() {
@Override
public void onChanged(ResponseHome responseHome) {
if(responseHome == null)return;
List<ResponseHomeChild> childList1 = responseHome.getChildList();
childList.addAll(childList1);
adapterHome.removeLoadingFooter();

View File

@ -31,6 +31,32 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/im_back" />
<androidx.media3.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:show_buffering="when_playing"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/im_back"
app:show_shuffle_button="true"/>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="@id/im_covert"
app:layout_constraintLeft_toLeftOf="@id/im_covert"
android:indeterminateTint="@color/white"
android:id="@+id/progressBar"
android:visibility="invisible"
app:layout_constraintRight_toRightOf="@id/im_covert"
app:layout_constraintTop_toTopOf="@id/im_covert" />
<TextView
android:id="@+id/tv_song_name"
android:layout_width="wrap_content"
@ -53,10 +79,24 @@
app:layout_constraintLeft_toLeftOf="@id/tv_song_name"
app:layout_constraintTop_toBottomOf="@id/tv_song_name" />
<SeekBar
<com.google.android.material.slider.Slider
android:id="@+id/seekbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:valueFrom="0"
app:layout_constraintLeft_toLeftOf="@id/im_covert"
app:layout_constraintRight_toRightOf="@id/im_covert"
app:layout_constraintTop_toBottomOf="@id/tv_singer_name"
app:labelBehavior="gone"
app:thumbColor="@color/white"
app:thumbRadius="6dp"
app:trackColorActive="@color/white"
app:trackColorInactive="#00000000"
app:trackHeight="3dp" />
<SeekBar
android:id="@+id/seekbar1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:maxHeight="3dp"
android:paddingStart="0dp"
@ -64,6 +104,7 @@
android:paddingEnd="0dp"
android:paddingRight="0dp"
android:progress="10"
android:visibility="gone"
android:progressDrawable="@drawable/seekbar_progress_drawable"
android:thumb="@drawable/seekbar_thumb"
app:layout_constraintLeft_toLeftOf="@id/im_covert"
@ -131,4 +172,6 @@
app:layout_constraintLeft_toLeftOf="@id/im_covert" />
</androidx.constraintlayout.widget.ConstraintLayout>