哈哈,在发表评论七年后,我终于不得不实施这个 :-D 我想要一个风滚草徽章;-)
- 据我所知
- 是的
- 据我所知,30-60 秒之间的轮询计时器应该没问题。一开始我想减少用户的网络流量,但是如果您同时播放广播,这无关紧要:-D
这是我快速而肮脏的解决方案,以防万一有人需要。示例中有一些自定义类,但您会明白的
import androidx.core.text.HtmlCompat
import de.jahpress.android.main.L
import de.jahpress.android.main.MAX_REQUEST_FOR_SHOUTCAST_TRACK_INFO
import de.jahpress.android.service.Data
import de.jahpress.android.service.radio.model.BaseStation
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
class ShoutCastTrackInfoManager {
private val timeOut = 5L
private val pollingIntervalMs = 60_000L
private var updateTimer: Timer? = null
private var trackInfoThread: Thread? = null
private var invalidTrackInfoCounter = 0
//will ask track info only one time (select station in my use case)
fun updateTrackInfoFor(station: BaseStation, resultCallback: (info: String?) -> Unit) {
L.d("TrackInfo: Get title info for ${station.getStationName()}")
invalidTrackInfoCounter = 0
stopTrackInfoPolling()
requestTrackInfoFromShoutcast(station, resultCallback)
}
//will start track info polling (if station is playing)
fun startTrackInfoPolling(station: BaseStation) {
L.d("TrackInfo: Get title info for ${station.getStationName()}")
stopTrackInfoPolling()
updateTimer = Timer()
updateTimer?.schedule(object : TimerTask() {
override fun run() {
requestTrackInfoFromShoutcast(station, null)
}
}, 0, pollingIntervalMs)
}
fun stopTrackInfoPolling() {
trackInfoThread?.let {
L.d("TrackInfo: Stopping current title update for stream")
it.interrupt()
}
updateTimer?.cancel()
}
private fun requestTrackInfoFromShoutcast(
station: BaseStation,
resultCallback: ((info: String?) -> Unit)?
) {
if (invalidTrackInfoCounter >= MAX_REQUEST_FOR_SHOUTCAST_TRACK_INFO) {
L.d("TrackInfo: $MAX_REQUEST_FOR_SHOUTCAST_TRACK_INFO invalid stream titles. Sto...")
invalidTrackInfoCounter = 0
stopTrackInfoPolling()
Data.currentTitleInfo = null //reset track info
return
}
trackInfoThread = thread {
try {
var trackInfo: String? = null
get7HtmlFromStream(station)?.let {
L.d("TrackInfo: Request track info at $it")
val request = Request.Builder().url(it).build()
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(timeOut, TimeUnit.SECONDS)
.writeTimeout(timeOut, TimeUnit.SECONDS)
.readTimeout(timeOut, TimeUnit.SECONDS)
.build()
val response = okHttpClient.newCall(request).execute()
if (response.isSuccessful) {
val result = response.body?.string()
trackInfo = extractTrackInfoFrom7Html(result)
if (trackInfo != null) {
Data.currentTitleInfo = trackInfo
}
}
response.close()
}
resultCallback?.invoke(trackInfo)
} catch (e: Exception) {
L.e(e)
resultCallback?.invoke(null)
stopTrackInfoPolling()
}
}
}
/**
* Will create Shoutcast 7.html which is located at stream url.
*
* For example: http://66.55.145.43:7473/stream
* 7.html at http://66.55.145.43:7473/7.html
*/
private fun get7HtmlFromStream(station: BaseStation): String? {
val baseStreamUrl = station.getStreamUrl()
L.w("Base url -> $baseStreamUrl")
if (baseStreamUrl == null) return null
val numberSlash = baseStreamUrl.count { c -> c == '/' }
if (numberSlash <= 2) {
return "$baseStreamUrl/7.html"
}
val startOfPath = station.getStreamUrl().indexOf("/", 8)
val streamUrl = station.getStreamUrl().subSequence(0, startOfPath)
return "$streamUrl/7.html"
}
/**
* Will convert webpage to trackinfo. Therefore
* 1. Remove all html-tags
* 2. Get <body> content of webpage
* 3. Extract and return trackinfo
*
* Trackinfo format is always like
* "632,1,1943,2000,439,128,Various Artists - Dance to Dancehall"
* so method will return everything after sixth "," comma character.
*
* Important:
* - Shoutcast might return invalid html
* - Site will return 404 error strings
* - might be empty
*/
private fun extractTrackInfoFrom7Html(html: String?): String? {
L.i("Extract track info from -> $html")
if (html == null) return null
val content = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT).toString()
val array = content.split(",")
return if (array.size < 7) {
null
} else {
var combinedTrackInfo = ""
for (index in 6 until array.size) {
combinedTrackInfo += "${array[index]} "
}
if (combinedTrackInfo.trim().isEmpty()) {
return null
}
return combinedTrackInfo
}
}
}