text: # coding: utf-8
  from __future__ import unicode_literals
  
  import re
  
  from .adobepass import AdobePassIE
  from ..compat import compat_str
  from ..utils import (
      int_or_none,
      determine_ext,
      parse_age_limit,
      try_get,
      urlencode_postdata,
      ExtractorError,
  )
  
  
  class GoIE(AdobePassIE):
      _SITE_INFO = {
          "abc": {
              "brand": "001",
              "requestor_id": "ABC",
          },
          "freeform": {
              "brand": "002",
              "requestor_id": "ABCFamily",
          },
          "watchdisneychannel": {
              "brand": "004",
              "resource_id": "Disney",
          },
          "watchdisneyjunior": {
              "brand": "008",
              "resource_id": "DisneyJunior",
          },
          "watchdisneyxd": {
              "brand": "009",
              "resource_id": "DisneyXD",
          },
          "disneynow": {
              "brand": "011",
              "resource_id": "Disney",
          },
          "fxnow.fxnetworks": {
              "brand": "025",
              "requestor_id": "dtci",
          },
      }
      _VALID_URL = r"""(?x)
                      https?://
                          (?:
                              (?:(?P<sub_domain>%s)\.)?go|
                              (?P<sub_domain_2>abc|freeform|disneynow|fxnow\.fxnetworks)
                          )\.com/
                          (?:
                              (?:[^/]+/)*(?P<id>[Vv][Dd][Kk][Aa]\w+)|
                              (?:[^/]+/)*(?P<display_id>[^/?\#]+)
                          )
                      """ % "|".join(
          list(_SITE_INFO.keys())
      )
      _TESTS = [
          {
              "url": "http://abc.go.com/shows/designated-survivor/video/most-recent/VDKA3807643",
              "info_dict": {
                  "id": "VDKA3807643",
                  "ext": "mp4",
                  "title": "The Traitor in the White House",
                  "description": "md5:05b009d2d145a1e85d25111bd37222e8",
              },
              "params": {
                  # m3u8 download
                  "skip_download": True,
              },
              "skip": "This content is no longer available.",
          },
          {
              "url": "http://watchdisneyxd.go.com/doraemon",
              "info_dict": {
                  "title": "Doraemon",
                  "id": "SH55574025",
              },
              "playlist_mincount": 51,
          },
          {
              "url": "http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood",
              "info_dict": {
                  "id": "VDKA3609139",
                  "ext": "mp4",
                  "title": "This Guilty Blood",
                  "description": "md5:f18e79ad1c613798d95fdabfe96cd292",
                  "age_limit": 14,
              },
              "params": {
                  "geo_bypass_ip_block": "3.244.239.0/24",
                  # m3u8 download
                  "skip_download": True,
              },
          },
          {
              "url": "https://abc.com/shows/the-rookie/episode-guide/season-02/03-the-bet",
              "info_dict": {
                  "id": "VDKA13435179",
                  "ext": "mp4",
                  "title": "The Bet",
                  "description": "md5:c66de8ba2e92c6c5c113c3ade84ab404",
                  "age_limit": 14,
              },
              "params": {
                  "geo_bypass_ip_block": "3.244.239.0/24",
                  # m3u8 download
                  "skip_download": True,
              },
          },
          {
              "url": "https://fxnow.fxnetworks.com/shows/better-things/video/vdka12782841",
              "info_dict": {
                  "id": "VDKA12782841",
                  "ext": "mp4",
                  "title": "First Look: Better Things - Season 2",
                  "description": "md5:fa73584a95761c605d9d54904e35b407",
              },
              "params": {
                  "geo_bypass_ip_block": "3.244.239.0/24",
                  # m3u8 download
                  "skip_download": True,
              },
          },
          {
              "url": "https://abc.com/shows/modern-family/episode-guide/season-01/101-pilot",
              "info_dict": {
                  "id": "VDKA22600213",
                  "ext": "mp4",
                  "title": "Pilot",
                  "description": "md5:74306df917cfc199d76d061d66bebdb4",
              },
              "params": {
                  # m3u8 download
                  "skip_download": True,
              },
          },
          {
              "url": "http://abc.go.com/shows/the-catch/episode-guide/season-01/10-the-wedding",
              "only_matching": True,
          },
          {
              "url": "http://abc.go.com/shows/world-news-tonight/episode-guide/2017-02/17-021717-intense-stand-off-between-man-with-rifle-and-police-in-oakland",
              "only_matching": True,
          },
          {
              # brand 004
              "url": "http://disneynow.go.com/shows/big-hero-6-the-series/season-01/episode-10-mr-sparkles-loses-his-sparkle/vdka4637915",
              "only_matching": True,
          },
          {
              # brand 008
              "url": "http://disneynow.go.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013",
              "only_matching": True,
          },
          {
              "url": "https://disneynow.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013",
              "only_matching": True,
          },
      ]
  
      def _extract_videos(self, brand, video_id="-1", show_id="-1"):
          display_id = video_id if video_id != "-1" else show_id
          return self._download_json(
              "http://api.contents.watchabc.go.com/vp2/ws/contents/3000/videos/%s/001/-1/%s/-1/%s/-1/-1.json"
              % (brand, show_id, video_id),
              display_id,
          )["video"]
  
      def _real_extract(self, url):
          mobj = re.match(self._VALID_URL, url)
          sub_domain = mobj.group("sub_domain") or mobj.group("sub_domain_2")
          video_id, display_id = mobj.group("id", "display_id")
          site_info = self._SITE_INFO.get(sub_domain, {})
          brand = site_info.get("brand")
          if not video_id or not site_info:
              webpage = self._download_webpage(url, display_id or video_id)
              data = self._parse_json(
                  self._search_regex(
                      r'["\']__abc_com__["\']\s*\]\s*=\s*({.+?})\s*;',
                      webpage,
                      "data",
                      default="{}",
                  ),
                  display_id or video_id,
                  fatal=False,
              )
              # https://abc.com/shows/modern-family/episode-guide/season-01/101-pilot
              layout = try_get(
                  data, lambda x: x["page"]["content"]["video"]["layout"], dict
              )
              video_id = None
              if layout:
                  video_id = try_get(
                      layout,
                      (lambda x: x["videoid"], lambda x: x["video"]["id"]),
                      compat_str,
                  )
              if not video_id:
                  video_id = self._search_regex(
                      (
                          # There may be inner quotes, e.g. data-video-id="'VDKA3609139'"
                          # from http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood
                          r'data-video-id=["\']*(VDKA\w+)',
                          # page.analytics.videoIdCode
                          r'\bvideoIdCode["\']\s*:\s*["\']((?:vdka|VDKA)\w+)',
                          # https://abc.com/shows/the-rookie/episode-guide/season-02/03-the-bet
                          r'\b(?:video)?id["\']\s*:\s*["\'](VDKA\w+)',
                      ),
                      webpage,
                      "video id",
                      default=video_id,
                  )
              if not site_info:
                  brand = self._search_regex(
                      (
                          r'data-brand=\s*["\']\s*(\d+)',
                          r'data-page-brand=\s*["\']\s*(\d+)',
                      ),
                      webpage,
                      "brand",
                      default="004",
                  )
                  site_info = next(
                      si for _, si in self._SITE_INFO.items() if si.get("brand") == brand
                  )
              if not video_id:
                  # show extraction works for Disney, DisneyJunior and DisneyXD
                  # ABC and Freeform has different layout
                  show_id = self._search_regex(
                      r'data-show-id=["\']*(SH\d+)', webpage, "show id"
                  )
                  videos = self._extract_videos(brand, show_id=show_id)
                  show_title = self._search_regex(
                      r'data-show-title="([^"]+)"', webpage, "show title", fatal=False
                  )
                  entries = []
                  for video in videos:
                      entries.append(
                          self.url_result(
                              video["url"], "Go", video.get("id"), video.get("title")
                          )
                      )
                  entries.reverse()
                  return self.playlist_result(entries, show_id, show_title)
          video_data = self._extract_videos(brand, video_id)[0]
          video_id = video_data["id"]
          title = video_data["title"]
  
          formats = []
          for asset in video_data.get("assets", {}).get("asset", []):
              asset_url = asset.get("value")
              if not asset_url:
                  continue
              format_id = asset.get("format")
              ext = determine_ext(asset_url)
              if ext == "m3u8":
                  video_type = video_data.get("type")
                  data = {
                      "video_id": video_data["id"],
                      "video_type": video_type,
                      "brand": brand,
                      "device": "001",
                  }
                  if video_data.get("accesslevel") == "1":
                      requestor_id = site_info.get("requestor_id", "DisneyChannels")
                      resource = site_info.get("resource_id") or self._get_mvpd_resource(
                          requestor_id, title, video_id, None
                      )
                      auth = self._extract_mvpd_auth(
                          url, video_id, requestor_id, resource
                      )
                      data.update(
                          {
                              "token": auth,
                              "token_type": "ap",
                              "adobe_requestor_id": requestor_id,
                          }
                      )
                  else:
                      self._initialize_geo_bypass({"countries": ["US"]})
                  entitlement = self._download_json(
                      "https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json",
                      video_id,
                      data=urlencode_postdata(data),
                  )
                  errors = entitlement.get("errors", {}).get("errors", [])
                  if errors:
                      for error in errors:
                          if error.get("code") == 1002:
                              self.raise_geo_restricted(
                                  error["message"], countries=["US"]
                              )
                      error_message = ", ".join([error["message"] for error in errors])
                      raise ExtractorError(
                          "%s said: %s" % (self.IE_NAME, error_message), expected=True
                      )
                  asset_url += "?" + entitlement["uplynkData"]["sessionKey"]
                  formats.extend(
                      self._extract_m3u8_formats(
                          asset_url,
                          video_id,
                          "mp4",
                          m3u8_id=format_id or "hls",
                          fatal=False,
                      )
                  )
              else:
                  f = {
                      "format_id": format_id,
                      "url": asset_url,
                      "ext": ext,
                  }
                  if re.search(r"(?:/mp4/source/|_source\.mp4)", asset_url):
                      f.update(
                          {
                              "format_id": ("%s-" % format_id if format_id else "")
                              + "SOURCE",
                              "preference": 1,
                          }
                      )
                  else:
                      mobj = re.search(r"/(\d+)x(\d+)/", asset_url)
                      if mobj:
                          height = int(mobj.group(2))
                          f.update(
                              {
                                  "format_id": ("%s-" % format_id if format_id else "")
                                  + "%dP" % height,
                                  "width": int(mobj.group(1)),
                                  "height": height,
                              }
                          )
                  formats.append(f)
          self._sort_formats(formats)
  
          subtitles = {}
          for cc in video_data.get("closedcaption", {}).get("src", []):
              cc_url = cc.get("value")
              if not cc_url:
                  continue
              ext = determine_ext(cc_url)
              if ext == "xml":
                  ext = "ttml"
              subtitles.setdefault(cc.get("lang"), []).append(
                  {
                      "url": cc_url,
                      "ext": ext,
                  }
              )
  
          thumbnails = []
          for thumbnail in video_data.get("thumbnails", {}).get("thumbnail", []):
              thumbnail_url = thumbnail.get("value")
              if not thumbnail_url:
                  continue
              thumbnails.append(
                  {
                      "url": thumbnail_url,
                      "width": int_or_none(thumbnail.get("width")),
                      "height": int_or_none(thumbnail.get("height")),
                  }
              )
  
          return {
              "id": video_id,
              "title": title,
              "description": video_data.get("longdescription")
              or video_data.get("description"),
              "duration": int_or_none(video_data.get("duration", {}).get("value"), 1000),
              "age_limit": parse_age_limit(video_data.get("tvrating", {}).get("rating")),
              "episode_number": int_or_none(video_data.get("episodenumber")),
              "series": video_data.get("show", {}).get("title"),
              "season_number": int_or_none(video_data.get("season", {}).get("num")),
              "thumbnails": thumbnails,
              "formats": formats,
              "subtitles": subtitles,
          }
  