第二篇-用Flutter手擼一個抖音國內版,看看有多炫

来源:https://www.cnblogs.com/fengqingyangNo1/archive/2020/05/24/12953032.html
-Advertisement-
Play Games

前言 繼上一篇使用Flutter開發的抖音國際版 後再次擼一個國內版抖音,大部分功能已完成,主要是Flutter開發APP速度很爽, 先看下圖 項目主要結構介紹 這次主要的改動在api.dart 及douyin.dart里,國內抖音的api是不同的,另外地址以及實體類也不一樣。詳細下麵介紹. 抖音的 ...


前言

繼上一篇使用Flutter開發的抖音國際版 後再次擼一個國內版抖音,大部分功能已完成,主要是Flutter開發APP速度很爽,  先看下圖

 

項目主要結構介紹

 

 這次主要的改動在api.dart 及douyin.dart里,國內抖音的api是不同的,另外地址以及實體類也不一樣。詳細下麵介紹.

抖音的實體類

主要是講json轉化為實體模型,然後綁定到view 層面,寫這個實體類還是很耗費時間的,畢竟得先用爬網知識講抖音的json拿到,並且根據json反向寫實體類.

這個地址獲取到抖音的推薦列表json: https://creator.douyin.com/aweme/v1/creator/data/billboard/?billboard_type=4,綁定第一個Douyin實體,拿到所有抖音的url地址   通過第二個api地址 https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids= 拿到抖音視頻所有的json信息並且綁定VideoData實體類
class Douyin {
  int statusCode;
  String statusMsg;
  List<BillboardData> billboardData;
  Extra extra;

  Douyin({this.statusCode, this.statusMsg, this.billboardData, this.extra});

  Douyin.fromJson(Map<String, dynamic> json) {
    statusCode = json["status_code"];
    statusMsg = json["status_msg"];
    if (json['billboard_data'] != null) {
      billboardData = new List<BillboardData>();
      json["billboard_data"].forEach((v) {
        billboardData.add(BillboardData.fromJson(v));
      });
    }
    extra = json['extra'] != null ? new Extra.fromJson(json['extra']) : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["status_code"] = this.statusCode;
    data["status_msg"] = this.statusMsg;
    if (this.billboardData != null) {
      data["billboard_data"] =
          this.billboardData.map((v) => v.toJson()).toList();
    }
    data["extra"] = this.extra;
    return data;
  }
}

class BillboardData {
  String author;
  String imgUrl;
  String link;
  int rank;
  String title;
  String value;

  BillboardData(
      {this.author, this.imgUrl, this.link, this.rank, this.title, this.value});

  BillboardData.fromJson(Map<String, dynamic> json) {
    this.author = json["author"];
    this.imgUrl = json["img_url"];
    this.link = json["link"];
    this.rank = json["rank"];
    this.title = json["title"];
    this.value = json["value"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["author"] = this.author;
    data["img_url"] = this.imgUrl;
    data["link"] = this.link;
    data["rank"] = this.rank;
    data["title"] = this.title;
    data["value"] = this.value;
    return data;
  }
}

class Extra {
  int now;
  Extra({this.now});

  Extra.fromJson(Map<String, dynamic> json) {
    now = json["now"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["now"] = this.now;
    return data;
  }
}

class VideoData {
  int statusCode;
  List<Itemlist> itemList;
  ExtraData extra;

  VideoData.fromJson(Map<String, dynamic> json) {
    this.statusCode = json["status_code"];
    if (json['item_list'] != null) {
      itemList = new List<Itemlist>();
      json['item_list'].forEach((v) {
        itemList.add(new Itemlist.fromJson(v));
      });
    }
    this.extra =
        json["extra"] != null ? new ExtraData.fromJson(json["extra"]) : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["status_code"] = this.statusCode;
    if (this.itemList != null) {
      data['item_list'] = this.itemList.map((v) => v.toJson()).toList();
    }
    if (this.extra != null) {
      data["extra"] = this.extra.toJson();
    }
    return data;
  }
}

class Itemlist {
  String awemeid;
  String videolabels;
  String labeltoptext;
  int category;
  Author author;
  int duration;
  String promotions;
  int ispreview;
  int createtime;
  List<ChaList> chalist;
  String commentlist;
  int authoruserid;
  String videotext;
  int groupid;
  bool islivereplay;
  ShareInfo shareinfo;
  String position;
  String imageinfos;
  RiskInfos riskinfos;
  String uniqidposition;
  String geofencing;
  Statistics statistics;
  int awemetype;
  Music music;
  List<TextExtra> textExtras;
  Video video;
  String shareurl;
  String desc;
  String longvideo;

