├── .github
├── FUNDING.yml
└── workflows
│ └── main.yml
├── .gitignore
├── .metadata
├── LICENSE
├── README-en.md
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── suntengfei
│ │ │ │ └── easy_tv_live
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── launch_image.png
│ │ │ ├── mipmap-ldpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── playstore-icon.png
│ │ │ ├── values-night-v27
│ │ │ └── styles.xml
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ ├── values-v27
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
└── images
│ ├── appreciate.png
│ ├── logo.png
│ └── video_bg.png
├── devtools_options.yaml
├── img_1.jpeg
├── img_2.jpeg
├── img_3.jpeg
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── icon-1024.png
│ │ │ ├── icon-20@2x.png
│ │ │ ├── icon-20@3x.png
│ │ │ ├── icon-29@2x.png
│ │ │ ├── icon-29@3x.png
│ │ │ ├── icon-38@2x.png
│ │ │ ├── icon-38@3x.png
│ │ │ ├── icon-40@2x.png
│ │ │ ├── icon-40@3x.png
│ │ │ ├── icon-60@2x.png
│ │ │ ├── icon-60@3x.png
│ │ │ ├── icon-64@2x.png
│ │ │ ├── icon-64@3x.png
│ │ │ ├── icon-68@2x.png
│ │ │ ├── icon-76@2x.png
│ │ │ └── icon-83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
└── RunnerTests
│ └── RunnerTests.swift
├── lib
├── channel_drawer_page.dart
├── entity
│ ├── font_model.dart
│ ├── play_channel_list_model.dart
│ └── subScribe_model.dart
├── generated
│ ├── intl
│ │ ├── messages_all.dart
│ │ ├── messages_en_US.dart
│ │ └── messages_zh_CN.dart
│ └── l10n.dart
├── l10n
│ ├── intl_en_US.arb
│ └── intl_zh_CN.arb
├── live_home_page.dart
├── main.dart
├── mobile_video_widget.dart
├── provider
│ ├── download_provider.dart
│ └── theme_provider.dart
├── router_keys.dart
├── setting
│ ├── qr_scan_page.dart
│ ├── reward_page.dart
│ ├── setting_beautify_page.dart
│ ├── setting_font_page.dart
│ ├── setting_page.dart
│ └── subscribe_page.dart
├── table_video_widget.dart
├── tv
│ ├── html_string.dart
│ ├── tv_appreciate_page.dart
│ ├── tv_channel_drawer_page.dart
│ ├── tv_page.dart
│ └── tv_setting_page.dart
├── util
│ ├── bing_util.dart
│ ├── check_version_util.dart
│ ├── date_util.dart
│ ├── device_sync_util.dart
│ ├── env_util.dart
│ ├── epg_util.dart
│ ├── font_util.dart
│ ├── http_util.dart
│ ├── latency_checker_util.dart
│ ├── log_util.dart
│ └── m3u_util.dart
└── widget
│ ├── date_position_widget.dart
│ ├── empty_page.dart
│ ├── focus_button.dart
│ ├── focus_card.dart
│ ├── no_scroller_behavior.dart
│ ├── update_download_btn.dart
│ ├── video_hold_bg.dart
│ └── volume_brightness_widget.dart
├── linux
├── .gitignore
├── CMakeLists.txt
├── flutter
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
├── main.cc
├── my_application.cc
└── my_application.h
├── macos
├── .gitignore
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── icon-128.png
│ │ │ ├── icon-128@2x.png
│ │ │ ├── icon-16.png
│ │ │ ├── icon-16@2x.png
│ │ │ ├── icon-256.png
│ │ │ ├── icon-256@2x.png
│ │ │ ├── icon-32.png
│ │ │ ├── icon-32@2x.png
│ │ │ ├── icon-512.png
│ │ │ └── icon-512@2x.png
│ ├── Base.lproj
│ │ └── MainMenu.xib
│ ├── Configs
│ │ ├── AppInfo.xcconfig
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── Warnings.xcconfig
│ ├── DebugProfile.entitlements
│ ├── Info.plist
│ ├── MainFlutterWindow.swift
│ ├── Release.entitlements
│ └── zh-Hans.lproj
│ │ └── MainMenu.strings
└── RunnerTests
│ └── RunnerTests.swift
├── pubspec.lock
├── pubspec.yaml
├── reward.txt
├── temp
├── test
└── widget_test.dart
├── versions.json
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter
├── CMakeLists.txt
├── generated_plugin_registrant.cc
├── generated_plugin_registrant.h
└── generated_plugins.cmake
├── runner
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── resources
│ └── app_icon.ico
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
└── system32
├── msvcp140.dll
├── vcruntime140.dll
└── vcruntime140_1.dll
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | # open_collective: easytv
3 | buy_me_a_coffee: aiyakuaile
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
17 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
18 | - platform: android
19 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
20 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
21 | - platform: ios
22 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
23 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
24 | - platform: linux
25 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
26 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
27 | - platform: macos
28 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
29 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
30 | - platform: web
31 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
32 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
33 | - platform: windows
34 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
35 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
2 |
3 | You are free to:
4 | - Share: copy and redistribute the material in any medium or format
5 | - Adapt: remix, transform, and build upon the material
6 |
7 | Under the following terms:
8 | - Attribution: You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
9 | - NonCommercial: You may not use the material for commercial purposes.
10 | - ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
11 |
12 | No additional restrictions: You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
13 |
14 | View the full license at: https://creativecommons.org/licenses/by-nc-sa/4.0/
15 |
--------------------------------------------------------------------------------
/README-en.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](README-en.md)
4 | [](README.md)
5 |
6 | # EasyTV
7 | EasyTV is a lightweight IPTV player that supports all platforms and Android TV large screens. It is simple and easy to use. Welcome to download and experience!
8 |
9 | #### Features
10 | - [x] IPTV player that supports all platforms
11 | - [x] Ability to add live source links independently
12 | - [x] Clean and simple UI interaction
13 | - [x] Support for parsing .m3u files
14 | - [x] Support for parsing .txt files (added in v1.5.0)
15 | - [x] Supports Android TV (added in v2.0.0, supports Android OS 5.0+)
16 | - [x] Desktop support (added in v2.6.0)
17 | - [x] Support switching fonts and font sizes
18 | - [x] Support using Bing daily images as video backgrounds
19 | - [x] Automatically merge and group the same channels
20 | - [x] Support channel switching via number keys
21 | - [x] Support channel switching with up and down keys (needs to be manually enabled in '设置->实现设置->上下键切换频道')
22 |
23 | #### TV Version Interaction
24 | - Up key: Switch lines
25 | - Down key: Modify or add subscription sources
26 | - OK key: Confirm operation, display channel list
27 |
28 | #### Fonts
29 | The fonts that support switching need to use the [easy_tv_font](https://github.com/aiyakuaile/easy_tv_font) project. If you want to add other fonts, please follow the instructions in this project.
30 |
31 | > For iOS, after downloading the ipa, you need to sign it for installation using tools.
32 | > If the added live source is an IPv6 address, please confirm that your current network can access IPv6 addresses normally.
33 | > [IPv6 Test Address](https://v6t.ipip.net/)
34 |
35 | ## Preview
36 |
37 | 
38 |
39 |  | 
40 | ---|---
41 |
42 |
43 | ## Sponsorship
44 | If you find this project useful, consider sponsoring me for a cup of coffee ❤
45 |
46 |
47 |
48 | For easier tracking of sponsors, we recommend using WeChat Scan to make a direct donation. Thank
49 |
50 |
51 | ## 贡献
52 |
53 |
54 |
55 |
56 |
57 | ### Star History
58 |
59 |
65 |
71 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](README-en.md)
4 | [](README.md)
5 |
6 | # 极简TV
7 |
8 | 极简TV,一款轻量级,支持全平台及安卓电视大屏的IPTV播放器,简洁易用,欢迎下载体验!
9 |
10 | #### TV源
11 | 本应用自带的节目源,来源自[IPTV-API](https://github.com/Guovin/iptv-api)这个项目,感谢此项目的开源与分享,推荐在应用中自主订阅```https://raw.githubusercontent.com/Guovin/iptv-api/gd/output/result.m3u```或者```https://cdn.jsdelivr.net/gh/Guovin/iptv-api@gd/output/result.m3u```,来保持节目源的及时更新!最新订阅链接请前往此项目查看。
12 |
13 | #### 功能
14 |
15 | - [x] 全平台支持的IPTV播放器
16 | - [x] 可自主添加直播源链接
17 | - [x] 干净简洁的UI交互
18 | - [x] 增加对.m3u文件的解析支持
19 | - [x] 增加对.txt文件的解析支持(v1.5.0新增)
20 | - [x] 支持Android TV(v2.0.0新增,支持AndroidOS 5.0+)
21 | - [x] 桌面端支持(v2.6.0新增)
22 | - [x] 支持切换字体和字体大小
23 | - [x] 支持使用Bing每日图片作为视频背景
24 | - [x] 相同频道自动合并,自动分组
25 | - [x] 支持数字换台
26 | - [x] 支持上下键换台(需要在'设置->实验设置->上下键切换频道'中手动打开)
27 |
28 | ### TV版交互
29 |
30 | ⬆️上键:切换频道或线路
31 |
32 | ⬇️下键:切换频道或进入设置页面
33 |
34 | 🆗ok键:确认操作、显示频道列表
35 |
36 | ### 字体
37 | 支持切换的字体需要使用[easy_tv_font](https://github.com/aiyakuaile/easy_tv_font)这个项目,
38 | 您如果想要添加其他字体,请按照这个项目的提示进行操作。
39 |
40 | > ios 支持15.5.0+以上版本,下载ipa后需要自行签名安装,例如:爱思助手,轻松签,巨魔等
41 |
42 | > 如果添加的直播源是ipv6地址,请先确认您当前的网络是否可以正常访问ipv6地址
43 | > [ipv6测试地址](https://v6t.ipip.net/)
44 |
45 | ## 预览
46 |
47 | 
48 |
49 |  | 
50 | ---|---
51 |
52 |
53 | ## 赞助
54 | 如果觉得此项目有用,可以考虑赞助我喝杯咖啡❤
55 |
56 |
57 |
58 | 为了方便统计打赏名单,推荐使用微信扫一扫直接赞赏,感谢您的支持!
59 |
60 |
61 |
62 |
63 | ## 贡献
64 |
65 |
66 |
67 |
68 |
69 | ## Star History
70 |
71 |
77 |
83 |
87 |
88 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5 | id "dev.flutter.flutter-gradle-plugin"
6 | }
7 |
8 | def localProperties = new Properties()
9 | def localPropertiesFile = rootProject.file("local.properties")
10 | if (localPropertiesFile.exists()) {
11 | localPropertiesFile.withReader("UTF-8") { reader ->
12 | localProperties.load(reader)
13 | }
14 | }
15 |
16 | def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
17 | if (flutterVersionCode == null) {
18 | flutterVersionCode = "1"
19 | }
20 |
21 | def flutterVersionName = localProperties.getProperty("flutter.versionName")
22 | if (flutterVersionName == null) {
23 | flutterVersionName = "1.0"
24 | }
25 |
26 | android {
27 | namespace = "com.suntengfei.easy_tv_live"
28 | compileSdk = 34
29 | // ndkVersion = flutter.ndkVersion
30 |
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_17
33 | targetCompatibility = JavaVersion.VERSION_17
34 | }
35 |
36 | kotlinOptions {
37 | jvmTarget = "17"
38 | }
39 |
40 | defaultConfig {
41 | applicationId = "com.easy.tvlive"
42 | minSdk = 21
43 | targetSdk = 34
44 | versionCode = flutterVersionCode.toInteger()
45 | versionName = flutterVersionName
46 | }
47 |
48 | signingConfigs {
49 | release {
50 | storeFile file("../../keystore.jks")
51 | storePassword System.getenv("KEYSTORE_PASSWORD")
52 | keyAlias System.getenv("KEY_ALIAS")
53 | keyPassword System.getenv("KEY_ALIAS_PASSWORD")
54 | }
55 | }
56 |
57 | buildTypes {
58 | release {
59 | signingConfig = signingConfigs.debug
60 | signingConfig = signingConfigs.release
61 | ndk {
62 | abiFilters "arm64-v8a", "armeabi-v7a"
63 | }
64 | }
65 | }
66 | }
67 |
68 | flutter {
69 | source = "../.."
70 | }
71 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
39 |
48 |
52 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
65 |
68 |
69 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/suntengfei/easy_tv_live/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.suntengfei.easy_tv_live
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | -
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/launch_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-hdpi/launch_image.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/playstore-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/android/app/src/main/res/playstore-icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night-v27/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
15 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-v27/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = "../build"
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(":app")
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 | id "com.android.application" version "8.5.0" apply false
22 | id "org.jetbrains.kotlin.android" version "1.9.23" apply false
23 | }
24 |
25 | include ":app"
26 |
--------------------------------------------------------------------------------
/assets/images/appreciate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/assets/images/appreciate.png
--------------------------------------------------------------------------------
/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/assets/images/logo.png
--------------------------------------------------------------------------------
/assets/images/video_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/assets/images/video_bg.png
--------------------------------------------------------------------------------
/devtools_options.yaml:
--------------------------------------------------------------------------------
1 | description: This file stores settings for Dart & Flutter DevTools.
2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
3 | extensions:
4 |
--------------------------------------------------------------------------------
/img_1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/img_1.jpeg
--------------------------------------------------------------------------------
/img_2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/img_2.jpeg
--------------------------------------------------------------------------------
/img_3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/img_3.jpeg
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '12.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | target 'RunnerTests' do
36 | inherit! :search_paths
37 | end
38 | end
39 |
40 | post_install do |installer|
41 | installer.pods_project.targets.each do |target|
42 | flutter_additional_ios_build_settings(target)
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "size": "20x20",
5 | "idiom": "universal",
6 | "filename": "icon-20@2x.png",
7 | "scale": "2x",
8 | "platform": "ios"
9 | },
10 | {
11 | "size": "20x20",
12 | "idiom": "universal",
13 | "filename": "icon-20@3x.png",
14 | "scale": "3x",
15 | "platform": "ios"
16 | },
17 | {
18 | "size": "29x29",
19 | "idiom": "universal",
20 | "filename": "icon-29@2x.png",
21 | "scale": "2x",
22 | "platform": "ios"
23 | },
24 | {
25 | "size": "29x29",
26 | "idiom": "universal",
27 | "filename": "icon-29@3x.png",
28 | "scale": "3x",
29 | "platform": "ios"
30 | },
31 | {
32 | "size": "38x38",
33 | "idiom": "universal",
34 | "filename": "icon-38@2x.png",
35 | "scale": "2x",
36 | "platform": "ios"
37 | },
38 | {
39 | "size": "38x38",
40 | "idiom": "universal",
41 | "filename": "icon-38@3x.png",
42 | "scale": "3x",
43 | "platform": "ios"
44 | },
45 | {
46 | "size": "40x40",
47 | "idiom": "universal",
48 | "filename": "icon-40@2x.png",
49 | "scale": "2x",
50 | "platform": "ios"
51 | },
52 | {
53 | "size": "40x40",
54 | "idiom": "universal",
55 | "filename": "icon-40@3x.png",
56 | "scale": "3x",
57 | "platform": "ios"
58 | },
59 | {
60 | "size": "60x60",
61 | "idiom": "universal",
62 | "filename": "icon-60@2x.png",
63 | "scale": "2x",
64 | "platform": "ios"
65 | },
66 | {
67 | "size": "60x60",
68 | "idiom": "universal",
69 | "filename": "icon-60@3x.png",
70 | "scale": "3x",
71 | "platform": "ios"
72 | },
73 | {
74 | "size": "64x64",
75 | "idiom": "universal",
76 | "filename": "icon-64@2x.png",
77 | "scale": "2x",
78 | "platform": "ios"
79 | },
80 | {
81 | "size": "64x64",
82 | "idiom": "universal",
83 | "filename": "icon-64@3x.png",
84 | "scale": "3x",
85 | "platform": "ios"
86 | },
87 | {
88 | "size": "68x68",
89 | "idiom": "universal",
90 | "filename": "icon-68@2x.png",
91 | "scale": "2x",
92 | "platform": "ios"
93 | },
94 | {
95 | "size": "76x76",
96 | "idiom": "universal",
97 | "filename": "icon-76@2x.png",
98 | "scale": "2x",
99 | "platform": "ios"
100 | },
101 | {
102 | "size": "83.5x83.5",
103 | "idiom": "universal",
104 | "filename": "icon-83.5@2x.png",
105 | "scale": "2x",
106 | "platform": "ios"
107 | },
108 | {
109 | "size": "1024x1024",
110 | "idiom": "universal",
111 | "filename": "icon-1024.png",
112 | "scale": "1x",
113 | "platform": "ios"
114 | }
115 | ],
116 | "info": {
117 | "version": 1,
118 | "author": "icon.wuruihong.com"
119 | }
120 | }
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-38@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-64@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-68@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aiyakuaile/easy_tv_live/968be6198437f8fb0c9dab9e6f0faec31a63c114/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | 极简TV
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | easy_tv_live
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | UIApplicationSupportsIndirectInputEvents
30 |
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 | UISupportedInterfaceOrientations~ipad
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationPortraitUpsideDown
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | NSAppTransportSecurity
49 |
50 | NSAllowsArbitraryLoads
51 |
52 |
53 | NSCameraUsageDescription
54 | Can I use the camera please?
55 |
56 |
57 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/lib/entity/font_model.dart:
--------------------------------------------------------------------------------
1 | class FontModel {
2 | FontModel(
3 | {this.id, this.fontName, this.fontKey, this.fontType, this.progress});
4 | FontModel.fromJson(dynamic json) {
5 | id = json['id'];
6 | fontName = json['font_name'];
7 | fontKey = json['font_key'];
8 | fontType = json['font_type'];
9 | progress = json['progress'] ?? 0.0;
10 | }
11 | String? id;
12 | String? fontName;
13 | String? fontKey;
14 | String? fontType;
15 | double? progress;
16 | }
17 |
--------------------------------------------------------------------------------
/lib/entity/play_channel_list_model.dart:
--------------------------------------------------------------------------------
1 | enum PlayListType {
2 | m3u,
3 | txt,
4 | }
5 |
6 | class PlayChannelListModel {
7 | PlayChannelListModel({
8 | this.epgUrl,
9 | this.type,
10 | this.playList,
11 | this.playGroupIndex = 0,
12 | this.playChannelIndex = 0,
13 | });
14 |
15 | PlayChannelListModel.fromJson(dynamic json) {
16 | playGroupIndex = 0;
17 | playChannelIndex = 0;
18 | epgUrl = json['epgUrl'];
19 | type = PlayListType.values[json['type']];
20 | if (json['playList'] != null) {
21 | playList = [];
22 | json['playList'].forEach((v) {
23 | playList?.add(PlayModel.fromJson(v));
24 | });
25 | }
26 | }
27 |
28 | String? epgUrl;
29 | List? playList;
30 | PlayListType? type;
31 | int? playGroupIndex;
32 | int? playChannelIndex;
33 | }
34 |
35 | class PlayModel {
36 | PlayModel({
37 | this.group,
38 | this.channel,
39 | });
40 |
41 | PlayModel.fromJson(dynamic json) {
42 | group = json['group'];
43 | if (json['channel'] != null) {
44 | channel = [];
45 | json['channel'].forEach((v) {
46 | channel?.add(Channel.fromJson(v));
47 | });
48 | }
49 | }
50 |
51 | String? group;
52 | List? channel;
53 |
54 | PlayModel copyWith({
55 | String? group,
56 | List? channel,
57 | }) =>
58 | PlayModel(
59 | group: group ?? this.group,
60 | channel: channel ?? this.channel,
61 | );
62 |
63 | Map toJson() {
64 | final map = {};
65 | map['group'] = group;
66 | if (channel != null) {
67 | map['channel'] = channel?.map((v) => v.toJson()).toList();
68 | }
69 | return map;
70 | }
71 | }
72 |
73 | class Channel {
74 | Channel({
75 | this.id,
76 | this.logo,
77 | this.title,
78 | this.urls,
79 | this.serialNum,
80 | this.groupIndex,
81 | this.channelIndex,
82 | });
83 |
84 | Channel.fromJson(dynamic json) {
85 | id = json['id'];
86 | logo = json['logo'];
87 | title = json['title'];
88 | urls = json['urls'] != null ? json['urls'].cast() : [];
89 | serialNum = json['serialNum'];
90 | groupIndex = json['groupIndex'];
91 | channelIndex = json['channelIndex'];
92 | }
93 | int? serialNum;
94 | String? id;
95 | String? logo;
96 | String? title;
97 | List? urls;
98 | int? groupIndex;
99 | int? channelIndex;
100 |
101 | Channel copyWith({
102 | String? id,
103 | String? logo,
104 | String? title,
105 | List? urls,
106 | int? serialNum,
107 | int? groupIndex,
108 | int? channelIndex,
109 | }) =>
110 | Channel(
111 | id: id ?? this.id,
112 | logo: logo ?? this.logo,
113 | title: title ?? this.title,
114 | urls: urls ?? this.urls,
115 | serialNum: serialNum ?? this.serialNum,
116 | groupIndex: groupIndex ?? this.groupIndex,
117 | channelIndex: channelIndex ?? this.channelIndex,
118 | );
119 |
120 | Map toJson() {
121 | final map = {};
122 | map['id'] = id;
123 | map['logo'] = logo;
124 | map['title'] = title;
125 | map['urls'] = urls;
126 | map['serialNum'] = serialNum;
127 | map['groupIndex'] = groupIndex;
128 | map['channelIndex'] = channelIndex;
129 | return map;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/lib/entity/subScribe_model.dart:
--------------------------------------------------------------------------------
1 | class SubScribeModel {
2 | SubScribeModel({this.time, this.link, this.selected = false});
3 |
4 | SubScribeModel.fromJson(dynamic json) {
5 | time = json['time'];
6 | link = json['link'];
7 | selected = json['selected'] ?? false;
8 | }
9 | String? time;
10 | String? link;
11 | bool? selected;
12 |
13 | Map toJson() {
14 | final map = {};
15 | map['time'] = time;
16 | map['link'] = link;
17 | map['selected'] = selected ?? false;
18 | return map;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_all.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that looks up messages for specific locales by
3 | // delegating to the appropriate library.
4 |
5 | // Ignore issues from commonly used lints in this file.
6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new
7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment
9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
10 | // ignore_for_file:comment_references
11 |
12 | import 'dart:async';
13 |
14 | import 'package:flutter/foundation.dart';
15 | import 'package:intl/intl.dart';
16 | import 'package:intl/message_lookup_by_library.dart';
17 | import 'package:intl/src/intl_helpers.dart';
18 |
19 | import 'messages_en_US.dart' as messages_en_us;
20 | import 'messages_zh_CN.dart' as messages_zh_cn;
21 |
22 | typedef Future LibraryLoader();
23 | Map _deferredLibraries = {
24 | 'en_US': () => new SynchronousFuture(null),
25 | 'zh_CN': () => new SynchronousFuture(null),
26 | };
27 |
28 | MessageLookupByLibrary? _findExact(String localeName) {
29 | switch (localeName) {
30 | case 'en_US':
31 | return messages_en_us.messages;
32 | case 'zh_CN':
33 | return messages_zh_cn.messages;
34 | default:
35 | return null;
36 | }
37 | }
38 |
39 | /// User programs should call this before using [localeName] for messages.
40 | Future initializeMessages(String localeName) {
41 | var availableLocale = Intl.verifiedLocale(
42 | localeName, (locale) => _deferredLibraries[locale] != null,
43 | onFailure: (_) => null);
44 | if (availableLocale == null) {
45 | return new SynchronousFuture(false);
46 | }
47 | var lib = _deferredLibraries[availableLocale];
48 | lib == null ? new SynchronousFuture(false) : lib();
49 | initializeInternalMessageLookup(() => new CompositeMessageLookup());
50 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
51 | return new SynchronousFuture(true);
52 | }
53 |
54 | bool _messagesExistFor(String locale) {
55 | try {
56 | return _findExact(locale) != null;
57 | } catch (e) {
58 | return false;
59 | }
60 | }
61 |
62 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
63 | var actualLocale =
64 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
65 | if (actualLocale == null) return null;
66 | return _findExact(actualLocale);
67 | }
68 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_zh_CN.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a zh_CN locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
12 |
13 | import 'package:intl/intl.dart';
14 | import 'package:intl/message_lookup_by_library.dart';
15 |
16 | final messages = new MessageLookup();
17 |
18 | typedef String MessageIfAbsent(String messageStr, List args);
19 |
20 | class MessageLookup extends MessageLookupByLibrary {
21 | String get localeName => 'zh_CN';
22 |
23 | static String m0(index) => "线路${index}";
24 |
25 | static String m1(line, channel) => "线路${line}播放: ${channel}";
26 |
27 | static String m2(code) => "响应异常${code}";
28 |
29 | static String m3(version) => "新版本v${version}";
30 |
31 | static String m4(address) => "推送地址:${address}";
32 |
33 | static String m5(line) => "切换线路${line} ...";
34 |
35 | final messages = _notInlinedMessages(_notInlinedMessages);
36 | static Map _notInlinedMessages(_) => {
37 | "addDataSource": MessageLookupByLibrary.simpleMessage("添加订阅源"),
38 | "addFiledHintText":
39 | MessageLookupByLibrary.simpleMessage("请输入或粘贴.m3u或.txt格式的订阅源链接"),
40 | "addNoHttpLink":
41 | MessageLookupByLibrary.simpleMessage("请输入http/https链接"),
42 | "addRepeat": MessageLookupByLibrary.simpleMessage("已添加过此订阅源"),
43 | "appName": MessageLookupByLibrary.simpleMessage("极简TV"),
44 | "checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
45 | "createTime": MessageLookupByLibrary.simpleMessage("创建时间"),
46 | "dataSourceContent": MessageLookupByLibrary.simpleMessage("确定添加此数据源吗?"),
47 | "defaultText": MessageLookupByLibrary.simpleMessage("默认"),
48 | "delete": MessageLookupByLibrary.simpleMessage("删除"),
49 | "dialogCancel": MessageLookupByLibrary.simpleMessage("取消"),
50 | "dialogConfirm": MessageLookupByLibrary.simpleMessage("确定"),
51 | "dialogDeleteContent":
52 | MessageLookupByLibrary.simpleMessage("确定删除此订阅吗?"),
53 | "dialogTitle": MessageLookupByLibrary.simpleMessage("温馨提示"),
54 | "findNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"),
55 | "fullScreen": MessageLookupByLibrary.simpleMessage("全屏切换"),
56 | "getDefaultError": MessageLookupByLibrary.simpleMessage("获取默认数据源失败"),
57 | "homePage": MessageLookupByLibrary.simpleMessage("主页"),
58 | "inUse": MessageLookupByLibrary.simpleMessage("使用中"),
59 | "landscape": MessageLookupByLibrary.simpleMessage("横屏模式"),
60 | "latestVersion": MessageLookupByLibrary.simpleMessage("已是最新版本"),
61 | "lineIndex": m0,
62 | "lineToast": m1,
63 | "loading": MessageLookupByLibrary.simpleMessage("正在加载"),
64 | "netBadResponse": m2,
65 | "netCancel": MessageLookupByLibrary.simpleMessage("请求取消"),
66 | "netReceiveTimeout": MessageLookupByLibrary.simpleMessage("响应超时"),
67 | "netSendTimeout": MessageLookupByLibrary.simpleMessage("请求超时"),
68 | "netTimeOut": MessageLookupByLibrary.simpleMessage("连接超时"),
69 | "newVersion": m3,
70 | "noEPG": MessageLookupByLibrary.simpleMessage("暂无节目信息"),
71 | "okRefresh": MessageLookupByLibrary.simpleMessage("【OK键】刷新"),
72 | "parseError": MessageLookupByLibrary.simpleMessage("解析数据源出错"),
73 | "pasterContent":
74 | MessageLookupByLibrary.simpleMessage("复制订阅源后,回到此页面可自动添加订阅源"),
75 | "playError": MessageLookupByLibrary.simpleMessage("此视频无法播放,请更换其它频道"),
76 | "playReconnect": MessageLookupByLibrary.simpleMessage("出错了,尝试重新连接..."),
77 | "portrait": MessageLookupByLibrary.simpleMessage("竖屏模式"),
78 | "pushAddress": m4,
79 | "refresh": MessageLookupByLibrary.simpleMessage("刷新"),
80 | "releaseHistory": MessageLookupByLibrary.simpleMessage("发布历史"),
81 | "setDefault": MessageLookupByLibrary.simpleMessage("设为默认"),
82 | "settings": MessageLookupByLibrary.simpleMessage("设置"),
83 | "subscribe": MessageLookupByLibrary.simpleMessage("IPTV订阅"),
84 | "switchLine": m5,
85 | "tipChangeLine": MessageLookupByLibrary.simpleMessage("切换线路"),
86 | "tipChannelList": MessageLookupByLibrary.simpleMessage("频道列表"),
87 | "tvParseParma": MessageLookupByLibrary.simpleMessage("参数错误"),
88 | "tvParsePushError": MessageLookupByLibrary.simpleMessage("请推送正确的链接"),
89 | "tvParseSuccess": MessageLookupByLibrary.simpleMessage("推送成功"),
90 | "tvPushContent": MessageLookupByLibrary.simpleMessage(
91 | "注意:必须在同一WIFI网络环境下\n1、使用极简TV手机版扫码可快速完成数据添加和双向同步\n2、使用其他App扫码,在扫码结果页,输入新的订阅源,点击页面中的推送即可添加成功"),
92 | "tvScanTip": MessageLookupByLibrary.simpleMessage("扫码添加订阅源"),
93 | "update": MessageLookupByLibrary.simpleMessage("立即更新"),
94 | "updateContent": MessageLookupByLibrary.simpleMessage("更新内容")
95 | };
96 | }
97 |
--------------------------------------------------------------------------------
/lib/l10n/intl_en_US.arb:
--------------------------------------------------------------------------------
1 | {
2 | "appName": "EasyTV",
3 | "loading": "Loading",
4 | "lineToast": "Line {line} playing: {channel}",
5 | "@lineToast": {
6 | "placeholders": {
7 | "line": {},
8 | "channel": {}
9 | }
10 | },
11 | "playError": "This video cannot be played, please switch to another channel",
12 | "switchLine": "Switching to line {line} ...",
13 | "@switchLine": {
14 | "placeholders": {
15 | "line": {}
16 | }
17 | },
18 | "playReconnect": "An error occurred, trying to reconnect...",
19 | "lineIndex": "Line {index}",
20 | "@lineIndex": {
21 | "placeholders": {
22 | "index": {}
23 | }
24 | },
25 | "tipChannelList": "Channel List",
26 | "tipChangeLine": "Switch Line",
27 | "portrait": "Portrait Mode",
28 | "landscape": "Landscape Mode",
29 | "fullScreen": "Full Screen Toggle",
30 | "settings": "Settings",
31 | "homePage": "Home Page",
32 | "releaseHistory": "Release History",
33 | "checkUpdate": "Check for Updates",
34 | "newVersion": "New Version v{version}",
35 | "@newVersion": {
36 | "placeholders": {
37 | "version": {}
38 | }
39 | },
40 | "update": "Update Now",
41 | "latestVersion": "You are on the latest version",
42 | "findNewVersion": "New version found",
43 | "updateContent": "Update Content",
44 | "dialogTitle": "Friendly Reminder",
45 | "dataSourceContent": "Are you sure you want to add this data source?",
46 | "dialogCancel": "Cancel",
47 | "dialogConfirm": "Confirm",
48 | "subscribe": "IPTV Subscription",
49 | "createTime": "Creation Time",
50 | "dialogDeleteContent": "Are you sure you want to delete this subscription?",
51 | "delete": "Delete",
52 | "setDefault": "Set as Default",
53 | "inUse": "In Use",
54 | "tvParseParma": "Parameter Error",
55 | "tvParseSuccess": "Push Successful",
56 | "tvParsePushError": "Please push the correct link",
57 | "tvScanTip": "Scan to add subscription source",
58 | "pushAddress": "Push Address: {address}",
59 | "@pushAddress": {
60 | "placeholders": {
61 | "address": {}
62 | }
63 | },
64 | "tvPushContent": "On the scan result page, enter the new subscription source and click the push button to add successfully",
65 | "pasterContent": "After copying the subscription source, return to this page to automatically add the subscription source",
66 | "addDataSource": "Add Subscription Source",
67 | "addFiledHintText": "Please enter or paste the .m3u or .txt format subscription link",
68 | "addRepeat": "This subscription source has been added",
69 | "addNoHttpLink": "Please enter an http/https link",
70 | "netTimeOut": "Connection timed out",
71 | "netSendTimeout": "Request timed out",
72 | "netReceiveTimeout": "Response timed out",
73 | "netBadResponse": "Abnormal response {code}",
74 | "@netBadResponse": {
75 | "placeholders": {
76 | "code": {}
77 | }
78 | },
79 | "netCancel": "Request Cancelled",
80 | "parseError": "Error parsing data source",
81 | "defaultText": "Default",
82 | "getDefaultError": "Failed to get the default data source",
83 | "okRefresh": "【OK key】 Refresh",
84 | "refresh": "Refresh",
85 | "noEPG": "NO EPG DATA"
86 | }
--------------------------------------------------------------------------------
/lib/l10n/intl_zh_CN.arb:
--------------------------------------------------------------------------------
1 | {
2 | "appName": "极简TV",
3 | "loading": "正在加载",
4 | "lineToast": "线路{line}播放: {channel}",
5 | "@lineToast": {
6 | "placeholders": {
7 | "line": {},
8 | "channel": {}
9 | }
10 | },
11 | "playError": "此视频无法播放,请更换其它频道",
12 | "switchLine": "切换线路{line} ...",
13 | "@switchLine": {
14 | "placeholders": {
15 | "line": {}
16 | }
17 | },
18 | "playReconnect": "出错了,尝试重新连接...",
19 | "lineIndex": "线路{index}",
20 | "@lineIndex": {
21 | "placeholders": {
22 | "index": {}
23 | }
24 | },
25 | "tipChannelList": "频道列表",
26 | "tipChangeLine": "切换线路",
27 | "portrait": "竖屏模式",
28 | "landscape": "横屏模式",
29 | "fullScreen": "全屏切换",
30 | "settings": "设置",
31 | "homePage": "主页",
32 | "releaseHistory": "发布历史",
33 | "checkUpdate": "检查更新",
34 | "newVersion": "新版本v{version}",
35 | "@newVersion": {
36 | "placeholders": {
37 | "version": {}
38 | }
39 | },
40 | "update": "立即更新",
41 | "latestVersion": "已是最新版本",
42 | "findNewVersion": "发现新版本",
43 | "updateContent": "更新内容",
44 | "dialogTitle": "温馨提示",
45 | "dataSourceContent": "确定添加此数据源吗?",
46 | "dialogCancel": "取消",
47 | "dialogConfirm": "确定",
48 | "subscribe": "IPTV订阅",
49 | "createTime": "创建时间",
50 | "dialogDeleteContent": "确定删除此订阅吗?",
51 | "delete": "删除",
52 | "setDefault": "设为默认",
53 | "inUse": "使用中",
54 | "tvParseParma": "参数错误",
55 | "tvParseSuccess": "推送成功",
56 | "tvParsePushError": "请推送正确的链接",
57 | "tvScanTip": "扫码添加订阅源",
58 | "pushAddress": "推送地址:{address}",
59 | "@pushAddress": {
60 | "placeholders": {
61 | "address": {}
62 | }
63 | },
64 | "tvPushContent": "注意:必须在同一WIFI网络环境下\n1、使用极简TV手机版扫码可快速完成数据添加和双向同步\n2、使用其他App扫码,在扫码结果页,输入新的订阅源,点击页面中的推送即可添加成功",
65 | "pasterContent": "复制订阅源后,回到此页面可自动添加订阅源",
66 | "addDataSource": "添加订阅源",
67 | "addFiledHintText": "请输入或粘贴.m3u或.txt格式的订阅源链接",
68 | "addRepeat": "已添加过此订阅源",
69 | "addNoHttpLink": "请输入http/https链接",
70 | "netTimeOut": "连接超时",
71 | "netSendTimeout": "请求超时",
72 | "netReceiveTimeout": "响应超时",
73 | "netBadResponse": "响应异常{code}",
74 | "@netBadResponse": {
75 | "placeholders": {
76 | "code": {}
77 | }
78 | },
79 | "netCancel": "请求取消",
80 | "parseError": "解析数据源出错",
81 | "defaultText": "默认",
82 | "getDefaultError": "获取默认数据源失败",
83 | "okRefresh": "【OK键】刷新",
84 | "refresh": "刷新",
85 | "noEPG": "暂无节目信息"
86 | }
--------------------------------------------------------------------------------
/lib/mobile_video_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_tv_live/router_keys.dart';
2 | import 'package:easy_tv_live/setting/subscribe_page.dart';
3 | import 'package:easy_tv_live/table_video_widget.dart';
4 | import 'package:easy_tv_live/widget/empty_page.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:sp_util/sp_util.dart';
7 | import 'package:video_player/video_player.dart';
8 | import 'package:window_manager/window_manager.dart';
9 |
10 | import 'generated/l10n.dart';
11 | import 'util/env_util.dart';
12 |
13 | class MobileVideoWidget extends StatefulWidget {
14 | final VideoPlayerController? controller;
15 | final GestureTapCallback? changeChannelSources;
16 | final String? toastString;
17 | final bool isLandscape;
18 | final Widget drawChild;
19 | final bool isBuffering;
20 | final bool isPlaying;
21 | final double aspectRatio;
22 | final GestureTapCallback onChangeSubSource;
23 |
24 | const MobileVideoWidget({
25 | super.key,
26 | required this.controller,
27 | required this.drawChild,
28 | required this.isBuffering,
29 | required this.isPlaying,
30 | required this.aspectRatio,
31 | // 数据源改变
32 | required this.onChangeSubSource,
33 | this.toastString,
34 | // 线路切换
35 | this.changeChannelSources,
36 | this.isLandscape = true,
37 | });
38 |
39 | @override
40 | State createState() => _MobileVideoWidgetState();
41 | }
42 |
43 | class _MobileVideoWidgetState extends State {
44 | @override
45 | Widget build(BuildContext context) {
46 | return Scaffold(
47 | appBar: AppBar(
48 | backgroundColor: Colors.black,
49 | centerTitle: true,
50 | title: Text(S.current.appName),
51 | leading: IconButton(
52 | icon: const Icon(Icons.qr_code_scanner),
53 | onPressed: () async {
54 | final isPlaying = widget.controller?.value.isPlaying ?? false;
55 | if (isPlaying) {
56 | widget.controller?.pause();
57 | }
58 | final res = await Navigator.of(context).pushNamed(RouterKeys.settingQrScan);
59 | if (res != null && res != '') {
60 | await Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
61 | final ip = Uri.parse(res!.toString()).host;
62 | return SubScribePage(remoteIp: ip, isTV: false);
63 | }));
64 | widget.controller?.play();
65 | final m3uData = SpUtil.getString('m3u_cache', defValue: '')!;
66 | if (m3uData == '') {
67 | widget.onChangeSubSource();
68 | }
69 | }
70 | },
71 | ),
72 | actions: [
73 | IconButton(
74 | onPressed: () async {
75 | if (!EnvUtil.isMobile) {
76 | windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: false);
77 | }
78 | final isPlaying = widget.controller?.value.isPlaying ?? false;
79 | if (isPlaying) {
80 | widget.controller?.pause();
81 | }
82 | await Navigator.of(context).pushNamed(RouterKeys.subScribe);
83 | widget.controller?.play();
84 | final m3uData = SpUtil.getString('m3u_cache', defValue: '')!;
85 | if (m3uData == '') {
86 | widget.onChangeSubSource();
87 | }
88 | if (!EnvUtil.isMobile) {
89 | windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: true);
90 | }
91 | },
92 | icon: const Icon(Icons.add)),
93 | IconButton(
94 | onPressed: () async {
95 | if (!EnvUtil.isMobile) {
96 | windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: false);
97 | }
98 | widget.controller?.pause();
99 | await Navigator.of(context).pushNamed(RouterKeys.setting);
100 | widget.controller?.play();
101 | if (!EnvUtil.isMobile) {
102 | windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: true);
103 | }
104 | },
105 | icon: const Icon(Icons.settings_outlined)),
106 | ],
107 | ),
108 | body: Column(
109 | children: [
110 | AspectRatio(
111 | aspectRatio: widget.aspectRatio,
112 | child: TableVideoWidget(
113 | controller: widget.controller,
114 | toastString: widget.toastString,
115 | isLandscape: false,
116 | aspectRatio: widget.aspectRatio,
117 | isBuffering: widget.isBuffering,
118 | isPlaying: widget.isPlaying,
119 | changeChannelSources: widget.changeChannelSources,
120 | onChangeSubSource: widget.onChangeSubSource,
121 | drawerIsOpen: false,
122 | ),
123 | ),
124 | Flexible(child: widget.toastString == 'UNKNOWN' ? EmptyPage(onRefresh: widget.onChangeSubSource) : widget.drawChild)
125 | ],
126 | ),
127 | );
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/lib/provider/download_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:apk_installer/apk_installer.dart';
2 | import 'package:easy_tv_live/util/log_util.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:path_provider/path_provider.dart';
5 |
6 | import '../util/http_util.dart';
7 |
8 | class DownloadProvider extends ChangeNotifier {
9 | bool _isDownloading = false;
10 | double _progress = 0.0;
11 |
12 | bool get isDownloading => _isDownloading;
13 | double get progress => _progress;
14 |
15 | Future downloadApk(String url) async {
16 | _isDownloading = true;
17 | notifyListeners();
18 |
19 | final savePath = '${(await getTemporaryDirectory()).path}/apk/${url.split('/').last}';
20 | LogUtil.v('download apk :::: $url');
21 | LogUtil.v('apk save path:::: $savePath');
22 |
23 | final code = await HttpUtil().downloadFile(url, savePath, progressCallback: (double currentProgress) {
24 | _progress = currentProgress;
25 | notifyListeners();
26 | });
27 | if (code == 200) {
28 | _isDownloading = false;
29 | await ApkInstaller.installApk(filePath: savePath);
30 | notifyListeners();
31 | } else {
32 | _isDownloading = false;
33 | notifyListeners();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/provider/theme_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:sp_util/sp_util.dart';
3 |
4 | import '../util/font_util.dart';
5 |
6 | class ThemeProvider extends ChangeNotifier {
7 | String _fontFamily = 'system';
8 | double _textScaleFactor = 1.0;
9 | String _fontUrl = '';
10 | bool _isBingBg = false;
11 | bool _useLightVersionCheck = true;
12 | bool _useDataProxy = true;
13 | bool _useAutoUpdate = false;
14 | bool _useLeftRightSelect = false;
15 | int _prePlaySerialNum = 1;
16 |
17 | bool get useAutoUpdate => _useAutoUpdate;
18 | bool get useLightVersionCheck => _useLightVersionCheck;
19 | bool get useDataProxy => _useDataProxy;
20 | String get fontFamily => _fontFamily;
21 | double get textScaleFactor => _textScaleFactor;
22 | String get fontUrl => _fontUrl;
23 | bool get isBingBg => _isBingBg;
24 | bool get useLeftRightSelect => _useLeftRightSelect;
25 | int get prePlaySerialNum => _prePlaySerialNum;
26 |
27 | ThemeProvider() {
28 | _useAutoUpdate = SpUtil.getBool('autoUpdate', defValue: false)!;
29 | _useLightVersionCheck = SpUtil.getBool('lightVersionCheck', defValue: true)!;
30 | _useDataProxy = SpUtil.getBool('dataProxy', defValue: true)!;
31 | _fontFamily = SpUtil.getString('appFontFamily', defValue: 'system')!;
32 | _fontUrl = SpUtil.getString('appFontUrl', defValue: '')!;
33 | _textScaleFactor = SpUtil.getDouble('fontScale', defValue: 1.0)!;
34 | _isBingBg = SpUtil.getBool('bingBg', defValue: false)!;
35 | _useLeftRightSelect = SpUtil.getBool('leftRightSelect', defValue: false)!;
36 | _prePlaySerialNum = SpUtil.getInt('prePlaySerialNum', defValue: 1)!;
37 | if (_fontFamily != 'system') {
38 | FontUtil().loadFont(_fontUrl, _fontFamily);
39 | }
40 | }
41 |
42 | void setFontFamily(String fontFamilyName, [String fontFullUrl = '']) {
43 | SpUtil.putString('appFontFamily', fontFamilyName);
44 | SpUtil.putString('appFontUrl', fontFullUrl);
45 | _fontFamily = fontFamilyName;
46 | _fontUrl = fontFullUrl;
47 | notifyListeners();
48 | }
49 |
50 | void setTextScale(double textScaleFactor) {
51 | SpUtil.putDouble('fontScale', textScaleFactor);
52 | _textScaleFactor = textScaleFactor;
53 | notifyListeners();
54 | }
55 |
56 | void setBingBg(bool isOpen) {
57 | SpUtil.putBool('bingBg', isOpen);
58 | _isBingBg = isOpen;
59 | notifyListeners();
60 | }
61 |
62 | void setLightVersionCheck(bool isOpen) {
63 | SpUtil.putBool('lightVersionCheck', isOpen);
64 | _useLightVersionCheck = isOpen;
65 | notifyListeners();
66 | }
67 |
68 | void setDataProxy(bool isOpen) {
69 | SpUtil.putBool('dataProxy', isOpen);
70 | _useDataProxy = isOpen;
71 | notifyListeners();
72 | }
73 |
74 | void setAutoUpdate(bool isOpen) {
75 | SpUtil.putBool('autoUpdate', isOpen);
76 | _useAutoUpdate = isOpen;
77 | notifyListeners();
78 | }
79 |
80 | void setLeftRightSelect(bool isOpen) {
81 | SpUtil.putBool('leftRightSelect', isOpen);
82 | _useLeftRightSelect = isOpen;
83 | notifyListeners();
84 | }
85 |
86 | void setPrePlaySerialNum(int serialNum) {
87 | SpUtil.putInt('prePlaySerialNum', serialNum);
88 | _prePlaySerialNum = serialNum;
89 | notifyListeners();
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/lib/router_keys.dart:
--------------------------------------------------------------------------------
1 | class RouterKeys {
2 | RouterKeys._();
3 | static const String subScribe = '/subScribe';
4 | static const String setting = '/setting';
5 | static const String settingFont = '/setting/font';
6 | static const String settingBeautify = '/setting/beautify';
7 | static const String settingReward = '/setting/reward';
8 | static const String settingQrScan = '/setting/qrScan';
9 | }
10 |
--------------------------------------------------------------------------------
/lib/setting/qr_scan_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:mobile_scanner/mobile_scanner.dart';
5 |
6 | class QrScanPage extends StatefulWidget {
7 | const QrScanPage({super.key});
8 |
9 | @override
10 | State createState() => _QrScanPageState();
11 | }
12 |
13 | class _QrScanPageState extends State with WidgetsBindingObserver {
14 | final MobileScannerController controller = MobileScannerController(formats: [BarcodeFormat.qrCode]);
15 |
16 | String? _code;
17 |
18 | @override
19 | void initState() {
20 | WidgetsBinding.instance.addObserver(this);
21 | unawaited(controller.start());
22 | super.initState();
23 | }
24 |
25 | @override
26 | Future dispose() async {
27 | WidgetsBinding.instance.removeObserver(this);
28 | super.dispose();
29 | await controller.dispose();
30 | }
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | return Scaffold(
35 | appBar: AppBar(
36 | title: Text('扫一扫'),
37 | ),
38 | body: MobileScanner(
39 | controller: controller,
40 | onDetect: (result) async {
41 | if (_code != null && _code!.isNotEmpty) return;
42 | _code = result.barcodes.first.displayValue;
43 | if (_code != null && _code!.isNotEmpty) {
44 | await controller.stop();
45 | debugPrint('onCapture::::$_code');
46 | Navigator.pop(context, _code);
47 | }
48 | },
49 | ),
50 | );
51 | }
52 |
53 | @override
54 | void didChangeAppLifecycleState(AppLifecycleState state) {
55 | if (!controller.value.isInitialized) {
56 | return;
57 | }
58 | switch (state) {
59 | case AppLifecycleState.detached:
60 | case AppLifecycleState.hidden:
61 | case AppLifecycleState.paused:
62 | return;
63 | case AppLifecycleState.resumed:
64 | unawaited(controller.start());
65 | case AppLifecycleState.inactive:
66 | unawaited(controller.stop());
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/setting/reward_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../util/env_util.dart';
4 | import '../util/http_util.dart';
5 |
6 | class RewardPage extends StatefulWidget {
7 | const RewardPage({super.key});
8 |
9 | @override
10 | State createState() => _RewardPageState();
11 | }
12 |
13 | class _RewardPageState extends State {
14 | String rewardText = '';
15 |
16 | @override
17 | void initState() {
18 | _loadReward();
19 | super.initState();
20 | }
21 |
22 | _loadReward() async {
23 | final res = await HttpUtil().getRequest(EnvUtil.rewardLink(), isShowLoading: false);
24 | if (mounted && res != null && res != '') {
25 | setState(() {
26 | rewardText = res.toString();
27 | });
28 | }
29 | }
30 |
31 | @override
32 | Widget build(BuildContext context) {
33 | return Scaffold(
34 | appBar: AppBar(
35 | title: const Text('赞赏榜'),
36 | ),
37 | body: SingleChildScrollView(
38 | padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20),
39 | child: Column(
40 | mainAxisSize: MainAxisSize.min,
41 | crossAxisAlignment: CrossAxisAlignment.start,
42 | children: [
43 | const Text(
44 | '使用微信扫一扫下方赞赏码,支持本软件!',
45 | ),
46 | const Divider(),
47 | Image.asset('assets/images/appreciate.png'),
48 | const SizedBox(height: 20),
49 | const Text(
50 | '🌈特别鸣谢以下老铁!',
51 | style: TextStyle(fontSize: 17),
52 | ),
53 | const Text(
54 | '若有遗漏请前往Github联系我补充!',
55 | style: TextStyle(fontSize: 10),
56 | ),
57 | const Divider(),
58 | Text(
59 | rewardText,
60 | style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w500, height: 2.0),
61 | )
62 | ],
63 | ),
64 | ),
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/setting/setting_beautify_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_tv_live/provider/download_provider.dart';
2 | import 'package:easy_tv_live/provider/theme_provider.dart';
3 | import 'package:easy_tv_live/util/check_version_util.dart';
4 | import 'package:easy_tv_live/util/env_util.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:provider/provider.dart';
7 |
8 | class SettingBeautifyPage extends StatelessWidget {
9 | const SettingBeautifyPage({super.key});
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | backgroundColor: const Color(0xFF1E1F22),
15 | appBar: AppBar(
16 | title: const Text('实验设置'),
17 | backgroundColor: const Color(0xFF1E1F22),
18 | leading: const SizedBox.shrink(),
19 | ),
20 | body: Align(
21 | alignment: Alignment.center,
22 | child: Container(
23 | alignment: Alignment.center,
24 | child: ListView(
25 | children: [
26 | SwitchListTile(
27 | autofocus: true,
28 | title: const Text('背景美化'),
29 | value: context.watch().isBingBg,
30 | subtitle: const Text('未播放时的屏幕背景,每日更换图片'),
31 | onChanged: (value) {
32 | context.read().setBingBg(value);
33 | },
34 | ),
35 | if (EnvUtil.isTV())
36 | SwitchListTile(
37 | autofocus: false,
38 | title: const Text('上下键切换频道'),
39 | value: context.watch().useLeftRightSelect,
40 | subtitle: const Text('开启后原先的设置页面和线路选择页面改成左右键打开'),
41 | onChanged: (value) {
42 | context.read().setLeftRightSelect(value);
43 | },
44 | ),
45 | SwitchListTile(
46 | title: const Text('更新提示免打扰'),
47 | value: context.watch().useLightVersionCheck,
48 | subtitle: const Text('开启后,播放页面的更新弹窗将会变成普通的消息提醒'),
49 | onChanged: (value) {
50 | context.read().setLightVersionCheck(value);
51 | },
52 | ),
53 | SwitchListTile(
54 | title: const Text('自动更新'),
55 | value: context.watch().useAutoUpdate,
56 | subtitle: const Text('发现新版本将会自动下载并安装'),
57 | onChanged: (value) {
58 | context.read().setAutoUpdate(value);
59 | },
60 | ),
61 | SwitchListTile(
62 | title: const Text('数据代理'),
63 | value: context.watch().useDataProxy,
64 | subtitle: const Text('Github访问受限的用户需开启'),
65 | onChanged: (value) {
66 | context.read().setDataProxy(value);
67 | },
68 | ),
69 | Builder(builder: (ctx) {
70 | final provider = context.watch();
71 | return ListTile(
72 | title: const Text('检查更新'),
73 | trailing: CheckVersionUtil.latestVersionEntity == null
74 | ? const Text('已是最新版本')
75 | : provider.isDownloading
76 | ? Text(
77 | '新版本正在下载中...${(provider.progress * 100).toStringAsFixed(1)}%',
78 | )
79 | : Text('🔴 发现新版本:v${CheckVersionUtil.latestVersionEntity?.latestVersion}'),
80 | onTap: () {
81 | if (!context.read().isDownloading) {
82 | CheckVersionUtil.checkVersion(context, true, true);
83 | }
84 | },
85 | );
86 | }),
87 | if (!CheckVersionUtil.isTV)
88 | ListTile(
89 | title: const Text('应用主页'),
90 | trailing: Icon(Icons.arrow_forward_ios),
91 | onTap: () {
92 | CheckVersionUtil.launchBrowserUrl(CheckVersionUtil.homeLink);
93 | },
94 | ),
95 | if (!CheckVersionUtil.isTV)
96 | ListTile(
97 | title: const Text('发布历史'),
98 | trailing: Icon(Icons.arrow_forward_ios),
99 | onTap: () {
100 | CheckVersionUtil.launchBrowserUrl(CheckVersionUtil.releaseLink);
101 | },
102 | ),
103 | ],
104 | ),
105 | ),
106 | ),
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/lib/tv/html_string.dart:
--------------------------------------------------------------------------------
1 | String getHtmlString(String ipAddress) => '''
2 |
3 |
4 |
5 |
6 |
7 | 极简TV
8 |
55 |
85 |
86 |
87 | 添加订阅源
88 |
89 |
90 |
91 |
92 |
93 | ''';
94 |
--------------------------------------------------------------------------------
/lib/tv/tv_setting_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_tv_live/setting/setting_font_page.dart';
2 | import 'package:easy_tv_live/setting/subscribe_page.dart';
3 | import 'package:easy_tv_live/tv/tv_appreciate_page.dart';
4 | import 'package:easy_tv_live/util/check_version_util.dart';
5 | import 'package:flutter/material.dart';
6 |
7 | import '../setting/setting_beautify_page.dart';
8 |
9 | class TvSettingPage extends StatefulWidget {
10 | const TvSettingPage({super.key});
11 |
12 | @override
13 | State createState() => _TvSettingPageState();
14 | }
15 |
16 | class _TvSettingPageState extends State {
17 | int _selectedIndex = -1;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return Row(
22 | children: [
23 | SizedBox(
24 | width: 240,
25 | child: Scaffold(
26 | appBar: AppBar(
27 | title: const Text('设置'),
28 | ),
29 | body: Column(
30 | children: [
31 | Flexible(
32 | child: ListView(
33 | children: [
34 | ListTile(
35 | leading: const Icon(Icons.card_giftcard),
36 | title: const Text('扫码赞赏'),
37 | selected: _selectedIndex == -1,
38 | autofocus: true,
39 | onTap: () {
40 | setState(() {
41 | _selectedIndex = -1;
42 | });
43 | },
44 | ),
45 | ListTile(
46 | leading: const Icon(Icons.subscriptions),
47 | title: const Text('订阅资源'),
48 | selected: _selectedIndex == 0,
49 | onTap: () {
50 | setState(() {
51 | _selectedIndex = 0;
52 | });
53 | },
54 | ),
55 | ListTile(
56 | leading: const Icon(Icons.font_download),
57 | title: const Text('字体设置'),
58 | selected: _selectedIndex == 1,
59 | onTap: () {
60 | setState(() {
61 | _selectedIndex = 1;
62 | });
63 | },
64 | ),
65 | ListTile(
66 | leading: const Icon(Icons.account_balance_outlined),
67 | title: Text('实验设置 ${CheckVersionUtil.latestVersionEntity == null ? '' : '🔴'}'),
68 | selected: _selectedIndex == 2,
69 | onTap: () {
70 | setState(() {
71 | _selectedIndex = 2;
72 | });
73 | },
74 | )
75 | ],
76 | ),
77 | ),
78 | Container(
79 | alignment: Alignment.centerLeft,
80 | padding: const EdgeInsets.only(left: 16),
81 | child: Text('V${CheckVersionUtil.version}'),
82 | )
83 | ],
84 | ),
85 | ),
86 | ),
87 | if (_selectedIndex == -1)
88 | const Expanded(
89 | child: TvAppreciatePage(),
90 | ),
91 | if (_selectedIndex == 0)
92 | const Expanded(
93 | child: SubScribePage(isTV: true),
94 | ),
95 | if (_selectedIndex == 1) const Expanded(child: SettingFontPage(isTV: true)),
96 | if (_selectedIndex == 2) const Expanded(child: SettingBeautifyPage()),
97 | ],
98 | );
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/util/bing_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:easy_tv_live/util/http_util.dart';
2 |
3 | class BingUtil {
4 | static String? bingImgUrl;
5 |
6 | static Future getBingImgUrl() async {
7 | if (bingImgUrl != null && bingImgUrl != '') return bingImgUrl;
8 | final res = await HttpUtil().getRequest('https://bing.biturl.top/', isShowLoading: false);
9 | if (res != null && res['url'] != null && res['url'] != '') {
10 | bingImgUrl = res['url'];
11 | return bingImgUrl;
12 | }
13 | return null;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/util/device_sync_util.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_easyloading/flutter_easyloading.dart';
3 | import 'package:provider/provider.dart';
4 | import 'package:sp_util/sp_util.dart';
5 |
6 | import '../entity/subScribe_model.dart';
7 | import '../provider/theme_provider.dart';
8 | import 'font_util.dart';
9 | import 'm3u_util.dart';
10 |
11 | class DeviceSyncUtil {
12 | DeviceSyncUtil._();
13 |
14 | static applyAllSettings(BuildContext context, Map syncMap) async {
15 | EasyLoading.show(status: '正在处理');
16 | final textScaleFactor = syncMap['textScaleFactor'];
17 | final fontFamily = syncMap['fontFamily'];
18 | final fontUrl = syncMap['fontUrl'];
19 | final isBingBg = syncMap['isBingBg'];
20 | final videoSource = syncMap['videoSource'];
21 |
22 | if (textScaleFactor != null) {
23 | context.read().setTextScale(textScaleFactor);
24 | }
25 |
26 | if (isBingBg != null) {
27 | context.read().setBingBg(isBingBg);
28 | }
29 |
30 | if (fontUrl != null || fontFamily != null) {
31 | final fontRes = fontFamily == 'system' ? true : await FontUtil().loadFont(fontUrl, fontFamily);
32 | if (fontRes) {
33 | if (context.mounted) context.read().setFontFamily(fontFamily, fontUrl);
34 | }
35 | }
36 |
37 | if (videoSource != null) {
38 | final source = videoSource as List;
39 | await M3uUtil.saveLocalData(source.map((e) => SubScribeModel.fromJson(e)).toList());
40 | await SpUtil.remove('m3u_cache');
41 | }
42 | EasyLoading.showSuccess('同步成功');
43 | }
44 |
45 | static Future