prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  LinkExtractor is not ignoring .m4v extension (video) by default
  By chance I found out that LinkExtractor is not ignoring the video extension m4v in the same way it is ignoring other video formats.
  
  https://en.wikipedia.org/wiki/M4V
  
  
  </issue>
  <code>
  [start of scrapy/linkextractors/__init__.py]
  1 """
  2 scrapy.linkextractors
  3 
  4 This package contains a collection of Link Extractors.
  5 
  6 For more info see docs/topics/link-extractors.rst
  7 """
  8 import re
  9 
  10 from six.moves.urllib.parse import urlparse
  11 from parsel.csstranslator import HTMLTranslator
  12 from w3lib.url import canonicalize_url
  13 
  14 from scrapy.utils.misc import arg_to_iter
  15 from scrapy.utils.url import (
  16     url_is_from_any_domain, url_has_any_extension,
  17 )
  18 
  19 
  20 # common file extensions that are not followed if they occur in links
  21 IGNORED_EXTENSIONS = [
  22     # images
  23     'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',
  24     'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',
  25 
  26     # audio
  27     'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',
  28 
  29     # video
  30     '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',
  31     'm4a',
  32 
  33     # office suites
  34     'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',
  35     'odp',
  36 
  37     # other
  38     'css', 'pdf', 'exe', 'bin', 'rss', 'zip', 'rar',
  39 ]
  40 
  41 
  42 _re_type = type(re.compile("", 0))
  43 _matches = lambda url, regexs: any(r.search(url) for r in regexs)
  44 _is_valid_url = lambda url: url.split('://', 1)[0] in {'http', 'https', 'file'}
  45 
  46 
  47 class FilteringLinkExtractor(object):
  48 
  49     _csstranslator = HTMLTranslator()
  50 
  51     def __init__(self, link_extractor, allow, deny, allow_domains, deny_domains,
  52                  restrict_xpaths, canonicalize, deny_extensions, restrict_css):
  53 
  54         self.link_extractor = link_extractor
  55 
  56         self.allow_res = [x if isinstance(x, _re_type) else re.compile(x)
  57                           for x in arg_to_iter(allow)]
  58         self.deny_res = [x if isinstance(x, _re_type) else re.compile(x)
  59                          for x in arg_to_iter(deny)]
  60 
  61         self.allow_domains = set(arg_to_iter(allow_domains))
  62         self.deny_domains = set(arg_to_iter(deny_domains))
  63 
  64         self.restrict_xpaths = tuple(arg_to_iter(restrict_xpaths))
  65         self.restrict_xpaths += tuple(map(self._csstranslator.css_to_xpath,
  66                                           arg_to_iter(restrict_css)))
  67 
  68         self.canonicalize = canonicalize
  69         if deny_extensions is None:
  70             deny_extensions = IGNORED_EXTENSIONS
  71         self.deny_extensions = {'.' + e for e in arg_to_iter(deny_extensions)}
  72 
  73     def _link_allowed(self, link):
  74         if not _is_valid_url(link.url):
  75             return False
  76         if self.allow_res and not _matches(link.url, self.allow_res):
  77             return False
  78         if self.deny_res and _matches(link.url, self.deny_res):
  79             return False
  80         parsed_url = urlparse(link.url)
  81         if self.allow_domains and not url_is_from_any_domain(parsed_url, self.allow_domains):
  82             return False
  83         if self.deny_domains and url_is_from_any_domain(parsed_url, self.deny_domains):
  84             return False
  85         if self.deny_extensions and url_has_any_extension(parsed_url, self.deny_extensions):
  86             return False
  87         return True
  88 
  89     def matches(self, url):
  90 
  91         if self.allow_domains and not url_is_from_any_domain(url, self.allow_domains):
  92             return False
  93         if self.deny_domains and url_is_from_any_domain(url, self.deny_domains):
  94             return False
  95 
  96         allowed = (regex.search(url) for regex in self.allow_res) if self.allow_res else [True]
  97         denied = (regex.search(url) for regex in self.deny_res) if self.deny_res else []
  98         return any(allowed) and not any(denied)
  99 
  100     def _process_links(self, links):
  101         links = [x for x in links if self._link_allowed(x)]
  102         if self.canonicalize:
  103             for link in links:
  104                 link.url = canonicalize_url(link.url)
  105         links = self.link_extractor._process_links(links)
  106         return links
  107 
  108     def _extract_links(self, *args, **kwargs):
  109         return self.link_extractor._extract_links(*args, **kwargs)
  110 
  111 
  112 # Top-level imports
  113 from .lxmlhtml import LxmlLinkExtractor as LinkExtractor
  114
  [end of scrapy/linkextractors/__init__.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/scrapy/linkextractors/__init__.py b/scrapy/linkextractors/__init__.py
  --- a/scrapy/linkextractors/__init__.py
  +++ b/scrapy/linkextractors/__init__.py
  @@ -28,7 +28,7 @@
   
       # video
       '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',
  -    'm4a',
  +    'm4a', 'm4v',
   
       # office suites
       'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',
  
verification_info: {"golden_diff": "diff --git a/scrapy/linkextractors/__init__.py b/scrapy/linkextractors/__init__.py\n--- a/scrapy/linkextractors/__init__.py\n+++ b/scrapy/linkextractors/__init__.py\n@@ -28,7 +28,7 @@\n \n     # video\n     '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',\n-    'm4a',\n+    'm4a', 'm4v',\n \n     # office suites\n     'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',\n", "issue": "LinkExtractor is not ignoring .m4v extension (video) by default\nBy chance I found out that LinkExtractor is not ignoring the video extension m4v in the same way it is ignoring other video formats.\r\n\r\nhttps://en.wikipedia.org/wiki/M4V\r\n\n", "before_files": [{"content": "\"\"\"\nscrapy.linkextractors\n\nThis package contains a collection of Link Extractors.\n\nFor more info see docs/topics/link-extractors.rst\n\"\"\"\nimport re\n\nfrom six.moves.urllib.parse import urlparse\nfrom parsel.csstranslator import HTMLTranslator\nfrom w3lib.url import canonicalize_url\n\nfrom scrapy.utils.misc import arg_to_iter\nfrom scrapy.utils.url import (\n    url_is_from_any_domain, url_has_any_extension,\n)\n\n\n# common file extensions that are not followed if they occur in links\nIGNORED_EXTENSIONS = [\n    # images\n    'mng', 'pct', 'bmp', 'gif', 'jpg', 'jpeg', 'png', 'pst', 'psp', 'tif',\n    'tiff', 'ai', 'drw', 'dxf', 'eps', 'ps', 'svg',\n\n    # audio\n    'mp3', 'wma', 'ogg', 'wav', 'ra', 'aac', 'mid', 'au', 'aiff',\n\n    # video\n    '3gp', 'asf', 'asx', 'avi', 'mov', 'mp4', 'mpg', 'qt', 'rm', 'swf', 'wmv',\n    'm4a',\n\n    # office suites\n    'xls', 'xlsx', 'ppt', 'pptx', 'pps', 'doc', 'docx', 'odt', 'ods', 'odg',\n    'odp',\n\n    # other\n    'css', 'pdf', 'exe', 'bin', 'rss', 'zip', 'rar',\n]\n\n\n_re_type = type(re.compile(\"\", 0))\n_matches = lambda url, regexs: any(r.search(url) for r in regexs)\n_is_valid_url = lambda url: url.split('://', 1)[0] in {'http', 'https', 'file'}\n\n\nclass FilteringLinkExtractor(object):\n\n    _csstranslator = HTMLTranslator()\n\n    def __init__(self, link_extractor, allow, deny, allow_domains, deny_domains,\n                 restrict_xpaths, canonicalize, deny_extensions, restrict_css):\n\n        self.link_extractor = link_extractor\n\n        self.allow_res = [x if isinstance(x, _re_type) else re.compile(x)\n                          for x in arg_to_iter(allow)]\n        self.deny_res = [x if isinstance(x, _re_type) else re.compile(x)\n                         for x in arg_to_iter(deny)]\n\n        self.allow_domains = set(arg_to_iter(allow_domains))\n        self.deny_domains = set(arg_to_iter(deny_domains))\n\n        self.restrict_xpaths = tuple(arg_to_iter(restrict_xpaths))\n        self.restrict_xpaths += tuple(map(self._csstranslator.css_to_xpath,\n                                          arg_to_iter(restrict_css)))\n\n        self.canonicalize = canonicalize\n        if deny_extensions is None:\n            deny_extensions = IGNORED_EXTENSIONS\n        self.deny_extensions = {'.' + e for e in arg_to_iter(deny_extensions)}\n\n    def _link_allowed(self, link):\n        if not _is_valid_url(link.url):\n            return False\n        if self.allow_res and not _matches(link.url, self.allow_res):\n            return False\n        if self.deny_res and _matches(link.url, self.deny_res):\n            return False\n        parsed_url = urlparse(link.url)\n        if self.allow_domains and not url_is_from_any_domain(parsed_url, self.allow_domains):\n            return False\n        if self.deny_domains and url_is_from_any_domain(parsed_url, self.deny_domains):\n            return False\n        if self.deny_extensions and url_has_any_extension(parsed_url, self.deny_extensions):\n            return False\n        return True\n\n    def matches(self, url):\n\n        if self.allow_domains and not url_is_from_any_domain(url, self.allow_domains):\n            return False\n        if self.deny_domains and url_is_from_any_domain(url, self.deny_domains):\n            return False\n\n        allowed = (regex.search(url) for regex in self.allow_res) if self.allow_res else [True]\n        denied = (regex.search(url) for regex in self.deny_res) if self.deny_res else []\n        return any(allowed) and not any(denied)\n\n    def _process_links(self, links):\n        links = [x for x in links if self._link_allowed(x)]\n        if self.canonicalize:\n            for link in links:\n                link.url = canonicalize_url(link.url)\n        links = self.link_extractor._process_links(links)\n        return links\n\n    def _extract_links(self, *args, **kwargs):\n        return self.link_extractor._extract_links(*args, **kwargs)\n\n\n# Top-level imports\nfrom .lxmlhtml import LxmlLinkExtractor as LinkExtractor\n", "path": "scrapy/linkextractors/__init__.py"}]}