  Itemlist(
      {this.awemeid,
      this.videolabels,
      this.labeltoptext,
      this.category,
      this.author,
      this.duration,
      this.promotions,
      this.ispreview,
      this.createtime,
      this.chalist,
      this.commentlist,
      this.authoruserid,
      this.videotext,
      this.groupid,
      this.islivereplay,
      this.shareinfo,
      this.position,
      this.imageinfos,
      this.riskinfos,
      this.uniqidposition,
      this.geofencing,
      this.statistics,
      this.awemetype,
      this.music,
      this.textExtras,
      this.video,
      this.shareurl,
      this.desc,
      this.longvideo});

  Itemlist.fromJson(Map<String, dynamic> json) {
    this.awemeid = json["aweme_id"];
    this.videolabels = json["video_labels"];
    this.labeltoptext = json["label_top_text"];
    this.category = json["category"];
    this.author =
        json["author"] != null ? new Author.fromJson(json["author"]) : null;
    this.duration = json["duration"];
    this.promotions = json["promotions"];
    this.ispreview = json["is_preview"];
    this.createtime = json["create_time"];
    if (json["cha_list"] != null) {
      this.chalist = new List<ChaList>();
      json["cha_list"].forEach((v) {
        this.chalist.add(new ChaList.fromJson(v));
      });
    }
    this.commentlist = json["comment_list"];
    this.authoruserid = json["author_user_id"];
    this.videotext = json["video_text"];
    this.groupid = json["group_id"];
    this.islivereplay = json["is_live_replay"];
    this.shareinfo = json["share_info"] != null
        ? new ShareInfo.fromJson(json["share_info"])
        : null;
    this.position = json["position"];
    this.imageinfos = json["image_infos"];
    this.riskinfos = json["risk_infos"] != null
        ? new RiskInfos.fromJson(json["risk_infos"])
        : null;
    this.uniqidposition = json["uniqid_position"];
    this.geofencing = json["geofencing"];
    this.statistics = json["statistics"] != null
        ? new Statistics.fromJson(json["statistics"])
        : null;
    this.awemetype = json["aweme_type"];
    this.music =
        json["music"] != null ? new Music.fromJson(json["music"]) : null;
    if (json["text_extra"] != null) {
      this.textExtras = new List<TextExtra>();
      json['text_extra'].forEach((v) {
        textExtras.add(new TextExtra.formJson(v));
      });
    }
    this.video =
        json["video"] != null ? new Video.fromJson(json["video"]) : null;
    this.shareurl = json["share_url"];
    this.desc = json["desc"];
    this.longvideo = json["long_video"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["aweme_id"] = this.awemeid;
    data["video_labels"] = this.videolabels;
    data["label_top_text"] = this.labeltoptext;
    data["category"] = this.category;
    if (this.author != null) {
      data["author"] = this.author.toJson();
    }
    data["duration"] = this.duration;
    data["promotions"] = this.promotions;
    data["is_preview"] = this.ispreview;
    data["create_time"] = this.createtime;
    if (this.chalist != null) {
      data["cha_list"] = this.chalist.map((e) => e.toJson()).toList();
    }
    data["comment_list"] = this.commentlist;
    data["author_user_id"] = this.authoruserid;
    data["video_text"] = this.videotext;
    data["group_id"] = this.groupid;
    data["is_live_replay"] = this.islivereplay;
    if (this.shareinfo != null) {
      data["share_info"] = this.shareinfo.toJson();
    }
    data["position"] = this.position;
    data["image_infos"] = this.imageinfos;
    if (this.riskinfos != null) {
      data["risk_infos"] = this.riskinfos.toJson();
    }
    data["uniqid_position"] = this.uniqidposition;
    data["geofencing"] = this.geofencing;
    if (this.statistics != null) {
      data["statistics"] = this.statistics.toJson();
    }
    data["aweme_type"] = this.awemetype;
    if (this.music != null) {
      data["music"] = this.music.toJson();
    }
    if (this.textExtras != null) {
      data["text_extra"] = this.textExtras.map((e) => e.toJson()).toList();
    }
    if (this.video != null) {
      data["video"] = this.video.toJson();
    }
    data["share_url"] = this.shareurl;
    data["desc"] = this.desc;
    data["long_video"] = this.longvideo;
    return data;
  }
}

class TextExtra {
  String hashtagName;
  int hashtagId;
  int start;
  int end;
  int type;

