An open API service indexing awesome lists of open source software.

https://github.com/yu2002s/dongyutvweb

这是一个电视浏览器项目,可以在电视上观看直播。
https://github.com/yu2002s/dongyutvweb

tv web

Last synced: 29 days ago
JSON representation

这是一个电视浏览器项目,可以在电视上观看直播。

Awesome Lists containing this project

README

          

## DongYuTVWeb

轻松在电视上观看直播,操作简单,适合家中老人使用,使用 WebView JavaScript 注入方式实现

## 交流/反馈

- QQ 群

[交流/反馈QQ群](./screenshots/qrcode_1773500386502.jpg)

- 公众号

[公众号](./app/src/main/assets/images/qrcode_mp.jpg)

- App发布网站

[App发布中心](http://app.jdynb.xyz)

- 相关问题解答

[相关问题解答](https://app.jdynb.xyz/faq)

## 下载

应该如何下载呢?

[App发布中心](http://app.jdynb.xyz)

1. 请在 [release](https://gitee.com/jdy2002/DongYuTvWeb/releases) 下载。
2. 如果你用的是手机,一般内核版本高,可以在 `release` 下载体积最小的,电视则下载体积大的。
3. 一般android版本高的话,下载体积最小的就行,实在不行都可以下载试试。
4. 市面上一般设备是64位的,请下载文件以`app_arm64`开头的文件, 老设备下载 `app_arm32` 开头的。

## 操作

- 方向上: 上一个频道
- 方向下: 下一个频道
- 左键: 声音减
- 右键: 声音加
- 确定键: 换台
- 数字键: 换台
- 菜单键:设置
- 连续按两下菜单键:换源
- 返回键:返回/退出
- #号键:强制刷新当前频道(有问题时使用)

> 使用老式的闭路电视操作,适合老人使用!

## 安装

### 远程安装

目前 1.0.13以上版本支持远程安装,需要用户在手机端,打开 设置 -> 远程安装 -> 通过 ip 和 端口连接到电视,点击远程安装即可
如何连接呢?tcl电视举例(其他品牌电视请问AI或百度):需要电视开启adb,进入 设置 -> 关于 -> 依次按下 上下左右 ,之后会显示扩展设置, 找到开启adb开关。然后打开设备的网络信息(WIFI详情),查看 IP V4地址,这个就是 ip,一般默认的端口填写 5555 即可

> 此方法也能用来更新,如果你不会使用,请使用下面方法安装和更新

### 其他方式

1. USB数据线连接电视和手机(或U盘),打开手机的通知,将`仅充电`改为`文件传输`,然后把安装包复制到电视,电视找到相关USB存储,安装apk
2. 通过无线方式安装,需要 android 11 以上,打开电视 `adb调试`(如何开启请百度),使用甲壳虫adb助手连接电视后安装

> 具体请百度或问AI,这里不进行详细描述

### 安装事项

**有些电视限制安装第三方App,需要使用 `adb` 禁用软件包安装程序**

tcl电视举例(其他品牌电视请问AI或百度):需要电视开启adb,进入 设置 -> 关于 -> 依次按下 `上下左右` ,之后会显示扩展设置,
找到`开启adb`开关,然后使用 https://www.jiakechong.net/ `甲壳虫adb助手` 连接到电视,
连接之前需要获取电视在`网络中的ip`,打开电视`网络信息`会有`ipv4` 地址,这个就是。然后用甲壳虫找到应用管理,
禁用`软件安装包程序`就可以安装app了。 安装完成之后**再把软件安装包程序启用**。

## 开机自启动

### 远程设置开机自启

1. 在手机端打开本App的 设置 -> 远程连接 -> 连接电视。
2. 点击操作按钮 -> 开启自启动 -> 设置默认桌面

### 手动设置

1. 查找电视的一些设置、App,看看有没有应用自启动相关的设置,对本应用开启自启动。可百度或 Ai 搜索自启动相关文章
2. 确保 App 已经打开过,并允许了**悬浮窗权限**
3. 设置默认的桌面,一般按 Home键(主页键) 会提示设置默认桌面,可以设置为本App并选择始终,或查找电视相关设置和App设置默认桌面

> 不保证所有电视都支持,仅在原生系统虚拟机 Android 12 测试通过

**如果上面的方法不奏效,可以尝试以下方法:**

1. [进入电视的工厂模式](https://www.cnblogs.com/05-hust/p/17089031.html)
2. 开启 USB 调试
3. 使用无线调试或数据线连接电视和电脑(手机-需要安装终端)[电脑安装adb](https://blog.csdn.net/chengxuyuanyy/article/details/149743294) 手机可以安装MT管理器,自带终端
4. 进入终端,输入以下命令
```shell
# 无线连接 adb connect ip:端口
adb devices # 查看设备是否连接成功
adb shell # 进入管理员模式
cmd package set-home-activity "xyz.jdynb.tv/.MainActivity" # 设置默认的启动项
reboot # 重启设备
```
> 注意:定制系统可能无效

## 无法播放问题

软件已内置X5内核,如果你设备安装失败会有问题请反馈!具体看【下载】

部分设备仍然无法正常使用,这时候需要手动下载并安装 [AndroidSystemWebView](https://www.apkmirror.com/apk/google-inc/android-system-webview/) (需要梯子)

这里提供以下下载地址:

- [平台自身维护WebView列表](http://app.jdynb.xyz/webview/)
- [ApkMirror](https://www.apkmirror.com/apk/google-inc/android-system-webview/) (需要梯子)
- [123网盘下载](https://www.123865.com/s/km2hjv-WOuOA) (32位)
- [Gitee下载](https://gitee.com/jdy2002/android-webview/releases)
- 加入公众号获取

## 自定义频道

目前不支持自定义源,如果你需要添加自己的源,请在公众号给我私信

频道完整配置文件 `main/assets/live-3.jsonc` [live-3.jsonc](https://gitee.com/jdy2002/DongYuTvWeb/raw/master/app/src/main/assets/lives/live-2026-03-19.jsonc)

最新调整:

- 去除 `number` 频道号码
- 变更 `live.jsonc` 为 `/lives/live-2026-03-19.jsonc`
- 增加频道上的 `children` 字段,支持换源

### 1. 对于没有验证的直播地址

修改 channel 频道列表

```json5
{
// ...
"channel": [
// ...
{
"channelType": "xxx", // 频道类型(分类名称)
"player": "common", // 播放器 id,这里写上,下方的 channelList 中没有设置的就默认为这个值
"channelList": [
{
"channelName": "香港卫视", // 频道名称 (需要唯一)
"tvLogo": "", // logo 目前没用到,可以不写
"number": 6, // 填写唯一的频道号码,用于数字换台 (废弃)
"player": "common", // 播放器 id,这个上方已经内置了,只需要写 common
"args": { // 播放参数,固定 liveUrl 即可
"liveUrl": "https://wwww.tv.com/playlist.m3u8?"
}
}
]
}
]
}
```

> 注意上方的 `liveUrl` 只能是普通的 m3u8地址,并且没有带时间戳等相关的参数

### 2. 对于开发人员

有些直播地址是有验证参数的,需要在脚本中进行处理生成

增加自定义频道,修改 `channel` 配置

```json5
// 省略其他
{
// ...
"channel": [
// ...
{
"channelType": "xx电视分类",
"player": "custom", // 设置自定义播放 id
"channelList": [
// ...
{
"channelName": "xx电视",
"tvLogo": "https://web.cmc.hebtv.com/cms/rmt0336/upload/Image/9/9tpklm/2025/07/18/30aaea69a1de46a881cef5d759d30b3e.png",
"number": 23
}
// ...
]
}
]
}
```

### 修改 `player` 配置

```json5
{
"player": {
"id": "custom", // 播放器 id,和上面定义的频道 player 参数一致
"name": "simple", // 内置的播放器名称,做了特殊处理,默认写这个
"script": { // 需要执行的 js 脚本
"init": [ // 在网页初始化完成时候执行的 js 脚本
"https://gitee.com/jdy2002/DongYuTvWeb/raw/master/app/src/main/assets/js/hebei/init.js"
],
"play": [ // 响应播放事件执行的 js 脚本
"https://gitee.com/jdy2002/DongYuTvWeb/raw/master/app/src/main/assets/js/hebei/play.js"
],
"resume_pause": [] // 暂停恢复执行的 js 脚本,这里留空,因为内部已经做了处理了
}
}
}
```

### 编辑 js 脚本

> 不需要云服务器,在 gitee 上建一个仓库,上面脚本地址自己写上仓库中实际的地址即可,具体格式参照上面的

> simple 播放器已经内置了 CryptoJS,所以这里就不需要引入了,直接使用即可

> 脚本中,`{{channelName}}` 会被替换成用户选择的频道名称,当然其他 args 参数也会被这样替换进入

> 内部已经关闭了跨域限制,可以直接请求

> `playLive` 是内置的方法,可以直接调用传入地址进行播放

> 注意:使用fetch请求时,有些会检测Referer请求头,请设置X-Referer请求头,内部会进行处理,
> 请求头数据请使用 `X-Body` 添加到请求头中,这里的数据将作为 Body 发送,

#### `init.js`, 对应上面 init 执行的脚本

##### 通过普通 fetch 发送请求

这种方式有限制,有些请求会检测 `Referer` 请求头,请设置自定义请求头 `X-Referer`,
推荐使用内置的 [HttpUtil](/app/src/main/assets/js/lib/dy-http-util.js) 发送请求

```js
(function() {
// 这里在播放地址上面加上需要的参数
window.addLiveUrlQuery = function (obj) {
let ts = parseInt(new Date().getTime() / (1000)) + (7200);
// CryptoJS 已经是内置的,可以直接使用
return obj.liveVideo[0].formats[0].url + "?t=" + ts + '&k=' + CryptoJS.MD5(obj.appCustomParams.movie.liveUri + obj.appCustomParams.movie.liveKey + ts);;
}

fetch("https://api.cmc.hebtv.com/scms/api/com/article/getArticleList?catalogId=32557&siteId=1", {
"headers": {
"accept": "application/json, text/javascript, */*; q=0.01",
"accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"sec-ch-ua": "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site",
// 如果不检测 Referer的话,可以正常写
"X-Referer": "https://www.hebtv.com/" // X-Referer 会自动在请求头添加 Referer,防止被检测
// 必须在设置了 X-Referer 之后,X-Body 才有效
"X-Body": "" // 这里设置请求体数据
},
"body": null,
"method": "GET"
}).then(res => {
return res.json()
}).then(res => {
const news = res.returnData.news
window.channelList = news.map(item => {
return {
title: item.title,
liveVideo: item.liveVideo,
appCustomParams: item.appCustomParams
}
})

// {{channelName}} 会被替换成频道名称,上面的 channel中的属性都会被传入到脚本中,这里可以进行自定义处理
const channelItem = window.channelList.find(item => item.title === '{{channelName}}')
const playUrl = window.addLiveUrlQuery(channelItem)
// 初始化时机执行的,所以这里需要对当前播放的频道进行播放,play.js 不会在初始化时候调用生效
playLive(playUrl)
})
})();
```
##### 使用 HttpUtil 发送请求

内置在 `simple` 播放器中的,可以直接使用,推荐使用这种方式发送请求

```js
const response = await HttpUtil.post(`https://feiying.litenews.cn/api/v1/auth/exchange?t=${now}&s=${s}`, body, {
headers: {
"accept": "*",
"accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
"content-type": "text/plain",
"priority": "u=1, i",
"sec-ch-ua": "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "cross-site",
"Referer": "https://v.iqilu.com/"
},
responseType: 'text'
})
const result = JSON.parse(decrypt(response.data))
playLive(result.data)
```

`HttpUtil` 的定义参见 [HttpUtil](/app/src/main/assets/js/lib/dy-http-util.js)

#### `play.js` 处理播放

```js
(function() {
const channelItem = window.channelList.find(item => item.title === '{{channelName}}')
const playUrl = window.addLiveUrlQuery(channelItem)
playLive(playUrl)
})();
```

### Base 播放器

对于加密的直播地址,需要使用 Base 播放器,直接通过浏览器加载来实现播放,去除播放器以外的节点,实现全屏播放。

[init.js实现](/app/src/main/assets/js/guangxi/init.js) 全屏并去除其他无用样式进行初始化

[play.js实现](/app/src/main/assets/js/guangxi/play.js) 直接使用 `window.location.href` 进行跳转

[resume_pause.js实现](/app/src/main/assets/js/guangxi/resume_pause.js) 查找按钮并点击

配置参考

> `url` 参数支持动态参数,使用 {{}} 方式传入

```json5
// player 配置
{
"id": "guangxi",
"name": "base", // 指定为 base
"url": "https://tv.gxtv.cn/channel/channelivePlay_{{id}}.html", // url 支持动态参数
// 设置指定的脚本
"script": {
"init": ["https://gitee.com/jdy2002/DongYuTvWeb/raw/master/app/src/main/assets/js/guangxi/init.js"],
"play": ["https://gitee.com/jdy2002/DongYuTvWeb/raw/master/app/src/main/assets/js/guangxi/play.js"],
"resume_pause": ["https://gitee.com/jdy2002/DongYuTvWeb/raw/master/app/src/main/assets/js/guangxi/resume_pause.js"]
}
}
```
> id 参数会注入到上面 url 的 {{id}} 中

```json5
// channel 配置
{
"channelType": "广西",
"player": "guangxi",
"channelList": [
{
"channelName": "广西卫视",
"args": {
"id": "e7a7ab7df9fe11e88bcfe41f13b60c62"
}
},
{
"channelName": "综艺旅游",
"args": {
"id": "f3335975f9fe11e88bcfe41f13b60c62"
}
}
]
}
```

### simple 播放器内置的 html 代码

[simple_player.html](/app/src/main/assets/html/simple_player.html)

```html



Document

body {
background: #000000;
}

#video {
width: 100vw;
height: 100vh;
}

#loading {
position: fixed;
left: 0;
top: 0;
z-index: 99;
width: 100vw;
height: 100vh;
font-size: 30px;
font-weight: bold;
color: white;
display:flex;
align-items: center;
justify-content: center;
}





加载中...请稍后

const video = document.querySelector("#video")

video.addEventListener('loadeddata', function() {
hideLoading()
})

function hideLoading() {
const loading = document.getElementById('loading');
loading.style.display = 'none';
}

function resumeOrPause() {
if (video.paused) {
video.play();
} else {
video.pause();
}
}

let hls = null;

function playLive(url, headers) {
video.volume = 1

// 检查是否支持 HLS.js 且为 m3u8 文件
if (Hls.isSupported() && url.includes('.m3u8')) {
if (hls) {
hls.destroy();
}

hls = new Hls({
debug: false,
xhrSetup: function(xhr, url) {
// xhr.setRequestHeader('X-Referer', headers['Referer'])
// 这里设置请求头
if (headers) {
for (const key in headers) {
xhr.setRequestHeader(key, headers[key]);
}
}
}
});

hls.loadSource(url);
hls.attachMedia(video);

hls.on(Hls.Events.MANIFEST_PARSED, function() {
video.play();
});

hls.on(Hls.Events.ERROR, function(event, data) {
console.error('HLS Error:' + JSON.stringify(data));
if (data.fatal) {
switch(data.type) {
case Hls.ErrorTypes.NETWORK_ERROR:
console.log('Network error, trying to recover');
hls.startLoad();
break;
case Hls.ErrorTypes.MEDIA_ERROR:
console.log('Media error, trying to recover');
hls.recoverMediaError();
break;
default:
console.log('Fatal error, destroying HLS instance');
hls.destroy();
break;
}
}
});
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
// iOS Safari 原生支持
video.src = url;
video.play();
} else {
// 其他格式直接播放
video.src = url;
video.play();
}
}

```

其他使用方法,请查看项目的 `/app/src/main/assets` 中的代码

### 配置多个源

具体配置参考 [channel 配置](/app/src/main/assets/lives/live-2026-03-19.jsonc)

```json5
// channel 配置
{
// ...
"channelName": "example",
// 设置多个源
"children": [
{
"channelName": "example-1",
"player": "base",
"args": {
"a": "b"
}
},
{
"channelName": "example-2",
"player": "common",
"args": {
liveUrl: "http://example.com/live.m3u8"
}
}
]
}
```

## 截图

![截图](./screenshots/MuMu12-20251223-161936.png)

![截图](./screenshots/MuMu12-20251223-161942.png)

![截图](./screenshots/MuMu12-20251223-161947.png)

## 注意

仅供学习交流,请勿用于商业用途