text: from __future__ import unicode_literals
  
  from ..utils import (ExtractorError, determine_ext, int_or_none, try_get,
                       unescapeHTML, url_or_none)
  from .common import InfoExtractor
  
  
  class NineGagIE(InfoExtractor):
      IE_NAME = "9gag"
      _VALID_URL = r"https?://(?:www\.)?9gag\.com/gag/(?P<id>[^/?&#]+)"
  
      _TESTS = [
          {
              "url": "https://9gag.com/gag/ae5Ag7B",
              "info_dict": {
                  "id": "ae5Ag7B",
                  "ext": "mp4",
                  "title": "Capybara Agility Training",
                  "upload_date": "20191108",
                  "timestamp": 1573237208,
                  "categories": ["Awesome"],
                  "tags": ["Weimaraner", "American Pit Bull Terrier"],
                  "duration": 44,
                  "like_count": int,
                  "dislike_count": int,
                  "comment_count": int,
              },
          },
          {
              # HTML escaped title
              "url": "https://9gag.com/gag/av5nvyb",
              "only_matching": True,
          },
      ]
  
      def _real_extract(self, url):
          post_id = self._match_id(url)
          post = self._download_json(
              "https://9gag.com/v1/post", post_id, query={"id": post_id}
          )["data"]["post"]
  
          if post.get("type") != "Animated":
              raise ExtractorError(
                  "The given url does not contain a video", expected=True
              )
  
          title = unescapeHTML(post["title"])
  
          duration = None
          formats = []
          thumbnails = []
          for key, image in (post.get("images") or {}).items():
              image_url = url_or_none(image.get("url"))
              if not image_url:
                  continue
              ext = determine_ext(image_url)
              image_id = key.strip("image")
              common = {
                  "url": image_url,
                  "width": int_or_none(image.get("width")),
                  "height": int_or_none(image.get("height")),
              }
              if ext in ("jpg", "png"):
                  webp_url = image.get("webpUrl")
                  if webp_url:
                      t = common.copy()
                      t.update(
                          {
                              "id": image_id + "-webp",
                              "url": webp_url,
                          }
                      )
                      thumbnails.append(t)
                  common.update(
                      {
                          "id": image_id,
                          "ext": ext,
                      }
                  )
                  thumbnails.append(common)
              elif ext in ("webm", "mp4"):
                  if not duration:
                      duration = int_or_none(image.get("duration"))
                  common["acodec"] = "none" if image.get("hasAudio") == 0 else None
                  for vcodec in ("vp8", "vp9", "h265"):
                      c_url = image.get(vcodec + "Url")
                      if not c_url:
                          continue
                      c_f = common.copy()
                      c_f.update(
                          {
                              "format_id": image_id + "-" + vcodec,
                              "url": c_url,
                              "vcodec": vcodec,
                          }
                      )
                      formats.append(c_f)
                  common.update(
                      {
                          "ext": ext,
                          "format_id": image_id,
                      }
                  )
                  formats.append(common)
          self._sort_formats(formats)
  
          section = try_get(post, lambda x: x["postSection"]["name"])
  
          tags = None
          post_tags = post.get("tags")
          if post_tags:
              tags = []
              for tag in post_tags:
                  tag_key = tag.get("key")
                  if not tag_key:
                      continue
                  tags.append(tag_key)
  
          get_count = lambda x: int_or_none(post.get(x + "Count"))
  
          return {
              "id": post_id,
              "title": title,
              "timestamp": int_or_none(post.get("creationTs")),
              "duration": duration,
              "formats": formats,
              "thumbnails": thumbnails,
              "like_count": get_count("upVote"),
              "dislike_count": get_count("downVote"),
              "comment_count": get_count("comments"),
              "age_limit": 18 if post.get("nsfw") == 1 else None,
              "categories": [section] if section else None,
              "tags": tags,
          }
  