  TextExtra(
      {this.hashtagName, this.hashtagId, this.start, this.end, this.type});

  TextExtra.formJson(Map<String, dynamic> json) {
    this.hashtagName = json["hashtag_name"];
    this.hashtagId = json["hashtag_id"];
    this.start = json["start"];
    this.end = json["end"];
    this.type = json["type"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["hashtag_name"] = this.hashtagName;
    data["hashtag_id"] = this.hashtagId;
    data["start"] = this.start;
    data["end"] = this.end;
    data["type"] = this.type;
    return data;
  }
}

// Author
class Author {
  String geofencing;
  String uid;
  String shortID;
  String signature;
  AvatarMedium avatarMedium;
  String uniqueId;
  String followersDetail;
  String platformSyncInfo;
  String policyVersion;
  String nickname;
  AvatarLarger avatarlarger;
  AvatarThumb avatarthumb;

  Author(
      {this.geofencing,
      this.uid,
      this.shortID,
      this.signature,
      this.avatarMedium,
      this.uniqueId,
      this.followersDetail,
      this.platformSyncInfo,
      this.policyVersion,
      this.nickname,
      this.avatarlarger,
      this.avatarthumb});

  Author.fromJson(Map<String, dynamic> json) {
    this.geofencing = json["geofencing"];
    this.uid = json["uid"];
    this.shortID = json["short_id"];
    this.signature = json["signature"];
    this.avatarMedium = json["avatar_medium"] != null
        ? new AvatarMedium.fromJson(json["avatar_medium"])
        : null;
    this.uniqueId = json["unique_id"];
    this.followersDetail = json["followers_detail"];
    this.platformSyncInfo = json["platform_sync_info"];
    this.policyVersion = json["policy_version"];
    this.nickname = json["nickname"];
    this.avatarlarger = json["avatar_larger"] != null
        ? new AvatarLarger.formJson(json["avatar_larger"])
        : null;
    this.avatarthumb = json["avatar_thumb"] != null
        ? new AvatarThumb.formJson(json["avatar_thumb"])
        : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["geofencing"] = this.geofencing;
    data["uid"] = this.uid;
    data["short_id"] = this.shortID;
    data["signature"] = this.signature;
    if (this.avatarMedium != null) {
      data["avatar_medium"] = this.avatarMedium.toJson();
    }
    data["unique_id"] = this.uniqueId;
    data["followers_detail"] = this.followersDetail;
    data["platform_sync_info"] = this.platformSyncInfo;
    data["policy_version"] = this.policyVersion;
    data["nickname"] = this.nickname;
    if (this.avatarlarger != null) {
      data["avatar_larger"] = this.avatarlarger.toJson();
    }
    if (this.avatarthumb != null) {
      data["avatar_thumb"] = this.avatarthumb.toJson();
    }
    return data;
  }
}

class AvatarMedium {
  List<String> urlList;
  String uri;
  AvatarMedium({this.urlList, this.uri});

  AvatarMedium.fromJson(Map<String, dynamic> json) {
    if (json["url_list"] != null) {
      this.urlList = json["url_list"].cast<String>();
    }
    this.uri = json["uri"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    return data;
  }
}

class AvatarLarger {
  String uri;
  List<String> urlList;
  AvatarLarger({this.uri, this.urlList});

