This commit is contained in:
ocean 2024-05-16 11:37:58 +08:00
parent 8c4629497c
commit 9b91074eeb
20 changed files with 789 additions and 110 deletions

View File

@ -62,6 +62,9 @@
<activity
android:name=".activity.MoSearchMoreActivity"
android:screenOrientation="portrait" />
<activity
android:name=".activity.MoSingerMoreSongActivity"
android:screenOrientation="portrait" />
<service
android:name=".service.PlaybackService"

View File

@ -3,19 +3,19 @@ package com.player.musicoo.activity
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.Drawable
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import android.view.LayoutInflater
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
import com.player.musicoo.R
import com.player.musicoo.media.MediaControllerManager
import com.player.musicoo.sp.AppStore
@ -137,15 +137,20 @@ abstract class MoBaseActivity : AppCompatActivity(), CoroutineScope by MainScope
title.text = getString(R.string.description)
val content = dialogView.findViewById<TextView>(R.id.dialog_content)
content.text = description
val okBtn = dialogView.findViewById<TextView>(R.id.dialog_ok_btn)
val close = dialogView.findViewById<RelativeLayout>(R.id.closeBtn)
val dialogBuilder = AlertDialog.Builder(this)
.setView(dialogView)
val dialog = dialogBuilder.create()
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.show()
okBtn.setOnClickListener {
close.setOnClickListener {
dialog.dismiss()
}
}
fun extractTextBeforeNewline(text: String): String {
// 用换行符分割文本,取第一个部分
return text.split("\n\n")[0]
}
}

View File

