YY播放器使用Flutter編寫的一個聚合播放器, 起因是看了 ZY-Player的源碼, 發現實現挺有意思的, 也比較簡單
地址: https://github.com/waifu-project/movie
下載源碼之后, 首先從入口函數入手
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await XHttp.init();
await GetStorage.init();
await MirrorManage.init();
final localStorage = GetStorage();
bool isDark = (localStorage.read(ConstDart.ls_isDark) ?? false);
bool systemBrightnessFlag = (localStorage.read(ConstDart.auto_dark) ?? false);
Brightness wrapperIfDark = Brightness.light;
{
if (isDark) wrapperIfDark = Brightness.dark;
if (GetPlatform.isWindows && systemBrightnessFlag) {
var windowMode = getWindowsThemeMode();
wrapperIfDark = windowMode;
}
if (GetPlatform.isDesktop) {
doWhenWindowReady(() {
final initialSize = Size(990, 720);
appWindow.minSize = initialSize;
appWindow.size = initialSize;
appWindow.alignment = Alignment.center;
appWindow.show();
});
}
}
大概可以看出支持了 windows 平台(從 doWhenWindowReady 回調可以看出)
繼續從依賴中猜一下大概實現方式
dependencies:
cupertino_icons: ^1.0.2 # 圖標
get: 4.3.8 # 狀態管理
salomon_bottom_bar: ^3.1.0
dio: ^4.0.0 # 網絡庫
cookie_jar: ^3.0.1
dio_cookie_manager: ^2.0.0
html: ^0.15.0
flutter_cupertino_settings: ^0.5.0
webview_flutter: ^2.1.1 # webview組件
flappy_search_bar_ns: ^2.0.2
xml2json: ^5.3.1 # xml to json
modal_bottom_sheet: ^2.0.0
pull_to_refresh: ^2.0.0
cupertino_list_tile: ^0.2.0
cached_network_image: ^3.1.0
chewie: ^1.2.2 # 視頻播放
video_player: ^2.2.5
get_storage: ^2.0.3
path_provider: ^2.0.6
flutter_html: ^2.1.5
clipboard: ^0.1.3
wakelock: ^0.5.6
auto_orientation: ^2.1.0
desktop_webview_window: ^0.0.5
bitsdojo_window: ^0.1.1+1
url_launcher: ^6.0.12
首先是所謂的聚合實現
在 lib/impl/movie.dart中有一個抽象類
abstract class MovieImpl {
/// 源信息
MovieMetaData get meta;
/// 獲取首頁
Future<List<MirrorOnceItemSerialize>> getHome({
int page = 1,
int limit = 10,
});
/// 搜索
Future<List<MirrorOnceItemSerialize>> getSearch({
required String keyword,
int page = 1,
int limit = 10,
});
/// 獲取視頻詳情
Future<MirrorOnceItemSerialize> getDetail(String movie_id);
}
基本上源的操作就那幾個:
- 首頁
- 搜索
- 詳情
接下來在看一下 MirrorOnceItemSerialize, 就是定義的一個標准實體類
class MirrorOnceItemSerialize {
/// id
final String id;
/// 標題
final String title;
/// 介紹
final String desc;
/// 喜歡
final int likeCount;
/// 訪問人數
final int viewCount;
/// 不喜歡
final int dislikeCount;
/// 小封面圖(必須要有)
final String smallCoverImage;
/// 大封面圖
final String bigCoverImage;
/// 視頻列表
final List<MirrorSerializeVideoInfo> videos;
/// 視頻信息
/// 視頻尺寸大小
/// 視頻長度大小
final MirrorSerializeVideoSize videoInfo;
}
接下來就是實現這個抽象類, 比如內置的奈菲源, 來看一下其的實現方式
@override
Future<MirrorOnceItemSerialize> getDetail(String movie_id) async {
var detailURL = createURL(path: "/detail/" + movie_id);
var resp = await XHttp.dio.get(
detailURL,
options: Options(
headers: header,
),
);
var parse = html.parse(resp.data);
var ele = parse.querySelector(".myui-panel_hd");
if (ele == null) throw UnimplementedError();
var mirrorList = ele.querySelectorAll('li');
List<fetchMovieFrameURL> frames = [];
mirrorList.map((e) {
// ...(忽略代碼)
return bat;
}).toList();
var data = await Future.wait<MirrorSerializeVideoInfo>(frames.map(
(e) async {
var url = await findIframeM3u8URL(e.id);
var title = e.title;
var item = MirrorSerializeVideoInfo(
url: url,
type: KBaseMirrorMovie.easyGetVideoType(url),
name: title,
);
return item;
},
).toList());
var infoEle = parse.querySelector(
'.myui-vodlist__thumb.img-md-220.img-xs-130.picture',
);
var coverImage = infoEle!.querySelector("img")?.attributes['data-original'] ?? "";
var title = infoEle.attributes['title'] ?? "";
return MirrorOnceItemSerialize(
id: movie_id,
smallCoverImage: coverImage,
title: title,
videos: data,
);
}
相信同學們基本上看明白了: 這個奈菲源的實現是通過解析 html 然后拿到數據
所以說, 只要你實現了視頻源抽象類, 你想怎么玩就怎么玩(所以快來PR添加資源吧)
同樣的, 根據這個抽象類, 實現了 ZY-Player 的源, 單個資源數據類型
{
"key": "快播雲",
"id": 1,
"name": "快播雲",
"api": "http://www.kuaibozy.com/api.php/provide/vod/from/kbm3u8/at/xml/",
"download": "",
"jiexiUrl": "https://jx.7kjx.com/?url=",
"group": "默認",
"isActive": true,
"status": "可用",
"reverseOrder": true
}
繼續扒一下 ZF-Player的源碼: https://github.com/cuiocean/ZY-Player/blob/master/src/lib/site/tools.js
就可以找到規則:
首頁就是: $ROOT?ac=videolist&pg=${pg}
/**
* 獲取資源列表
* @param {*} key 資源網 key
* @param {number} [pg=1] 翻頁 page
* @param {*} t 分類 type
* @returns
*/
list (key, pg = 1, t) {
return new Promise((resolve, reject) => {
this.getSite(key).then(res => {
const site = res
let url = null
if (t) {
url = `${site.api}?ac=videolist&t=${t}&pg=${pg}`
} else {
url = `${site.api}?ac=videolist&pg=${pg}`
}
// todo
})
})
},
詳情就是: $ROOT?ac=videolist&ids=${id}
/**
* 獲取資源詳情
* @param {*} key 資源網 key
* @param {*} id 資源唯一標識符 id
* @returns
*/
detail (key, id) {
return new Promise((resolve, reject) => {
this.getSite(key).then(res => {
const url = `${res.api}?ac=videolist&ids=${id}`
// TODO
})
}
搜索就是: $ROOT?wd=${wd}
/**
* 搜索資源
* @param {*} key 資源網 key
* @param {*} wd 搜索關鍵字
* @returns
*/
search (key, wd) {
return new Promise((resolve, reject) => {
this.getSite(key).then(res => {
const site = res
const url = `${site.api}?wd=${encodeURI(wd)}`
// TODO
})
})
}
根據此規則就可以兼容 ZY-Player 的資源
后續
大概實現方式這樣, 比較簡單, 后續加一點點細節就可以了
有興趣的同學可以看一下源碼: https://github.com/waifu-project/movie
mail: chenhonzhou@gmail.com