  AvatarLarger.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class AvatarThumb {
  String uri;
  List<String> urlList;
  AvatarThumb({this.uri, this.urlList});

  AvatarThumb.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class ExtraData {
  int now;
  String logid;
  ExtraData({this.now, this.logid});

  ExtraData.fromJson(Map<String, dynamic> json) {
    this.now = json["now"];
    this.logid = json["logid"];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["now"] = this.now;
    data["logid"] = this.logid;
    return data;
  }
}
//End author

//ChaList
class ChaList {
  String chaName;
  int viewCount;
  String hashTagProfile;
  bool isCommerce;
  String cid;
  String desc;
  int userCount;
  String connectMusic;
  int type;
  CoverItem coverItem;
  ChaList(
      {this.chaName,
      this.viewCount,
      this.hashTagProfile,
      this.isCommerce,
      this.cid,
      this.desc,
      this.userCount,
      this.connectMusic,
      this.type,
      this.coverItem});

  ChaList.fromJson(Map<String, dynamic> json) {
    this.chaName = json["cha_name"];
    this.viewCount = json["view_count"];
    this.hashTagProfile = json["hash_tag_profile"];
    this.isCommerce = json["is_commerce"];
    this.cid = json["cid"];
    this.desc = json["desc"];
    this.userCount = json["user_count"];
    this.connectMusic = json["connect_music"];
    this.type = json["type"];
    this.coverItem = json["cover_item"] != null
        ? new CoverItem.formJson(json["cover_item"])
        : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["cha_name"] = this.chaName;
    data["view_count"] = this.viewCount;
    data["hash_tag_profile"] = this.hashTagProfile;
    data["is_commerce"] = this.isCommerce;
    data["cid"] = this.cid;
    data["desc"] = this.desc;
    data["user_count"] = this.userCount;
    data["connect_music"] = this.connectMusic;
    data["type"] = this.type;
    if (this.coverItem != null) {
      data["cover_item"] = this.coverItem.toJson();
    }
    return data;
  }
}

class CoverItem {
  String uri;
  List<String> urlList;
  CoverItem({this.uri, this.urlList});
  CoverItem.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}
//End ChaList

//ShareInfo
class ShareInfo {
  String shareweibodesc;
  String sharedesc;
  String sharetitle;
  ShareInfo({this.shareweibodesc, this.sharedesc, this.sharetitle});
  ShareInfo.fromJson(Map<String, dynamic> json) {
    this.shareweibodesc = json["share_weibo_desc"];
    this.sharedesc = json["share_desc"];
    this.sharetitle = json["share_title"];
  }
  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["share_weibo_desc"] = this.shareweibodesc;
    data["share_desc"] = this.sharedesc;
    data["share_title"] = this.sharetitle;
    return data;
  }
}
//End ShareInfo

//RiskInfos
class RiskInfos {
  bool warn;
  int type;
  String content;
  RiskInfos({this.warn, this.type, this.content});
  RiskInfos.fromJson(Map<String, dynamic> json) {
    this.warn = json["warn"] as bool;
    this.type = json["type"];
    this.content = json["content"];
  }
  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["warn"] = this.warn;
    data["type"] = this.type;
    data["content"] = this.content;
    return data;
  }
}
//End RiskInfos

//Statistics
class Statistics {
  String awemeId;
  int commentCount;
  int diggCount;
  Statistics.fromJson(Map<String, dynamic> json) {
    this.awemeId = json["aweme_id"];
    this.commentCount = json["comment_count"];
    this.diggCount = json["digg_count"];
  }
  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["aweme_id"] = this.awemeId;
    data["comment_count"] = this.commentCount;
    data["digg_count"] = this.diggCount;
    return data;
  }
}
//End Statistics

//Music
class Music {
  CoverLarge coverlarge;
  CoverMedium covermedium;
  int duration;
  int status;
  String mid;
  String title;
  String author;
  PlayUrl playurl;
  String position;
  int id;
  CoverHd coverhd;
  CoverThumb coverthumb;

  Music.fromJson(Map<String, dynamic> json) {
    this.coverlarge = json["cover_large"] != null
        ? new CoverLarge.formJson(json["cover_large"])
        : null;
    this.covermedium = json["cover_medium"] != null
        ? new CoverMedium.formJson(json["cover_medium"])
        : null;
    this.duration = json["duration"];
    this.status = json["status"];
    this.mid = json["mid"];
    this.title = json["title"];
    this.author = json["author"];
    this.playurl = json["play_url"] != null
        ? new PlayUrl.formJson(json["play_url"])
        : null;
    this.position = json["position"];
    this.id = json["id"];
    this.coverhd = json["cover_hd"] != null
        ? new CoverHd.formJson(json["cover_hd"])
        : null;
    this.coverthumb = json["cover_thumb"] != null
        ? new CoverThumb.formJson(json["cover_thumb"])
        : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.coverlarge != null) {
      data["cover_large"] = this.coverlarge.toJson();
    }
    if (this.covermedium != null) {
      data["cover_medium"] = this.covermedium.toJson();
    }
    data["duration"] = this.duration;
    data["status"] = this.status;
    data["mid"] = this.mid;
    data["title"] = this.title;
    data["author"] = this.author;
    if (this.playurl != null) {
      data["play_url"] = this.playurl.toJson();
    }
    data["position"] = this.position;
    data["id"] = this.id;
    if (this.coverhd != null) {
      data["cover_hd"] = this.coverhd.toJson();
    }
    if (this.coverthumb != null) {
      data["cover_thumb"] = this.coverthumb.toJson();
    }
    return data;
  }
}

class CoverLarge {
  String uri;
  List<String> urlList;
  CoverLarge({this.uri, this.urlList});
  CoverLarge.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class CoverMedium {
  String uri;
  List<String> urlList;
  CoverMedium({this.uri, this.urlList});
  CoverMedium.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class PlayUrl {
  String uri;
  List<String> urlList;
  PlayUrl({this.uri, this.urlList});
  PlayUrl.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class CoverHd {
  String uri;
  List<String> urlList;
  CoverHd({this.uri, this.urlList});
  CoverHd.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class CoverThumb {
  String uri;
  List<String> urlList;
  CoverThumb({this.uri, this.urlList});
  CoverThumb.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}
//End Music

//Video
class Video {
  int width;
  OriginCover origincover;
  String ratio;
  bool haswatermark;
  String bitrate;
  PlayAddr playaddr;
  Cover cover;
  int duration;
  String vid;
  int islongvideo;
  int height;
  DynamicCover dynamiccover;
  Video(
      {this.width,
      this.origincover,
      this.ratio,
      this.haswatermark,
      this.bitrate,
      this.playaddr,
      this.cover,
      this.duration,
      this.vid,
      this.islongvideo,
      this.height,
      this.dynamiccover});
  Video.fromJson(Map<String, dynamic> json) {
    this.width = json["width"];
    this.origincover = json["origin_cover"] != null
        ? new OriginCover.formJson(json["origin_cover"])
        : null;
    this.ratio = json["ratio"];
    this.haswatermark = json["has_watermark"];
    this.bitrate = json["bit_rate"];
    this.playaddr = json["play_addr"] != null
        ? new PlayAddr.formJson(json["play_addr"])
        : null;
    this.cover =
        json["cover"] != null ? new Cover.formJson(json["cover"]) : null;
    this.duration = json["duration"];
    this.vid = json["vid"];
    this.islongvideo = json["is_long_video"];
    this.height = json["height"];
    this.dynamiccover = json["dynamic_cover"] != null
        ? new DynamicCover.formJson(json["dynamic_cover"])
        : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["width"] = this.width;
    if (this.origincover != null) {
      data["origin_cover"] = this.origincover.toJson();
    }
    data["ratio"] = this.ratio;
    data["has_watermark"] = this.haswatermark;
    data["bit_rate"] = this.bitrate;
    if (this.playaddr != null) {
      data["play_addr"] = this.playaddr.toJson();
    }
    if (this.cover != null) {
      data["cover"] = this.cover.toJson();
    }
    data["duration"] = this.duration;
    data["vid"] = this.vid;
    data["is_long_video"] = this.islongvideo;
    data["height"] = this.height;
    if (this.dynamiccover != null) {
      data["dynamic_cover"] = this.dynamiccover.toJson();
    }
    return data;
  }
}

class OriginCover {
  String uri;
  List<String> urlList;
  OriginCover({this.uri, this.urlList});
  OriginCover.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class PlayAddr {
  String uri;
  List<String> urlList;
  PlayAddr({this.uri, this.urlList});
  PlayAddr.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class Cover {
  String uri;
  List<String> urlList;
  Cover({this.uri, this.urlList});
  Cover.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

class DynamicCover {
  String uri;
  List<String> urlList;
  DynamicCover({this.uri, this.urlList});
  DynamicCover.formJson(Map<String, dynamic> json) {
    this.uri = json["uri"] as String;
    this.urlList = json["url_list"].cast<String>();
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data["url_list"] = this.urlList;
    data["uri"] = this.uri;
    return data;
  }
}

//End Video

  

取抖音無水印視頻,這裡才是關鍵

通過這個api地址 https://aweme.snssdk.com/aweme/v1/play/?video_id={}&ratio=720p&line=0&media_type=4&vr_type=0&improve_bitrate=0&is_play_url=1&h265=1&adapt720=1 可以成功的拿到無水印視頻,

此方法僅用於學習研究目的,不得從事違法活動哈,  否則與本作者無關.

具體實現代碼如下

import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'package:dio/dio.dart';

class RequestController {
  //static String host = "https://www.tiktok.com/";
  static String host = "https://creator.douyin.com";
  String url = host + "/aweme/v1/creator/data/billboard/?billboard_type=4";

  String video =
      "https://www.iesdouyin.com/web/api/v2/aweme/iteminfo/?item_ids=";

  String player = "https://aweme.snssdk.com/aweme/v1/play/?video_id=";

  Future<String> getVideos() async {
    try {
      var response = await http.get(url);
      return response.body;
    } catch (e) {
      return e.toString();
    }
  }

  //獲取無水印的視頻
  Future<String> getRedirects(String videoid) async {
    try {
      var response = await new Dio().get(
        player +
            videoid +
            "&ratio=720p&line=0&media_type=4&vr_type=0&improve_bitrate=0&is_play_url=1&h265=1&adapt720=1",
        options: Options(
            headers: headers,
            contentType: "text/html; charset=utf-8",
            followRedirects: false,
            validateStatus: (status) {
              return status < 500;
            }),
      );
      if (response.statusCode == 302) {
        return response.data.toString().split('"')[1];
      }
      return '';
    } catch (ex) {
      return '';
    }
  }

  Future<String> getCookie() async {
    try {
      var response = await http.get(host + "/share/item/");
      return response.headers["set-cookie"];
    } catch (e) {
      return "error";
    }
  }

  var headers = {
    "user-agent":
        "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1"
  };
}

 

剩下就是改造TrendingScreen里獲取視頻的方法 

  getTrending() async {
    //var cookies = await api.getCookie();
    //api.setCookie(cookies);
    try {
      var response = await http.get(
        api.url,
        headers: api.headers,
      );
      Douyin tiktok = Douyin.fromJson(jsonDecode(response.body));
      tiktok.billboardData.forEach(
        (item) {
          setState(() {
            getVideos(item);
          });
        },
      );
    } catch (ex) {
      SimpleDialog(
        title: Text('Hot videos list is empty'),
      );
      print(ex);
    }
  }

  

把獲取到的無水印視頻載入到view層

getVideos(BillboardData v) async {
    try {
      var url = v.link.split("/")[5];
      var response = await http.get(
        api.video + url + "&dytk",
        headers: api.headers,
      );
      VideoData videoData = VideoData.fromJson(jsonDecode(response.body));
      //獲取無水印的視頻地址
      api.getRedirects(videoData.itemList[0].video.playaddr.uri).then((url) => {
            print( Uri.decodeFull(url)),
            if (url != '')
              {
                videos.add(VideoItem(
                  data: videoData,
                  videourl: url,
                ))
              }
          });
    } catch (ex) {
      print(ex);
    }
  }

 

最後,就是綁定視頻數據啦,大功告成!

  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          DouyinVideoPlayer(
            url: videourl,
          ),
          title(),
          VideoDescription(
            description: data.itemList[0].textExtras[0].hashtagName,
            musicName: data.itemList[0].music.title,
            authorName: data.itemList[0].music.author,
            userName: data.itemList[0].author.nickname,
          ),
          ActionsToolbar(
            comments: data.itemList[0].statistics.commentCount.toString(),
            userImg: data.itemList[0].author.avatarMedium.urlList[0],
            favorite: data.itemList[0].statistics.diggCount,
            coverImg: data.itemList[0].music.covermedium.urlList[0],
          ),
        ],
      ),
    );
  }

 

結語

寫到這裡,手擼一個抖音app完成了基本功能,一些數據轉化比喻點贊數還是原始int數據,需要轉化成K M 等,後續待完善,畢竟花了一天時間來寫這個app純粹為了興趣. 本人也極喜歡玩抖音.

另外因為寫得匆忙,只測試了少量數據,json返回的數據有些為null ,也不知道具體是啥類型,這個待完善。 各位博友們覺得感興趣的點個贊哈.  支持支持!

 

順便說一下,各位支持的話點贊關註我繼續完善剩餘的部分,有需要學習瞭解的可以聯繫我,代碼上傳到github ,地址:https://github.com/WangCharlie/douyin,到這裡就點個star, 謝謝.

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • MMU存在的意義 [導讀] 本文從記憶體管理的發展歷程角度層層遞進,介紹MMU的誕生背景,工作機制。而忽略了具體處理器的具體實現細節,將MMU的工作原理從概念上比較清晰的梳理了一遍。 MMU誕生之前: 在傳統的批處理系統如DOS系統,應用程式與操作系統在記憶體中的佈局大致如下圖: 應用程式直接訪問物理內 ...
  • [導讀] 前文描述了棧的基本概念,本文來聊聊堆是怎麼會事兒。RT Thread 在社區廣受歡迎,閱讀了其內核代碼,實現了堆的管理,代碼設計很清晰,可讀性很好。故一方面瞭解RT Thread內核實現,一方面可以弄清楚其堆的內部實現。將學習體會記錄分享,希望對於堆的理解及實現有一個更深入的認知。 註,文 ...
  • 通常大家開發大部分是本地git push 提交,伺服器上git pull 手動更新。git 可以使用webhook實現自動部署。webhook是倉庫平臺的一個鉤子事件,通過hook 鉤子監聽代碼,回調通知(通知地址就是你在各個git倉庫平臺中填寫的webhook地址,一般在你的某個項目倉庫如mypr ...
  • 根據碎片的生命周期,我們知道onAttach()方法首先會被執行,因此在這裡做一些數據初始化的操作,比如調用getNews()方法獲取幾條模擬的新聞數據,以及完成NewsAdapter的創建,然後在onCreateView()方法中載入了news_title_frag佈局,並給新聞列表的ListVi ...
  • 1/ 概述 利用Spring Boot作為基礎框架,Spring Security作為安全框架,WebSocket作為通信框架,實現點對點聊天和群聊天。 2/ 所需依賴 Spring Boot 版本 1.5.3,使用MongoDB存儲數據(非必須),Maven依賴如下: 配置文件內容: 大致程式結構 ...
  • 新開一坑——Elements of Financial Risk Management in Python 用 python 完成 " Elements of Financial Risk Management (Second Edition)" 一書的課後實踐練習,希望年底之前能完成吧。 項目地址 ...
  • 最近開始學Go語言,但是在使用VS Code 編寫Go的時候出現了插件無法下載的問題。最初我的解決辦法也是從github下載再安裝,但是我並不喜歡這種做法,因為我要在多台pc上使用VS Code編寫Go,所以我覺要重覆多次很麻煩,而且插件的安裝也非常麻煩,我曾經一度想放棄學習Go語言,沒錯因為安裝插 ...
  • 本教程源碼請訪問: "tutorial_demo" 上篇教程我們學習瞭如何使用工廠模式解耦,把對象的創建由程式員交給自定義的工廠類,在這篇教程我們將學到如何使用Spring的IOC解決程式的耦合問題。 一、什麼是IOC IOC:Inversion of Control,控制反轉,將創建對象的權力交給 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...