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 {
importList = databaseManager.getAllAudioFiles()
withContext(Dispatchers.Main) {
Log.d("ocean", "initImportAudio importList->${importList.size}")
callback(importList)
}
}

View File

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

View File

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

View File

@ -200,13 +200,6 @@ class ImportFragment : Fragment() {
musicFiles.add(contentUri.toString())
// 如果你想要获取文件的具体路径,可以使用 data 变量
// musicFiles.add(data)
Log.d(
"ocean",
"name->${name} " +
"uri.toString()->${contentUri.toString()} " +
"duration2->$duration2 "
+ "data->$data"
)
CoroutineScope(Dispatchers.IO).launch {
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.models.MusicCarouselShelfRenderer
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.MusicTowRowListView
class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
// private var baseHomePage: Innertube.BaseHomePage? = null
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentMoHomeBinding
get() = FragmentMoHomeBinding::inflate
@ -27,11 +31,37 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
}
private suspend fun initView() {
Innertube.homePage()?.onFailure {
Log.d("ocean", "onFailure->$it")
}
?.onSuccess {
for (home: Innertube.HomePage in it) {
Innertube.homePage()?.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, "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(
@ -53,9 +83,11 @@ class MoHomeFragment : MoBaseFragment<FragmentMoHomeBinding>() {
}
}
}
initHomeDataMore(it)
}?.onFailure {
Log.d(TAG, "initHomeDataMore onFailure ->${it}")
}
}
}
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.Thumbnail
import com.player.musicoo.innertube.utils.brotli
import com.player.musicoo.util.LogTag
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
object Innertube {
val TAG = LogTag.VO_API_LOG
val client = HttpClient(OkHttp) {
BrowserUserAgent()
@ -39,7 +41,7 @@ object Innertube {
}
defaultRequest {
url(scheme = "https", host ="music.youtube.com") {
url(scheme = "https", host = "music.youtube.com") {
headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
headers.append("X-Goog-Api-Key", "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8")
parameters.append("prettyPrint", "false")
@ -54,9 +56,12 @@ object Innertube {
internal const val search = "/youtubei/v1/search"
internal const val searchSuggestions = "/youtubei/v1/music/get_search_suggestions"
internal const val musicResponsiveListItemRendererMask = "musicResponsiveListItemRenderer(flexColumns,fixedColumns,thumbnail,navigationEndpoint)"
internal const val musicTwoRowItemRendererMask = "musicTwoRowItemRenderer(thumbnailRenderer,title,subtitle,navigationEndpoint)"
const val playlistPanelVideoRendererMask = "playlistPanelVideoRenderer(title,navigationEndpoint,longBylineText,shortBylineText,thumbnail,lengthText)"
internal const val musicResponsiveListItemRendererMask =
"musicResponsiveListItemRenderer(flexColumns,fixedColumns,thumbnail,navigationEndpoint)"
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 = "*") =
header("X-Goog-FieldMask", value)
@ -159,6 +164,14 @@ object Innertube {
companion object
}
data class BaseHomePage(
val visitorData: String?,
val cToken: String?,
val continuation: String,
val itct: String,
val homePage: List<HomePage>
)
data class HomePage(
val title: String?,
val contents: List<MusicCarouselShelfRenderer.Content>

View File

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

View File

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

View File

@ -12,6 +12,7 @@ data class Continuation(
) {
@Serializable
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
//
suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
suspend fun Innertube.homePage(): Result<Innertube.BaseHomePage>? =
runCatchingNonCancellable {
val response = client.post(browse) {
@ -29,7 +29,17 @@ suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
?.sectionListRenderer
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()
@ -45,7 +55,8 @@ suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
val homePage =
content.musicCarouselShelfRenderer?.contents?.let {
Innertube.HomePage(text,
Innertube.HomePage(
text,
it
)
}
@ -54,5 +65,14 @@ suspend fun Innertube.homePage(): Result<List<Innertube.HomePage>>? =
}
}
}
searchList
val baseHomePage =
Innertube.BaseHomePage(
visitorData,
cToken,
cToken.toString(),
itct.toString(),
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?.addListener({
mediaController = controllerFuture?.get()
Log.d("ocean", "MediaControllerManager init")
Log.d("ocean", "MediaController connected: ${mediaController?.isConnected}")
}, MoreExecutors.directExecutor())
}
@ -57,7 +54,6 @@ object MediaControllerManager {
Uri.parse("file:///android_asset/$currentAudioFile")
}
Log.d("ocean","uri->$uri")
val resourceId = R.mipmap.musicoo_logo_img
val imgUri: Uri? = if (audio.image.isNotEmpty()) {
@ -114,7 +110,6 @@ object MediaControllerManager {
Uri.parse("file:///android_asset/$currentAudioFile")
}
Log.d("ocean","uri->$uri")
val resourceId = R.mipmap.musicoo_logo_img
val imgUri: Uri? = if (audio.image.isNotEmpty()) {

View File

@ -2,5 +2,6 @@ package com.player.musicoo.util
object LogTag {
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"
}