YY播放器源碼解析


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM