This commit is contained in:
ocean 2024-04-23 11:24:06 +08:00
parent 70e1ef66c8
commit 44e13d2903
15 changed files with 220 additions and 40 deletions

View File

@ -64,7 +64,6 @@ class App : Application() {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
importList = databaseManager.getAllAudioFiles() importList = databaseManager.getAllAudioFiles()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
Log.d("ocean", "initImportAudio importList->${importList.size}")
callback(importList) callback(importList)
} }
} }

View File

@ -60,7 +60,6 @@ class MainActivity : BaseActivity() {
binding.playingStatusLayout.visibility = View.VISIBLE binding.playingStatusLayout.visibility = View.VISIBLE
val currentAudio = App.currentPlayingAudio val currentAudio = App.currentPlayingAudio
Log.d("ocean","main currentAudio->$currentAudio")
val maxProgress = try { val maxProgress = try {
getAudioDurationFromAssets(this, currentAudio?.file!!) getAudioDurationFromAssets(this, currentAudio?.file!!)
} catch (e: Exception) { } catch (e: Exception) {
@ -127,7 +126,6 @@ class MainActivity : BaseActivity() {
binding.playBlackBtn.setOnClickListener { binding.playBlackBtn.setOnClickListener {
val currentPlayer = MediaControllerManager.getController() val currentPlayer = MediaControllerManager.getController()
if (currentPlayer != null) { if (currentPlayer != null) {
Log.d("ocean", "currentPlayer.playbackState->${currentPlayer.playbackState}")
if (currentPlayer.playbackState == Player.STATE_READY) { if (currentPlayer.playbackState == Player.STATE_READY) {
if (currentPlayer.isPlaying) { if (currentPlayer.isPlaying) {
currentPlayer.pause() currentPlayer.pause()
@ -144,19 +142,16 @@ class MainActivity : BaseActivity() {
playWhenReady: Boolean, playWhenReady: Boolean,
reason: Int reason: Int
) { ) {
Log.d("ocean", "main2 onPlayWhenReadyChanged")
updatePlayState(playWhenReady) updatePlayState(playWhenReady)
updateProgressState() updateProgressState()
} }
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
Log.d("ocean", "main2 onPlaybackStateChanged")
updateProgressState() updateProgressState()
} }
}) })
} }
} else { } else {
Log.d("ocean", "main currentPlayer == null")
} }
} }
} }

View File

@ -52,7 +52,6 @@ class PlayDetailsActivity : BaseActivity() {
onBackPressed() onBackPressed()
Toast.makeText(this, getString(R.string.data_error), Toast.LENGTH_SHORT).show() Toast.makeText(this, getString(R.string.data_error), Toast.LENGTH_SHORT).show()
} }
Log.d("ocean", "PlayDetailsActivity audio->$audio")
initView() initView()
} }
@ -104,13 +103,11 @@ class PlayDetailsActivity : BaseActivity() {
playWhenReady: Boolean, playWhenReady: Boolean,
reason: Int reason: Int
) { ) {
Log.d("ocean", "details onPlayWhenReadyChanged")
updatePlayState(playWhenReady, "playWhenReady") updatePlayState(playWhenReady, "playWhenReady")
updateProgressState() updateProgressState()
} }
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
Log.d("ocean", "details onPlaybackStateChanged")
updateProgressState() updateProgressState()
} }
}) })

View File