@ -76,22 +76,28 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
// 处理来自 PrimaryActivity 的情况
updateCurrentMediaItemInfo()
} else {
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC)
if (meController != null && meController.currentMediaItem != null && videoId == meController.currentMediaItem?.mediaId) {
//进入的id与当前的id一样就不重新去获取播放
updateCurrentMediaItemInfo()
} else {
binding.nameTv.text = intent.getStringExtra(PLAY_DETAILS_NAME)
binding.descTv.text = intent.getStringExtra(PLAY_DETAILS_DESC)
if (videoId.isNullOrEmpty()) {
finish()
return
if (videoId.isNullOrEmpty()) {
finish()
return
}
//传入进来的ID就是进入此界面的当前ID
currentVideoID = videoId
//根据进来界面的当前ID来获取资源。
initData(
videoId,
playlistId,
playlistSetVideoId,
params
)
}
//传入进来的ID就是进入此界面的当前ID
currentVideoID = videoId
//根据进来界面的当前ID来获取资源。
initData(
videoId,
playlistId,
playlistSetVideoId,
params
)
}
onReceive()
@ -130,7 +136,8 @@ class MoPlayDetailsActivity : MoBaseActivity(), Player.Listener {
binding.playbackErrorLayout.visibility = View.GONE
binding.loadingView.visibility = View.GONE
binding.disableClicksLayout.visibility = View.GONE
val currentString = convertMillisToMinutesAndSecondsString(MediaControllerManager.getCurrentPosition())
val currentString =
convertMillisToMinutesAndSecondsString(MediaControllerManager.getCurrentPosition())
binding.progressDurationTv.text = currentString
if (MediaControllerManager.getDuration() > 0) {
binding.totalDurationTv.visibility = View.VISIBLE

View File

@ -83,8 +83,11 @@ class MoSearchMoreActivity : MoBaseActivity() {
}
Request.MoreData -> {
LogD(TAG, "Request.MoreData")
currentContinuation?.let { it1 -> initDataMore(it1) }
if (!currentContinuation.isNullOrEmpty()) {
initDataMore(currentContinuation!!)
}else{
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}
}
}
@ -138,8 +141,10 @@ class MoSearchMoreActivity : MoBaseActivity() {
@SuppressLint("NotifyDataSetChanged")
private suspend fun initDataMore(continuation: String) {
Innertube.moSearchPage(ContinuationBody(continuation = continuation))?.onSuccess { result ->
LogD(TAG, "initDataMore result->$result")
currentContinuation = result.continuation
if (currentContinuation.isNullOrEmpty()) {
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
if (result.searchResultList.isNotEmpty()) {
list.addAll(result.searchResultList)
if (!isFinishing) {
@ -147,10 +152,10 @@ class MoSearchMoreActivity : MoBaseActivity() {
}
binding.refreshLayout.finishLoadMore(true)
} else {
binding.refreshLayout.finishLoadMore(2000, true, false)
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}?.onFailure {
binding.refreshLayout.finishLoadMore(2000, false, false)
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}

View File

@ -1,15 +1,10 @@
package com.player.musicoo.activity
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import com.gyf.immersionbar.ktx.immersionBar
import com.player.musicoo.adapter.DetailsListAdapter
import com.player.musicoo.databinding.ActivityDetailsBinding
import com.player.musicoo.databinding.ActivitySingerDetailsBinding
import com.player.musicoo.fragment.MoHomeFragment
import com.player.musicoo.innertube.Innertube
import com.player.musicoo.innertube.requests.moPlaylistPage
import com.player.musicoo.innertube.requests.moSingerListPage
import com.player.musicoo.util.LogTag.LogD
import com.player.musicoo.view.SingerDetailsOtherView
@ -75,6 +70,9 @@ class MoSingerDetailsActivity : MoBaseActivity() {
binding.tryAgainBtn.setOnClickListener {
requests.trySend(Request.TryAgain)
}
binding.singerDescExpand.setOnClickListener {
showSongDescriptionDialog(binding.singerDesc.text.toString())
}
}
private suspend fun initData(browseId: String) {
@ -86,7 +84,14 @@ class MoSingerDetailsActivity : MoBaseActivity() {
.load(it.thumbnail)
.into(binding.singerImg)
binding.singerName.text = it.title
binding.singerDesc.text = it.description
if (it.description.isNullOrEmpty()) {
binding.singerDesc.visibility = View.GONE
binding.singerDescExpand.visibility = View.GONE
} else {
binding.singerDesc.visibility = View.VISIBLE
binding.singerDescExpand.visibility = View.VISIBLE
binding.singerDesc.text = extractTextBeforeNewline(it.description)
}
if (it.list != null) {
for (bean: Innertube.SingerDetailsListPage in it.list) {
if (bean.contents?.musicShelfContentList != null && bean.contents.musicShelfContentList.isNotEmpty()) {

View File

@ -0,0 +1,170 @@
package com.player.musicoo.activity
import android.annotation.SuppressLint
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.gyf.immersionbar.ktx.immersionBar
import com.player.musicoo.adapter.SingerMoreSongAdapter
import com.player.musicoo.databinding.ActivitySearchMoreBinding
import com.player.musicoo.innertube.Innertube
import com.player.musicoo.innertube.models.bodies.BrowseBody
import com.player.musicoo.innertube.models.bodies.ContinuationBody
import com.player.musicoo.innertube.requests.moSingerDetailsMoreLoadMorePage
import com.player.musicoo.innertube.requests.moSingerDetailsMorePage
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
class MoSingerMoreSongActivity : MoBaseActivity() {
private val requests: Channel<Request> = Channel(Channel.UNLIMITED)
enum class Request {
TryAgain,
MoreData,
}
companion object {
const val SINGER_MORE_SONG_BROWSE_ID = "singer_more_song_browse_id"
const val SINGER_MORE_SONG_PARAMS = "singer_more_song_params"
const val SINGER_MORE_TYPE = "singer_more_type"
}
private lateinit var binding: ActivitySearchMoreBinding
private var browseId: String? = null
private var params: String? = null
private var type: String? = null
private var list: MutableList<Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults> =
mutableListOf()
private var currentContinuation: String? = null
private var adapter: SingerMoreSongAdapter? = null
override suspend fun main() {
binding = ActivitySearchMoreBinding.inflate(layoutInflater)
setContentView(binding.root)
browseId = intent.getStringExtra(SINGER_MORE_SONG_BROWSE_ID)
params = intent.getStringExtra(SINGER_MORE_SONG_PARAMS)
type = intent.getStringExtra(SINGER_MORE_TYPE)
if (browseId.isNullOrEmpty() || params.isNullOrEmpty()) {
return
}
initImmersionBar()
initView()
initAdapter()
initData(browseId!!, params!!)
onReceive()
}
private fun initImmersionBar() {
immersionBar {
statusBarDarkFont(false)
statusBarView(binding.view)
}
}
private suspend fun onReceive() {
while (isActive) {
select<Unit> {
requests.onReceive {
when (it) {
Request.TryAgain -> {
initData(browseId!!, params!!)
}
Request.MoreData -> {
if (!currentContinuation.isNullOrEmpty()) {
initDataMore(currentContinuation!!)
}else{
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}
}
}
}
}
}
private fun initView() {
binding.backBtn.setOnClickListener {
finish()
}
binding.tryAgainBtn.setOnClickListener {
requests.trySend(Request.TryAgain)
}
binding.refreshLayout.setEnableRefresh(false)
binding.refreshLayout.setEnableLoadMore(true)
binding.refreshLayout.setOnLoadMoreListener {
requests.trySend(Request.MoreData)
}
}
private fun initAdapter() {
adapter = SingerMoreSongAdapter(this, list, type)
if (type == "MUSIC_PAGE_TYPE_ARTIST_DISCOGRAPHY") {
binding.rv.layoutManager = GridLayoutManager(this, 2)
} else {
binding.rv.layoutManager =
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
}
binding.rv.adapter = adapter
}
private suspend fun initData(browseId: String, params: String) {
showLoadingUi()
Innertube.moSingerDetailsMorePage(BrowseBody(browseId = browseId, params = params))
?.onSuccess { result ->
showDataUi()
val title = result.title
if (title.isNullOrEmpty()) {
binding.title.visibility = View.GONE
} else {
binding.title.visibility = View.VISIBLE
binding.title.text = title
}
currentContinuation = result.continuation
list.clear()
list.addAll(result.songList)
}?.onFailure {
showNoContentUi()
}
}
@SuppressLint("NotifyDataSetChanged")
private suspend fun initDataMore(continuation: String) {
Innertube.moSingerDetailsMoreLoadMorePage(ContinuationBody(continuation = continuation))
?.onSuccess { result ->
currentContinuation = result.continuation
if (currentContinuation.isNullOrEmpty()) {
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
if (result.songList.isNotEmpty()) {
list.addAll(result.songList)
if (!isFinishing) {
adapter?.notifyDataSetChanged()
binding.refreshLayout.finishLoadMore(true)
}
} else {
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}?.onFailure {
binding.refreshLayout.finishLoadMoreWithNoMoreData()
}
}
private fun showDataUi() {
binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.GONE
}
private fun showLoadingUi() {
binding.loadingLayout.visibility = View.VISIBLE
binding.noContentLayout.visibility = View.GONE
}
private fun showNoContentUi() {
binding.loadingLayout.visibility = View.GONE
binding.noContentLayout.visibility = View.VISIBLE
}
}

View File

@ -0,0 +1,145 @@
package com.player.musicoo.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.player.musicoo.R
import com.player.musicoo.activity.MoListDetailsActivity
import com.player.musicoo.activity.MoPlayDetailsActivity
import com.player.musicoo.activity.MoSingerDetailsActivity
import com.player.musicoo.databinding.MusicTowRowItemBinding
import com.player.musicoo.databinding.PlayListItemBinding
import com.player.musicoo.databinding.SearchResultOtherItemBinding
import com.player.musicoo.databinding.SearchResultOtherLayoutBinding
import com.player.musicoo.innertube.Innertube
import com.player.musicoo.media.MediaControllerManager
import com.player.musicoo.util.LogTag
class SingerMoreSongAdapter(
private val context: Context,
private val list: List<Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults>,
private val type: String? = null,
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
TYPE_ONE -> {
val binding =
SearchResultOtherItemBinding.inflate(
LayoutInflater.from(context),
parent,
false
)
ViewHolderOne(binding)
}
TYPE_TWO -> {
val binding =
MusicTowRowItemBinding.inflate(LayoutInflater.from(context), parent, false)
ViewHolderTow(binding)
}
else -> throw IllegalArgumentException("Invalid view type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val bean = list[position]
when (holder.itemViewType) {
TYPE_ONE -> {
val typeOneHolder = holder as ViewHolderOne
typeOneHolder.bind(bean)
typeOneHolder.itemView.setOnClickListener {
val intent = Intent(context, MoPlayDetailsActivity::class.java)
intent.putExtra(
MoPlayDetailsActivity.PLAY_DETAILS_VIDEO_ID,
bean.videoId
)
intent.putExtra(
MoPlayDetailsActivity.PLAY_DETAILS_PLAY_LIST_ID,
bean.playlistId
)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_NAME, bean.title)
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, bean.name)
context.startActivity(intent)
}
}
TYPE_TWO -> {
val typeTwoHolder = holder as ViewHolderTow
typeTwoHolder.bind(bean)
typeTwoHolder.itemView.setOnClickListener {
val intent = Intent(context, MoListDetailsActivity::class.java)
intent.putExtra(
MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID,
bean.browseId
)
context.startActivity(intent)
}
}
}
}
private val TYPE_ONE = 1
private val TYPE_TWO = 2
override fun getItemViewType(position: Int): Int {
return if (type == "MUSIC_PAGE_TYPE_ARTIST_DISCOGRAPHY") {
TYPE_TWO
} else {
TYPE_ONE
}
}
override fun getItemCount(): Int = list.size
inner class ViewHolderOne(private val binding: SearchResultOtherItemBinding) :
RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(bean: Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults) {
binding.apply {
Glide.with(context)
.load(bean.thumbnail)
.into(image)
title.text = bean.title
name.text = bean.name
}
}
}
inner class ViewHolderTow(private val binding: MusicTowRowItemBinding) :
RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(bean: Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults) {
binding.apply {
Glide.with(context)
.load(bean.thumbnail)
.into(image)
nameTv.text = bean.title
descTv.text = bean.name
}
}
}
private var itemClickListener: OnItemClickListener? = null
fun setOnItemClickListener(listener: OnItemClickListener) {
itemClickListener = listener
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
}

View File

@ -1,5 +1,6 @@
package com.player.musicoo.adapter
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
@ -8,11 +9,12 @@ import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.player.musicoo.activity.MoListDetailsActivity
import com.player.musicoo.activity.MoPlayDetailsActivity
import com.player.musicoo.activity.MoSingerDetailsActivity
import com.player.musicoo.databinding.MusicTowRowItemBinding
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
class TowRowListAdapter(
private val context: Context,
private val context: Activity,
private val list: List<MusicCarouselShelfRenderer.Content>,
) :
RecyclerView.Adapter<TowRowListAdapter.ViewHolder>() {
@ -35,6 +37,8 @@ class TowRowListAdapter(
val browseId = browseEndpoint?.browseId
val pageType = browseEndpoint?.type
val watchEndpoint = bean.musicTwoRowItemRenderer
?.navigationEndpoint
?.watchEndpoint
@ -81,9 +85,21 @@ class TowRowListAdapter(
intent.putExtra(MoPlayDetailsActivity.PLAY_DETAILS_DESC, desc)
context.startActivity(intent)
} else {
val intent = Intent(context, MoListDetailsActivity::class.java)
intent.putExtra(MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID, browseId)
context.startActivity(intent)
if (pageType == "MUSIC_PAGE_TYPE_ARTIST") {
val intent = Intent(context, MoSingerDetailsActivity::class.java)
intent.putExtra(
MoSingerDetailsActivity.SINGER_DETAILS_PAGE_BROWSE_ID,
browseId
)
context.startActivity(intent)
if (context is MoSingerDetailsActivity) {
context.finish()
}
} else {
val intent = Intent(context, MoListDetailsActivity::class.java)
intent.putExtra(MoListDetailsActivity.PLAY_LIST_PAGE_BROWSE_ID, browseId)
context.startActivity(intent)
}
}
}
}

View File

@ -1,9 +1,12 @@
package com.player.musicoo.innertube
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
import com.player.musicoo.innertube.models.MusicResponsiveListItemRenderer
import com.player.musicoo.innertube.models.MusicShelfRenderer
import com.player.musicoo.innertube.models.MusicTwoRowItemRenderer
import com.player.musicoo.innertube.models.NavigationEndpoint
import com.player.musicoo.innertube.models.Runs
import com.player.musicoo.innertube.models.Thumbnail
import com.player.musicoo.innertube.utils.brotli
import com.player.musicoo.util.LogTag
import io.ktor.client.HttpClient
import io.ktor.client.engine.okhttp.OkHttp
import io.ktor.client.plugins.BrowserUserAgent
@ -15,11 +18,6 @@ import io.ktor.client.request.header
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.serialization.kotlinx.json.json
import com.player.musicoo.innertube.models.NavigationEndpoint
import com.player.musicoo.innertube.models.Runs
import com.player.musicoo.innertube.models.Thumbnail
import com.player.musicoo.innertube.utils.brotli
import com.player.musicoo.util.LogTag
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
@ -176,7 +174,10 @@ object Innertube {
)
data class SingerDetailsListPage(
val pageType: String? = null,
val title: String?,
val browseId: String? = null,
val params: String? = null,
val contents: SingerDetailsContent?
) {
data class SingerDetailsContent(
@ -240,6 +241,24 @@ object Innertube {
)
}
data class BaseSingleColumnBrowseSongResults(
val title: String? = null,
val continuation: String? = null,
val songList: List<SingleColumnBrowseSongResults>
) {
data class SingleColumnBrowseSongResults(
val title: String?,
val name: String?,
val thumbnail: String?,
val videoId: String? = null,
val playlistId: String? = null,
val browseId: String? = null,
val params: String? = null,
val pageType: String? = null
)
}
data class ArtistPage(
val name: String?,
val description: String?,

View File

@ -26,7 +26,7 @@ data class BrowseResponse(
@Serializable
data class ContinuationContents(
val sectionListContinuation : SectionListContinuation?
val sectionListContinuation: SectionListContinuation?
)
@Serializable
@ -34,7 +34,13 @@ data class BrowseResponse(
@JsonNames("musicVisualHeaderRenderer")
val musicImmersiveHeaderRenderer: MusicImmersiveHeaderRenderer?,
val musicDetailHeaderRenderer: MusicDetailHeaderRenderer?,
val musicHeaderRenderer: MusicHeaderRenderer?
) {
@Serializable
data class MusicHeaderRenderer(
val title: Runs?
)
@Serializable
data class MusicDetailHeaderRenderer(
val title: Runs?,

View File

@ -2,10 +2,13 @@ package com.player.musicoo.innertube.requests
import com.player.musicoo.innertube.Innertube
import com.player.musicoo.innertube.models.BrowseResponse
import com.player.musicoo.innertube.models.ContinuationResponse
import com.player.musicoo.innertube.models.GridRenderer
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
import com.player.musicoo.innertube.models.MusicShelfRenderer
import com.player.musicoo.innertube.models.SectionListRenderer
import com.player.musicoo.innertube.models.bodies.BrowseBody
import com.player.musicoo.innertube.models.bodies.ContinuationBody
import com.player.musicoo.innertube.utils.runCatchingNonCancellable
import com.player.musicoo.util.LogTag
import io.ktor.client.call.body
@ -53,6 +56,9 @@ suspend fun Innertube.moSingerListPage(browseId: String): Result<Innertube.Singe
if (sectionListRendererContents != null) {
for (content: SectionListRenderer.Content in sectionListRendererContents) {
var title = ""
var browseId = ""
var params = ""
var pageType = ""
val musicShelfRendererContents: MutableList<MusicShelfRenderer.Content> =
mutableListOf()
val musicCarouselShelfContents: MutableList<MusicCarouselShelfRenderer.Content> =
@ -64,6 +70,14 @@ suspend fun Innertube.moSingerListPage(browseId: String): Result<Innertube.Singe
musicShelfRendererContents.clear()
musicShelfRendererContents.addAll(content.musicShelfRenderer.contents)
}
val browseEndpoint = content.musicShelfRenderer.title
?.runs
?.firstOrNull()
?.navigationEndpoint
?.browseEndpoint
browseId = browseEndpoint?.browseId ?: ""
params = browseEndpoint?.params ?: ""
}
if (content.musicCarouselShelfRenderer != null) {
title = content.musicCarouselShelfRenderer.header
@ -75,6 +89,17 @@ suspend fun Innertube.moSingerListPage(browseId: String): Result<Innertube.Singe
musicCarouselShelfContents.clear()
musicCarouselShelfContents.addAll(content.musicCarouselShelfRenderer.contents)
}
val browseEndpoint = content.musicCarouselShelfRenderer.header
?.musicCarouselShelfBasicHeaderRenderer
?.title
?.runs
?.firstOrNull()
?.navigationEndpoint
?.browseEndpoint
browseId = browseEndpoint?.browseId ?: ""
params = browseEndpoint?.params ?: ""
pageType = browseEndpoint?.type ?: ""
}
val singerDetailsContent = Innertube.SingerDetailsListPage.SingerDetailsContent(
@ -83,20 +108,13 @@ suspend fun Innertube.moSingerListPage(browseId: String): Result<Innertube.Singe
)
val singerDetails =
Innertube.SingerDetailsListPage(title = title, contents = singerDetailsContent)
// LogTag.LogD(TAG, "title->${singerDetails.title}")
// LogTag.LogD(
// TAG,
// "musicShelfContentList size->${singerDetails.contents?.musicShelfContentList?.size}"
// )
// LogTag.LogD(
// TAG,
// "musicCarouselShelfContentList size->${singerDetails.contents?.musicCarouselShelfContentList?.size}"
// )
//
// LogTag.LogD(TAG, "--------------------------------------------")
Innertube.SingerDetailsListPage(
title = title,
contents = singerDetailsContent,
browseId = browseId,
params = params,
pageType = pageType,
)
if (title.isNotEmpty()) {
list.add(singerDetails)
}
@ -109,4 +127,184 @@ suspend fun Innertube.moSingerListPage(browseId: String): Result<Innertube.Singe
description = description,
list
)
}
suspend fun Innertube.moSingerDetailsMorePage(body: BrowseBody): Result<Innertube.BaseSingleColumnBrowseSongResults>? =
runCatchingNonCancellable {
val response = client.post(browse) {
setBody(body)
}.body<BrowseResponse>()
val gridRenderer = response.contents
?.singleColumnBrowseResultsRenderer
?.tabs?.firstOrNull()
?.tabRenderer
?.content
?.sectionListRenderer
?.contents
?.firstOrNull()
?.gridRenderer
val musicPlaylistShelfRenderer = response.contents
?.singleColumnBrowseResultsRenderer
?.tabs
?.firstOrNull()
?.tabRenderer
?.content
?.sectionListRenderer
?.contents
?.firstOrNull()
?.musicShelfRenderer
val title = response.header?.musicHeaderRenderer?.title?.text
val continuation = musicPlaylistShelfRenderer
?.continuations
?.firstOrNull()
?.nextContinuationData
?.continuation
val songList: MutableList<Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults> =
mutableListOf()
val contentList = musicPlaylistShelfRenderer?.contents
val contentGridItems = gridRenderer?.items
if (contentList != null) {
for (content: MusicShelfRenderer.Content in contentList) {
val contentTitle = content.musicResponsiveListItemRenderer
?.flexColumns
?.firstOrNull()
?.musicResponsiveListItemFlexColumnRenderer
?.text?.text
val name = content.musicResponsiveListItemRenderer
?.flexColumns
?.get(1)
?.musicResponsiveListItemFlexColumnRenderer
?.text?.text
val thumbnail = content.musicResponsiveListItemRenderer
?.thumbnail
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails?.let {
it.getOrNull(2) ?: it.getOrNull(1) ?: it.getOrNull(0)
}?.url
val navigationEndpoint = content.musicResponsiveListItemRenderer
?.flexColumns
?.firstOrNull()
?.musicResponsiveListItemFlexColumnRenderer
?.text
?.runs
?.firstOrNull()
?.navigationEndpoint
val videoId = navigationEndpoint?.watchEndpoint?.videoId
val playlistId = navigationEndpoint?.watchEndpoint?.playlistId
songList.add(
Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults(
title = contentTitle,
name = name,
thumbnail = thumbnail,
videoId = videoId,
playlistId = playlistId
)
)
}
} else {
if (contentGridItems != null) {
for (content: GridRenderer.Item in contentGridItems) {
songList.add(
Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults(
title = content.musicTwoRowItemRenderer?.title?.runs?.firstOrNull()?.text,
name = content.musicTwoRowItemRenderer?.subtitle?.text,
thumbnail = content.musicTwoRowItemRenderer
?.thumbnailRenderer
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails?.let {
it.getOrNull(2) ?: it.getOrNull(1) ?: it.getOrNull(0)
}?.url,
browseId = content.musicTwoRowItemRenderer?.navigationEndpoint?.browseEndpoint?.browseId,
params = content.musicTwoRowItemRenderer?.navigationEndpoint?.browseEndpoint?.params,
pageType = content.musicTwoRowItemRenderer?.navigationEndpoint?.browseEndpoint?.type
)
)
}
}
}
Innertube.BaseSingleColumnBrowseSongResults(
title = title,
songList = songList,
continuation = continuation
)
}
suspend fun Innertube.moSingerDetailsMoreLoadMorePage(body: ContinuationBody): Result<Innertube.BaseSingleColumnBrowseSongResults>? =
runCatchingNonCancellable {
val response = client.post(browse) {
setBody(body)
}.body<ContinuationResponse>()
val continuation = response.continuationContents
?.musicShelfContinuation
?.continuations
?.firstOrNull()
?.nextContinuationData
?.continuation
val songList: MutableList<Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults> =
mutableListOf()
val contentList = response.continuationContents?.musicShelfContinuation?.contents
if (contentList != null) {
for (content: MusicShelfRenderer.Content in contentList) {
val title = content.musicResponsiveListItemRenderer
?.flexColumns
?.firstOrNull()
?.musicResponsiveListItemFlexColumnRenderer
?.text?.text
val name = content.musicResponsiveListItemRenderer
?.flexColumns
?.get(1)
?.musicResponsiveListItemFlexColumnRenderer
?.text?.text
val thumbnail = content.musicResponsiveListItemRenderer
?.thumbnail
?.musicThumbnailRenderer
?.thumbnail
?.thumbnails?.let {
it.getOrNull(2) ?: it.getOrNull(1) ?: it.getOrNull(0)
}?.url
val navigationEndpoint = content.musicResponsiveListItemRenderer
?.flexColumns
?.firstOrNull()
?.musicResponsiveListItemFlexColumnRenderer
?.text
?.runs
?.firstOrNull()
?.navigationEndpoint
val videoId = navigationEndpoint?.watchEndpoint?.videoId
val playlistId = navigationEndpoint?.watchEndpoint?.playlistId
songList.add(
Innertube.BaseSingleColumnBrowseSongResults.SingleColumnBrowseSongResults(
title, name, thumbnail, videoId, playlistId
)
)
}
}
Innertube.BaseSingleColumnBrowseSongResults(
continuation = continuation,
songList = songList
)
}

View File

@ -1,6 +1,7 @@
package com.player.musicoo.view
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
@ -12,7 +13,7 @@ import com.player.musicoo.adapter.TowRowListAdapter
import com.player.musicoo.innertube.Innertube
@SuppressLint("ViewConstructor")
class MusicTowRowListView(context: Context, homePage: Innertube.HomePage) : ModuleView(context) {
class MusicTowRowListView(context: Activity, homePage: Innertube.HomePage) : ModuleView(context) {
init {
contentView = inflate(getContext(), R.layout.music_list_layout, this)
val title = contentView?.findViewById<TextView>(R.id.title)

View File

@ -1,21 +1,50 @@
package com.player.musicoo.view
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.player.musicoo.R
import com.player.musicoo.activity.MoSearchMoreActivity
import com.player.musicoo.activity.MoSingerMoreSongActivity
import com.player.musicoo.adapter.TowRowListAdapter
import com.player.musicoo.innertube.Innertube
@SuppressLint("ViewConstructor")
class SingerDetailsOtherView (context: Context, bean: Innertube.SingerDetailsListPage) :
class SingerDetailsOtherView(context: Activity, bean: Innertube.SingerDetailsListPage) :
ModuleView(context) {
init {
contentView = inflate(getContext(), R.layout.singer_music_list_layout, this)
val title = contentView?.findViewById<TextView>(R.id.title)
title?.text = bean.title
val moreBtn = contentView?.findViewById<LinearLayout>(R.id.moreBtn)
moreBtn?.setOnClickListener {
val intent = Intent(context, MoSingerMoreSongActivity::class.java)
intent.putExtra(
MoSingerMoreSongActivity.SINGER_MORE_SONG_BROWSE_ID,
bean.browseId
)
intent.putExtra(MoSingerMoreSongActivity.SINGER_MORE_SONG_PARAMS, bean.params)
intent.putExtra(MoSingerMoreSongActivity.SINGER_MORE_TYPE, bean.pageType)
context.startActivity(intent)
}
when (bean.pageType) {
"MUSIC_PAGE_TYPE_ARTIST_DISCOGRAPHY" -> {
moreBtn?.visibility = VISIBLE
}
"MUSIC_PAGE_TYPE_PLAYLIST" -> {
moreBtn?.visibility = VISIBLE
}
else -> {
moreBtn?.visibility = GONE
}
}
val rv = contentView?.findViewById<RecyclerView>(R.id.rv)

View File

@ -2,11 +2,14 @@ package com.player.musicoo.view
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.widget.LinearLayout
import android.widget.TextView
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.player.musicoo.R
import com.player.musicoo.activity.MoSingerMoreSongActivity
import com.player.musicoo.adapter.SingerDetailsSongListAdapter
import com.player.musicoo.innertube.Innertube
@ -18,6 +21,14 @@ class SingerDetailsSongView(context: Context, bean: Innertube.SingerDetailsListP
val title = contentView?.findViewById<TextView>(R.id.title)
title?.text = bean.title
val moreBtn = contentView?.findViewById<LinearLayout>(R.id.moreBtn)
moreBtn?.setOnClickListener {
val intent = Intent(context, MoSingerMoreSongActivity::class.java)
intent.putExtra(MoSingerMoreSongActivity.SINGER_MORE_SONG_BROWSE_ID, bean.browseId)
intent.putExtra(MoSingerMoreSongActivity.SINGER_MORE_SONG_PARAMS, bean.params)
context.startActivity(intent)
}
val rv = contentView?.findViewById<RecyclerView>(R.id.rv)
val adapter = SingerDetailsSongListAdapter(context, bean.contents?.musicShelfContentList!!)

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/main_bg_color" />
<corners android:radius="16dp" />
</shape>

View File

@ -54,10 +54,10 @@
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateTint="@color/green"
android:progressBackgroundTint="@color/green"
android:progressTint="@color/green"
android:layout_height="wrap_content" />
android:progressTint="@color/green" />
</LinearLayout>
@ -176,10 +176,13 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:scrollbars="none"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:overScrollMode="never"
android:layout_height="wrap_content"
android:layout_margin="16dp" />
android:scrollbars="none" />
</LinearLayout>
</LinearLayout>

View File

@ -118,9 +118,7 @@
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:overScrollMode="never"
android:scrollbars="none"
tools:itemCount="10"
tools:listitem="@layout/music_responsive_item" />
android:scrollbars="none" />
<com.scwang.smart.refresh.footer.BallPulseFooter
android:layout_width="match_parent"

View File

@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingBottom="12dp"
android:paddingTop="12dp"
android:orientation="horizontal">
<RelativeLayout

View File

@ -1,56 +1,78 @@
<!-- custom_dialog_layout.xml -->
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
android:orientation="vertical"
app:cardCornerRadius="16dp"
app:cardElevation="0dp">
android:padding="16dp"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:background="@drawable/drw_dialog_bg"
android:layout_height="wrap_content"
android:background="@color/main_bg_color"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="56dp"
android:layout_height="56dp"
android:src="@mipmap/musicoo_logo_img" />
<TextView
android:id="@+id/dialog_title"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:fontFamily="@font/medium_font"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="16dp" />
</LinearLayout>
android:gravity="center_vertical">
<TextView
android:id="@+id/dialog_content"
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@mipmap/musicoo_logo_img" />
<TextView
android:id="@+id/dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:fontFamily="@font/medium_font"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="16dp" />
</LinearLayout>
<RelativeLayout
android:id="@+id/closeBtn"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentEnd="true"
android:src="@drawable/cha_icon" />
</RelativeLayout>
</RelativeLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:fontFamily="@font/regular_font"
android:text="@string/app_name"
android:textColor="@color/white_60"
android:textSize="14dp" />
android:layout_marginBottom="16dp">
<TextView
android:id="@+id/dialog_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/regular_font"
android:text="@string/app_name"
android:textColor="@color/white_60"
android:textSize="14dp" />
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:layout_width="match_parent"
android:visibility="gone"
android:layout_height="40dp"
android:gravity="center_vertical">
@ -88,4 +110,4 @@
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@ -6,15 +6,43 @@
android:orientation="vertical"
tools:ignore="MissingDefaultResource">
<TextView
android:id="@+id/title"
android:layout_marginTop="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/medium_font"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="18dp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="32dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/medium_font"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="18dp" />
<LinearLayout
android:id="@+id/moreBtn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/regular_font"
android:text="@string/more"
android:textColor="@color/white_60"
android:textSize="12dp" />
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:rotation="270"
android:src="@drawable/arrow_bottom_grey_icon" />
</LinearLayout>
</RelativeLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"