Skip to content

Commit

Permalink
fetch sources via the network
Browse files Browse the repository at this point in the history
  • Loading branch information
lizongying committed Dec 13, 2024
1 parent 2369dc1 commit baf4db7
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 25 deletions.
10 changes: 10 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
## 更新日誌

### v1.3.8.8

* 通過網絡獲取默認視頻源列表

### v1.3.8.7-kitkat

* 修復切換頻道不正確的問題
* EPG兼容匹配
* 修復一些閃退問題

### v1.3.8.7

* 修復切換頻道不正確的問題
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 我的電視·〇

電視網絡視頻播放軟件,可以自定義視頻源
電視視頻播放軟件,可以自定義視頻源

[my-tv-0](https://github.com/lizongying/my-tv-0)

Expand Down Expand Up @@ -84,8 +84,6 @@ adb install my-tv-0.apk
* 詳細EPG
* 淺色菜單
* 無效的頻道?
* 判断文件是否被修改
* 多源管理
* 如果上次播放頻道不在收藏?
* 當list為空,顯示group
* 默認頻道菜單顯示
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/lizongying/mytv0/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ class MainActivity : AppCompatActivity() {

Utils.isp.observe(this) {
Log.i(TAG, "isp $it")
// val id = R.raw.mobile
val id = when (it) {
ISP.CHINA_MOBILE -> R.raw.mobile
// ISP.CHINA_MOBILE -> R.raw.mobile
// ISP.IPV6->R.raw.ipv6
else -> 0
}

Expand Down
5 changes: 3 additions & 2 deletions app/src/main/java/com/lizongying/mytv0/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class MainViewModel : ViewModel() {
cacheChannels = getCache()

if (cacheChannels.isEmpty()) {
cacheChannels = context.resources.openRawResource(R.raw.channels).bufferedReader()
cacheChannels = context.resources.openRawResource(DEFAULT_CHANNELS_FILE).bufferedReader()
.use { it.readText() }
}

Expand Down Expand Up @@ -237,7 +237,7 @@ class MainViewModel : ViewModel() {
}

fun reset(context: Context) {
val str = context.resources.openRawResource(R.raw.channels).bufferedReader()
val str = context.resources.openRawResource(DEFAULT_CHANNELS_FILE).bufferedReader()
.use { it.readText() }

try {
Expand Down Expand Up @@ -485,5 +485,6 @@ class MainViewModel : ViewModel() {
companion object {
private const val TAG = "MainViewModel"
const val CACHE_FILE_NAME = "channels.txt"
val DEFAULT_CHANNELS_FILE = R.raw.channels
}
}
79 changes: 74 additions & 5 deletions app/src/main/java/com/lizongying/mytv0/SimpleServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.lizongying.mytv0

import MainViewModel
import MainViewModel.Companion.CACHE_FILE_NAME
import MainViewModel.Companion.DEFAULT_CHANNELS_FILE
import android.content.Context
import android.net.Uri
import android.os.Handler
Expand All @@ -15,7 +16,12 @@ import com.lizongying.mytv0.data.ReqSourceAdd
import com.lizongying.mytv0.data.ReqSources
import com.lizongying.mytv0.data.RespSettings
import com.lizongying.mytv0.data.Source
import com.lizongying.mytv0.requests.HttpClient
import fi.iki.elonen.NanoHTTPD
import io.github.lizongying.Gua
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
import java.io.IOException
import java.nio.charset.StandardCharsets
Expand All @@ -36,8 +42,9 @@ class SimpleServer(private val context: Context, private val viewModel: MainView
override fun serve(session: IHTTPSession): Response {
return when (session.uri) {
"/api/settings" -> handleSettings()
"/api/channels" -> handleChannelsFromFile(session)
"/api/uri" -> handleChannelsFromUri(session)
"/api/sources" -> handleSources()
"/api/import-text" -> handleImportFromText(session)
"/api/import-uri" -> handleImportFromUri(session)
"/api/proxy" -> handleProxy(session)
"/api/epg" -> handleEPG(session)
"/api/channel" -> handleDefaultChannel(session)
Expand All @@ -56,7 +63,7 @@ class SimpleServer(private val context: Context, private val viewModel: MainView
""
}
if (str.isEmpty()) {
str = context.resources.openRawResource(R.raw.channels).bufferedReader()
str = context.resources.openRawResource(DEFAULT_CHANNELS_FILE).bufferedReader()
.use { it.readText() }
}

Expand Down Expand Up @@ -94,7 +101,69 @@ class SimpleServer(private val context: Context, private val viewModel: MainView
return newFixedLengthResponse(Response.Status.OK, "application/json", response)
}

private fun handleChannelsFromFile(session: IHTTPSession): Response {
private suspend fun fetchSources(url: String): String {
val urls =
if (url.startsWith("https://raw.githubusercontent.com") || url.startsWith("https://github.com")) {
listOf(
"https://ghp.ci/",
"https://gh.llkk.cc/",
"https://github.moeyy.xyz/",
"https://mirror.ghproxy.com/",
"https://ghproxy.cn/",
"https://ghproxy.net/",
"https://ghproxy.click/",
"https://ghproxy.com/",
"https://github.moeyy.cn/",
"https://gh-proxy.llyke.com/",
"https://www.ghproxy.cc/",
"https://cf.ghproxy.cc/"
).map {
Pair("$it$url", url)
}
} else {
listOf(Pair(url, url))
}

var sources = ""
var success = false
for ((a, b) in urls) {
Log.i(TAG, "request $a")
try {
withContext(Dispatchers.IO) {
val request = okhttp3.Request.Builder().url(a).build()
val response = HttpClient.okHttpClient.newCall(request).execute()

if (response.isSuccessful) {
sources = response.bodyAlias()?.string() ?: ""
success = true
} else {
Log.e(TAG, "Request status ${response.codeAlias()}")
}
}
} catch (e: Exception) {
e.printStackTrace()
Log.e(TAG, "fetchSources", e)
}

if (success) break
}

return sources
}

private fun handleSources(): Response {
val response = runBlocking(Dispatchers.IO) {
fetchSources("https://raw.githubusercontent.com/lizongying/my-tv-0/main/app/src/main/res/raw/sources.txt")
}

return newFixedLengthResponse(
Response.Status.OK,
"application/json",
Gua().decode(response)
)
}

private fun handleImportFromText(session: IHTTPSession): Response {
R.string.start_config_channel.showToast()
val response = ""
try {
Expand All @@ -114,7 +183,7 @@ class SimpleServer(private val context: Context, private val viewModel: MainView
return newFixedLengthResponse(Response.Status.OK, "text/plain", response)
}

private fun handleChannelsFromUri(session: IHTTPSession): Response {
private fun handleImportFromUri(session: IHTTPSession): Response {
R.string.start_config_channel.showToast()
val response = ""
try {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/lizongying/mytv0/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ enum class ISP {
CHINA_MOBILE,
CHINA_UNICOM,
CHINA_TELECOM,
IPV6,
}

data class IpInfo(
Expand Down
77 changes: 66 additions & 11 deletions app/src/main/res/raw/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,14 @@
margin-left: .6rem;
}

input[readonly] {
.source-grey {
background-color: grey;
}

.source-green {
background-color: darkslategray;
}

.history {
margin-top: 10px;
}
Expand All @@ -97,8 +101,9 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
<label for="uri" id="uri-label">視頻源地址</label>
<div class="container">
<input type="text" id="uri"/>
<input type="button" id="confirm-uri" value="新增" class="insert"/>
<input type="button" id="confirm-uri" value="新增" class="add"/>
</div>
<div id="sources-new"></div>
<div id="source"></div>
</div>

Expand Down Expand Up @@ -166,7 +171,7 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
proxyLabel: '代理地址',
confirm: '確認',
remove: '刪除',
insert: '新增',
add: '新增',
},
SIMPLIFIED_CHINESE: {
appName: '我的电视·〇',
Expand All @@ -179,7 +184,7 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
proxyLabel: '代理地址',
confirm: '确认',
remove: '删除',
insert: '新增',
add: '新增',
}
};
const t = (key) => lang['TRADITIONAL_CHINESE'][key];
Expand All @@ -191,12 +196,11 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
document.querySelector('#channel-label').innerText = t('channelLabel');
document.querySelector('#epg-label').innerText = t('epgLabel');
document.querySelector('#proxy-label').innerText = t('proxyLabel');
document.querySelector('#confirm-uri').innerText = t('insert');
document.querySelectorAll('.confirm').forEach(
i => i.value = t('confirm')
);
document.querySelectorAll('.insert').forEach(
i => i.value = t('insert')
document.querySelectorAll('.add').forEach(
i => i.value = t('add')
);

const source = document.querySelector('#source');
Expand Down Expand Up @@ -225,12 +229,12 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
const uri = document.querySelector('#uri').value.trim();
if (uri.length > 0) {
const uuid = uuidV4()
save('/api/uri', JSON.stringify({
save('/api/import-uri', JSON.stringify({
id: uuid,
uri: uri,
}))

let htmlString = `<div class="container history"><input type="text" readonly value="${uri}" /><input type="button" value="删除" class="remove" data-source-id="${uuid}"/></div>`;
let htmlString = `<div class="container history"><input type="text" readonly value="${uri}" class="source-grey"/><input type="button" value="删除" class="remove" data-source-id="${uuid}"/></div>`;
let doc = new DOMParser().parseFromString(htmlString, 'text/html');
const firstChild = source.firstChild;
if (firstChild) {
Expand All @@ -246,7 +250,7 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
document.querySelector('#confirm-plain-text').onclick = () => {
const content = document.querySelector('#plain-text').value.trim();
if (content.length > 0) {
save('/api/channels', content)
save('/api/import-text', content)
}
}

Expand Down Expand Up @@ -297,6 +301,38 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
})
}

const handleAdd = () => {
const addList = document.querySelectorAll('.add');
[...addList].forEach(add => {
add.value = t('add');
add.onclick = () => {
const uri = add.dataset.uri.trim();
console.log('uri', uri);

const uuid = uuidV4();
save('/api/import-uri', JSON.stringify({
id: uuid,
uri: uri,
}));

add.parentElement.remove();

let htmlString = `<div class="container history"><input type="text" readonly value="${uri}" class="source-grey"/><input type="button" value="删除" class="remove" data-source-id="${uuid}"/></div>`;
let doc = new DOMParser().parseFromString(htmlString, 'text/html');
const firstChild = source.firstChild;
if (firstChild) {
source.insertBefore(doc.body.firstChild, firstChild);
} else {
source.appendChild(doc.body.firstChild);
}

handleRemove();

document.querySelector('#uri').value = uri;
}
})
}

const uuidV4 = () => {
return ([1e7] + '-' + 1e3 + '-' + 4e3 + '-' + 8e3 + '-' + 1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
Expand All @@ -314,12 +350,31 @@ <h4>視頻源可以设置為地址/文本/文件其中之一</h4>
document.querySelector('#proxy').value = json.proxy;

json.history.forEach(v => {
let htmlString = `<div class="container history"><input type="text" readonly value="${v.uri}" /><input type="button" value="删除" class="remove" data-source-id="${v.id}"/></div>`;
let htmlString = `<div class="container history"><input type="text" readonly value="${v.uri}" class="source-grey"/><input type="button" value="删除" class="remove" data-source-id="${v.id}"/></div>`;
let doc = new DOMParser().parseFromString(htmlString, 'text/html');
source.appendChild(doc.body.firstChild);
})

handleRemove()

const sources = await fetch('/api/sources');
const sourcesText = await sources.text();
console.log(sourcesText);

const histories = json.history.map(v => v.uri)

const sourcesNew = document.querySelector('#sources-new');
sourcesText.trim().split("\n").forEach(v => {
if (histories.includes(v)) {
return
}

let htmlString = `<div class="container history"><input type="text" readonly value="${v}" class="source-green"/><input type="button" value="增加" class="add" data-uri="${v}"/></div>`;
let doc = new DOMParser().parseFromString(htmlString, 'text/html');
sourcesNew.appendChild(doc.body.firstChild);
})

handleAdd()
})()
</script>
</html>
1 change: 1 addition & 0 deletions app/src/main/res/raw/mobile.txt

Large diffs are not rendered by default.

Loading

0 comments on commit baf4db7

Please sign in to comment.