@ -200,13 +200,6 @@ class ImportFragment : Fragment() {
musicFiles.add(contentUri.toString()) musicFiles.add(contentUri.toString())
// 如果你想要获取文件的具体路径,可以使用 data 变量 // 如果你想要获取文件的具体路径,可以使用 data 变量
// musicFiles.add(data) // musicFiles.add(data)
Log.d(
"ocean",
"name->${name} " +
"uri.toString()->${contentUri.toString()} " +
"duration2->$duration2 "
+ "data->$data"
)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
if (audio.duration > 0) { if (audio.duration > 0) {

View File

@ -8,10 +8,14 @@ import com.player.musicoo.databinding.FragmentMoHomeBinding
import com.player.musicoo.innertube.Innertube import com.player.musicoo.innertube.Innertube
import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer import com.player.musicoo.innertube.models.MusicCarouselShelfRenderer
import com.player.musicoo.innertube.requests.homePage import com.player.musicoo.innertube.requests.homePage
import com.player.musicoo.innertube.requests.homePageMore
import com.player.musicoo.view.MusicResponsiveListView import com.player.musicoo.view.MusicResponsiveListView
import com.player.musicoo.view.MusicTowRowListView import com.player.musicoo.view.MusicTowRowListView
class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() { class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
// private var baseHomePage: Innertube.BaseHomePage? = null
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoHomeBinding override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoHomeBinding
get() = FragmentMoHomeBinding::inflate get() = FragmentMoHomeBinding::inflate
@ -27,11 +31,8 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
} }
private suspend fun initView() { private suspend fun initView() {
Innertube.homePage()?.onFailure { Innertube.homePage()?.onSuccess {
Log.d("ocean", "onFailure->$it") for (home: Innertube.HomePage in it.homePage) {
}
?.onSuccess {
for (home: Innertube.HomePage in it) {
for (content: MusicCarouselShelfRenderer.Content in home.contents) { for (content: MusicCarouselShelfRenderer.Content in home.contents) {
if (content.musicResponsiveListItemRenderer != null) { if (content.musicResponsiveListItemRenderer != null) {
binding.contentLayout.addView( binding.contentLayout.addView(
@ -53,9 +54,40 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
} }
} }
} }
initHomeDataMore(it)
}?.onFailure { Log.d(TAG, "homePage onFailure->${it}") }
} }
private suspend fun initHomeDataMore(baseHomePage: Innertube.BaseHomePage) {
if (baseHomePage.cToken?.isNotEmpty() == true) {
Innertube.homePageMore(baseHomePage)?.onSuccess {
for (home: Innertube.HomePage in it.homePage) {
for (content: MusicCarouselShelfRenderer.Content in home.contents) {
if (content.musicResponsiveListItemRenderer != null) {
binding.contentLayout.addView(
MusicResponsiveListView(
requireActivity(),
home
)
)
break
}
if (content.musicTwoRowItemRenderer != null) {
binding.contentLayout.addView(
MusicTowRowListView(
requireActivity(),
home
)
)
break
}
}
}
initHomeDataMore(it)
}?.onFailure {
Log.d(TAG, "initHomeDataMore onFailure ->${it}")
}
}
} }
override fun onResume() { override fun onResume() {

View File

@ -16,10 +16,12 @@ import com.player.musicoo.innertube.models.NavigationEndpoint
import com.player.musicoo.innertube.models.Runs import com.player.musicoo.innertube.models.Runs
import com.player.musicoo.innertube.models.Thumbnail import com.player.musicoo.innertube.models.Thumbnail
import com.player.musicoo.innertube.utils.brotli import com.player.musicoo.innertube.utils.brotli
import com.player.musicoo.util.LogTag
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
object Innertube { object Innertube {
val TAG = LogTag.VO_API_LOG
val client = HttpClient(OkHttp) { val client = HttpClient(OkHttp) {
BrowserUserAgent() BrowserUserAgent()
@ -54,9 +56,12 @@ object Innertube {
internal const val search = "/youtubei/v1/search" internal const val search = "/youtubei/v1/search"
internal const val searchSuggestions = "/youtubei/v1/music/get_search_suggestions" internal const val searchSuggestions = "/youtubei/v1/music/get_search_suggestions"
internal const val musicResponsiveListItemRendererMask = "musicResponsiveListItemRenderer(flexColumns,fixedColumns,thumbnail,navigationEndpoint)" internal const val musicResponsiveListItemRendererMask =
internal const val musicTwoRowItemRendererMask = "musicTwoRowItemRenderer(thumbnailRenderer,title,subtitle,navigationEndpoint)" "musicResponsiveListItemRenderer(flexColumns,fixedColumns,thumbnail,navigationEndpoint)"
const val playlistPanelVideoRendererMask = "playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)" internal const val musicTwoRowItemRendererMask =
"musicTwoRowItemRenderer(thumbnailRenderer,title,subtitle,navigationEndpoint)"
const val playlistPanelVideoRendererMask =
"playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)"
internal fun HttpRequestBuilder.mask(value: String = "*") = internal fun HttpRequestBuilder.mask(value: String = "*") =
header("X-Goog-FieldMask", value) header("X-Goog-FieldMask", value)
@ -159,6 +164,14 @@ object Innertube {
companion object companion object
} }
data class BaseHomePage(
val visitorData: String?,
val cToken: String?,
val continuation: String,
val itct: String,
val homePage: List<HomePage>
)
data class HomePage( data class HomePage(
val title: String?, val title: String?,
val contents: List<MusicCarouselShelfRenderer.Content> val contents: List<MusicCarouselShelfRenderer.Content>

View File

@ -6,16 +6,29 @@ import kotlinx.serialization.json.JsonNames
@Serializable @Serializable
data class BrowseResponse( data class BrowseResponse(
val responseContext: ResponseContext?,
val contents: Contents?, val contents: Contents?,
val header: Header?, val header: Header?,
val microformat: Microformat? val microformat: Microformat?,
val continuationContents: ContinuationContents?
) { ) {
@Serializable
data class ResponseContext(
val visitorData: String?
)
@Serializable @Serializable
data class Contents( data class Contents(
val singleColumnBrowseResultsRenderer: Tabs?, val singleColumnBrowseResultsRenderer: Tabs?,
val sectionListRenderer: SectionListRenderer?, val sectionListRenderer: SectionListRenderer?,
) )
@Serializable
data class ContinuationContents(
val sectionListContinuation : SectionListContinuation?
)
@Serializable @Serializable
data class Header @OptIn(ExperimentalSerializationApi::class) constructor( data class Header @OptIn(ExperimentalSerializationApi::class) constructor(
@JsonNames("musicVisualHeaderRenderer") @JsonNames("musicVisualHeaderRenderer")
@ -60,4 +73,5 @@ data class BrowseResponse(
val urlCanonical: String? val urlCanonical: String?
) )
} }
} }

View File

@ -13,7 +13,7 @@ data class Context(
val clientVersion: String, val clientVersion: String,
val platform: String, val platform: String,
val hl: String = "zh", val hl: String = "zh",
// val visitorData: String = "CgtEUlRINDFjdm1YayjX1pSaBg%3D%3D", val visitorData: String? = null,
val androidSdkVersion: Int? = null, val androidSdkVersion: Int? = null,
val userAgent: String? = null val userAgent: String? = null
) )

View File

@ -12,6 +12,7 @@ data class Continuation(
) { ) {
@Serializable @Serializable
data class Data( data class Data(
val continuation: String? val continuation: String?,
val clickTrackingParams: String
) )
} }

View File

@ -0,0 +1,29 @@
package com.player.musicoo.innertube.models
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class SectionListContinuation(
val contents: List<Content>?,
val continuations: List<Continuation>?
) {
@Serializable
data class Content(
@JsonNames("musicImmersiveCarouselShelfRenderer")
val musicCarouselShelfRenderer: MusicCarouselShelfRenderer?,
@JsonNames("musicPlaylistShelfRenderer")
val musicShelfRenderer: MusicShelfRenderer?,
val gridRenderer: GridRenderer?,
val musicDescriptionShelfRenderer: MusicDescriptionShelfRenderer?,
) {
@Serializable
data class MusicDescriptionShelfRenderer(
val description: Runs?,
)
}
}

View File

@ -0,0 +1,9 @@
package com.player.musicoo.innertube.models.bodies
import com.player.musicoo.innertube.models.Context
import kotlinx.serialization.Serializable
@Serializable
data class BrowseMoreBody(
val context: Context = Context.DefaultWeb,
)

View File

@ -12,7 +12,7 @@ import io.ktor.client.request.post
import io.ktor.client.request.setBody import io.ktor.client.request.setBody
// //
suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? = suspend fun Innertube.homePage(): Result<Innertube.BaseHomePage>? =
runCatchingNonCancellable { runCatchingNonCancellable {
val response = client.post(browse) { val response = client.post(browse) {
@ -29,7 +29,17 @@ suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
?.sectionListRenderer ?.sectionListRenderer
val contents = sectionListRenderer?.contents val contents = sectionListRenderer?.contents
Log.d("ocean","contents->$contents")
val visitorData = response.responseContext?.visitorData
val continuations = sectionListRenderer?.continuations
val nextContinuationData = continuations
?.firstOrNull()
?.nextContinuationData
val cToken = nextContinuationData?.continuation
val itct = nextContinuationData?.clickTrackingParams
val searchList: MutableList<Innertube.HomePage> = mutableListOf() val searchList: MutableList<Innertube.HomePage> = mutableListOf()
@ -45,7 +55,8 @@ suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
val homePage = val homePage =
content.musicCarouselShelfRenderer?.contents?.let { content.musicCarouselShelfRenderer?.contents?.let {
Innertube.HomePage(text, Innertube.HomePage(
text,
it it
) )
} }
@ -54,5 +65,14 @@ suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
} }
} }
} }
val baseHomePage =
Innertube.BaseHomePage(
visitorData,
cToken,
cToken.toString(),
itct.toString(),
searchList searchList
)
baseHomePage
} }

View File

@ -0,0 +1,82 @@
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.Context
import com.player.musicoo.innertube.models.SectionListContinuation
import com.player.musicoo.innertube.models.bodies.BrowseMoreBody
import com.player.musicoo.innertube.utils.runCatchingNonCancellable
import io.ktor.client.call.body
import io.ktor.client.request.post
import io.ktor.client.request.setBody
suspend fun Innertube.homePageMore(
baseHomePage: Innertube.BaseHomePage
): Result<Innertube.BaseHomePage>? =
runCatchingNonCancellable {
val defaultWeb = Context(
client = Context.Client(
clientName = "WEB_REMIX",
clientVersion = "1.20220918",
platform = "DESKTOP",
visitorData = baseHomePage.visitorData
)
)
val url =
browse + "?ctoken=${baseHomePage.cToken}&continuation=${baseHomePage.continuation}&type=next&itct=${baseHomePage.itct}&prettyPrint=false"
val response = client.post(url) {
setBody(BrowseMoreBody(defaultWeb))
}.body<BrowseResponse>()
val sectionListContinuation = response
.continuationContents
?.sectionListContinuation
val contents = sectionListContinuation?.contents
val visitorData = baseHomePage.visitorData//保存上次的data
val continuations = sectionListContinuation?.continuations
val nextContinuationData = continuations
?.firstOrNull()
?.nextContinuationData
val cToken = nextContinuationData?.continuation
val itct = nextContinuationData?.clickTrackingParams
val searchList: MutableList<Innertube.HomePage> = mutableListOf()
if (contents != null) {
for (content: SectionListContinuation.Content in contents) {
val text = content.musicCarouselShelfRenderer
?.header
?.musicCarouselShelfBasicHeaderRenderer
?.title
?.runs
?.firstOrNull()
?.text
val homePage =
content.musicCarouselShelfRenderer?.contents?.let {
Innertube.HomePage(
text,
it
)
}
if (homePage != null) {
searchList.add(homePage)
}
}
}
Innertube.BaseHomePage(
visitorData,
cToken,
cToken.toString(),
itct.toString(),
searchList
)
}

View File

@ -33,9 +33,6 @@ object MediaControllerManager {
controllerFuture = MediaController.Builder(context, sessionToken).buildAsync() controllerFuture = MediaController.Builder(context, sessionToken).buildAsync()
controllerFuture?.addListener({ controllerFuture?.addListener({
mediaController = controllerFuture?.get() mediaController = controllerFuture?.get()
Log.d("ocean", "MediaControllerManager init")
Log.d("ocean", "MediaController connected: ${mediaController?.isConnected}")
}, MoreExecutors.directExecutor()) }, MoreExecutors.directExecutor())
} }
@ -57,7 +54,6 @@ object MediaControllerManager {
Uri.parse("file:///android_asset/$currentAudioFile") Uri.parse("file:///android_asset/$currentAudioFile")
} }
Log.d("ocean","uri->$uri")
val resourceId = R.mipmap.musicoo_logo_img val resourceId = R.mipmap.musicoo_logo_img
val imgUri: Uri? = if (audio.image.isNotEmpty()) { val imgUri: Uri? = if (audio.image.isNotEmpty()) {
@ -114,7 +110,6 @@ object MediaControllerManager {
Uri.parse("file:///android_asset/$currentAudioFile") Uri.parse("file:///android_asset/$currentAudioFile")
} }
Log.d("ocean","uri->$uri")
val resourceId = R.mipmap.musicoo_logo_img val resourceId = R.mipmap.musicoo_logo_img
val imgUri: Uri? = if (audio.image.isNotEmpty()) { val imgUri: Uri? = if (audio.image.isNotEmpty()) {

View File

@ -2,5 +2,6 @@ package com.player.musicoo.util
object LogTag { object LogTag {
const val VO_ACT_LOG = "vo-act—log" const val VO_ACT_LOG = "vo-act—log"
const val VO_FRAGMENT_LOG = "vo_fragment_log" const val VO_FRAGMENT_LOG = "vo-fragment-log"
const val VO_API_LOG = "vo-api—log"
} }