We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Lutris fails to open .tar.gz with "not a gzip file" if file is a tar file
Lutris fails to open archive where 7z doesn't. If I rename the file to `TTRLinux-v1.2.3.tar` Lutris can open and extract it. `format: tar` doesn't work here.

```
files:
- client: https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz
game:
  exe: Launcher
installer:
- extract:
    file: client
    format: tar
```

```
2019-04-04 16:53:40,809: Downloading file 1 of 1
2019-04-04 16:53:40,811: Downloading [client]: https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz to /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz
2019-04-04 16:53:40,814: Downloading https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz to /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz
2019-04-04 16:53:40,816: Starting download of:
 https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz
2019-04-04 16:53:44,410: Finished downloading https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz
2019-04-04 16:53:44,822: Installer command: {'extract': {'format': 'tar', 'file': 'client'}}
2019-04-04 16:53:44,823: Extracting TTRLinux-v1.2.3.tar.gz
2019-04-04 16:53:44,824: extracting file /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz to /home/cxf/Games/toontown-rewritten
2019-04-04 16:53:44,831: Extracting /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz to /home/cxf/Games/toontown-rewritten
2019-04-04 16:53:44,832: Extraction failed: not a gzip file
Traceback (most recent call last):
  File "/usr/lib/python3.6/tarfile.py", line 1643, in gzopen
    t = cls.taropen(name, mode, fileobj, **kwargs)
  File "/usr/lib/python3.6/tarfile.py", line 1619, in taropen
    return cls(name, mode, fileobj, **kwargs)
  File "/usr/lib/python3.6/tarfile.py", line 1482, in __init__
    self.firstmember = self.next()
  File "/usr/lib/python3.6/tarfile.py", line 2297, in next
    tarinfo = self.tarinfo.fromtarfile(self)
  File "/usr/lib/python3.6/tarfile.py", line 1092, in fromtarfile
    buf = tarfile.fileobj.read(BLOCKSIZE)
  File "/usr/lib/python3.6/gzip.py", line 276, in read
    return self._buffer.read(size)
  File "/usr/lib/python3.6/_compression.py", line 68, in readinto
    data = self.read(len(byte_view))
  File "/usr/lib/python3.6/gzip.py", line 463, in read
    if not self._read_gzip_header():
  File "/usr/lib/python3.6/gzip.py", line 411, in _read_gzip_header
    raise OSError('Not a gzipped file (%r)' % magic)
OSError: Not a gzipped file (b'To')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/cxf/.lutris/lutris/util/extract.py", line 90, in extract_archive
    _do_extract(path, temp_path, opener, mode, extractor)
  File "/home/cxf/.lutris/lutris/util/extract.py", line 134, in _do_extract
    handler = opener(archive, mode)
  File "/usr/lib/python3.6/tarfile.py", line 1589, in open
    return func(name, filemode, fileobj, **kwargs)
  File "/usr/lib/python3.6/tarfile.py", line 1647, in gzopen
    raise ReadError("not a gzip file")
tarfile.ReadError: not a gzip file
2019-04-04 16:53:44,835: Error while completing task <bound method CommandsMixin.extract of <lutris.installer.interpreter.ScriptInterpreter object at 0x7f0507748c88>>: not a gzip file
<class 'lutris.util.extract.ExtractFailure'> not a gzip file
  File "/home/cxf/.lutris/lutris/util/jobs.py", line 30, in target
    result = self.function(*args, **kwargs)
  File "/home/cxf/.lutris/lutris/installer/commands.py", line 180, in extract
    extract.extract_archive, filename, dest_path, merge_single, extractor
  File "/home/cxf/.lutris/lutris/installer/commands.py", line 527, in _killable_process
    result = result_obj.get()  # Wait process end & reraise exceptions
  File "/usr/lib/python3.6/multiprocessing/pool.py", line 670, in get
    raise self._value
2019-04-04 16:53:49,289: Install cancelled
```

```
cxf@PC:~/Downloads$ 7z e TTRLinux-v1.2.3.tar.gz 

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i5-4460  CPU @ 3.20GHz (306C3),ASM,AES-NI)

Scanning the drive for archives:
1 file, 37233152 bytes (36 MiB)

Extracting archive: TTRLinux-v1.2.3.tar.gz
WARNING:
TTRLinux-v1.2.3.tar.gz
Can not open the file as [gzip] archive
The file is open as [tar] archive

--
Path = TTRLinux-v1.2.3.tar.gz
Open WARNING: Can not open the file as [gzip] archive
Type = tar
Physical Size = 37233152
Headers Size = 2048
Code Page = UTF-8

Everything is Ok

Archives with Warnings: 1
Folders: 1
Files: 1
Size:       37231062
Compressed: 37233152
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lutris/util/extract.py`
Content:
```
1 import os
2 import uuid
3 import shutil
4 import tarfile
5 import subprocess
6 import gzip
7 import zlib
8 from lutris.util import system
9 from lutris.util.log import logger
10 from lutris import settings
11 
12 
13 class ExtractFailure(Exception):
14     """Exception raised when and archive fails to extract"""
15 
16 
17 def is_7zip_supported(path, extractor):
18     supported_extractors = (
19         "7z",
20         "xz",
21         "bzip2",
22         "gzip",
23         "tar",
24         "zip",
25         "ar",
26         "arj",
27         "cab",
28         "chm",
29         "cpio",
30         "cramfs",
31         "dmg",
32         "ext",
33         "fat",
34         "gpt",
35         "hfs",
36         "ihex",
37         "iso",
38         "lzh",
39         "lzma",
40         "mbr",
41         "msi",
42         "nsis",
43         "ntfs",
44         "qcow2",
45         "rar",
46         "rpm",
47         "squashfs",
48         "udf",
49         "uefi",
50         "vdi",
51         "vhd",
52         "vmdk",
53         "wim",
54         "xar",
55         "z",
56     )
57     if extractor:
58         return extractor.lower() in supported_extractors
59     _base, ext = os.path.splitext(path)
60     if ext:
61         ext = ext.lstrip(".").lower()
62         return ext in supported_extractors
63 
64 
65 def extract_archive(path, to_directory=".", merge_single=True, extractor=None):
66     path = os.path.abspath(path)
67     mode = None
68     logger.debug("Extracting %s to %s", path, to_directory)
69 
70     if path.endswith(".tar.gz") or path.endswith(".tgz") or extractor == "tgz":
71         opener, mode = tarfile.open, "r:gz"
72     elif path.endswith(".tar.xz") or path.endswith(".txz") or extractor == "txz":
73         opener, mode = tarfile.open, "r:xz"
74     elif path.endswith(".tar") or extractor == "tar":
75         opener, mode = tarfile.open, "r:"
76     elif path.endswith(".gz") or extractor == "gzip":
77         decompress_gz(path, to_directory)
78         return
79     elif path.endswith(".tar.bz2") or path.endswith(".tbz") or extractor == "bz2":
80         opener, mode = tarfile.open, "r:bz2"
81     elif is_7zip_supported(path, extractor):
82         opener = "7zip"
83     else:
84         raise RuntimeError(
85             "Could not extract `%s` as no appropriate extractor is found" % path
86         )
87     temp_name = ".extract-" + str(uuid.uuid4())[:8]
88     temp_path = temp_dir = os.path.join(to_directory, temp_name)
89     try:
90         _do_extract(path, temp_path, opener, mode, extractor)
91     except (OSError, zlib.error, tarfile.ReadError) as ex:
92         logger.exception("Extraction failed: %s", ex)
93         raise ExtractFailure(str(ex))
94     if merge_single:
95         extracted = os.listdir(temp_path)
96         if len(extracted) == 1:
97             temp_path = os.path.join(temp_path, extracted[0])
98 
99     if os.path.isfile(temp_path):
100         destination_path = os.path.join(to_directory, extracted[0])
101         if os.path.isfile(destination_path):
102             logger.warning("Overwrite existing file %s", destination_path)
103             os.remove(destination_path)
104         shutil.move(temp_path, to_directory)
105         os.removedirs(temp_dir)
106     else:
107         for archive_file in os.listdir(temp_path):
108             source_path = os.path.join(temp_path, archive_file)
109             destination_path = os.path.join(to_directory, archive_file)
110             # logger.debug("Moving extracted files from %s to %s", source_path, destination_path)
111 
112             if system.path_exists(destination_path):
113                 logger.warning("Overwrite existing path %s", destination_path)
114                 if os.path.isfile(destination_path):
115                     os.remove(destination_path)
116                     shutil.move(source_path, destination_path)
117                 elif os.path.isdir(destination_path):
118                     try:
119                         system.merge_folders(source_path, destination_path)
120                     except OSError as ex:
121                         logger.error("Failed to merge to destination %s: %s", destination_path, ex)
122                         raise ExtractFailure(str(ex))
123             else:
124                 shutil.move(source_path, destination_path)
125         system.remove_folder(temp_dir)
126     logger.debug("Finished extracting %s to %s", path, to_directory)
127     return path, to_directory
128 
129 
130 def _do_extract(archive, dest, opener, mode=None, extractor=None):
131     if opener == "7zip":
132         extract_7zip(archive, dest, archive_type=extractor)
133     else:
134         handler = opener(archive, mode)
135         handler.extractall(dest)
136         handler.close()
137 
138 
139 def decompress_gz(file_path, dest_path=None):
140     """Decompress a gzip file."""
141     if dest_path:
142         dest_filename = os.path.join(dest_path, os.path.basename(file_path[:-3]))
143     else:
144         dest_filename = file_path[:-3]
145 
146     gzipped_file = gzip.open(file_path, "rb")
147     file_content = gzipped_file.read()
148     gzipped_file.close()
149 
150     dest_file = open(dest_filename, "wb")
151     dest_file.write(file_content)
152     dest_file.close()
153 
154     return dest_path
155 
156 
157 def extract_7zip(path, dest, archive_type=None):
158     _7zip_path = os.path.join(settings.RUNTIME_DIR, "p7zip/7z")
159     if not system.path_exists(_7zip_path):
160         _7zip_path = system.find_executable("7z")
161     if not system.path_exists(_7zip_path):
162         raise OSError("7zip is not found in the lutris runtime or on the system")
163     command = [_7zip_path, "x", path, "-o{}".format(dest), "-aoa"]
164     if archive_type:
165         command.append("-t{}".format(archive_type))
166     subprocess.call(command)
167
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lutris/util/extract.py b/lutris/util/extract.py
--- a/lutris/util/extract.py
+++ b/lutris/util/extract.py
@@ -67,23 +67,42 @@
     mode = None
     logger.debug("Extracting %s to %s", path, to_directory)
 
-    if path.endswith(".tar.gz") or path.endswith(".tgz") or extractor == "tgz":
+    if extractor is None:
+        if path.endswith(".tar.gz") or path.endswith(".tgz"):
+            extractor = "tgz"
+        elif path.endswith(".tar.xz") or path.endswith(".txz"):
+            extractor = "txz"
+        elif path.endswith(".tar"):
+            extractor = "tar"
+        elif path.endswith(".tar.bz2") or path.endswith(".tbz"):
+            extractor = "bz2"
+        elif path.endswith(".gz"):
+            extractor = "gzip"
+        elif is_7zip_supported(path, None):
+            extractor = None
+        else:
+            raise RuntimeError(
+                "Could not extract `%s` - no appropriate extractor found" % path
+            )
+
+    if extractor == "tgz":
         opener, mode = tarfile.open, "r:gz"
-    elif path.endswith(".tar.xz") or path.endswith(".txz") or extractor == "txz":
+    elif extractor == "txz":
         opener, mode = tarfile.open, "r:xz"
-    elif path.endswith(".tar") or extractor == "tar":
+    elif extractor == "tar":
         opener, mode = tarfile.open, "r:"
-    elif path.endswith(".gz") or extractor == "gzip":
+    elif extractor == "bz2":
+        opener, mode = tarfile.open, "r:bz2"
+    elif extractor == "gzip":
         decompress_gz(path, to_directory)
         return
-    elif path.endswith(".tar.bz2") or path.endswith(".tbz") or extractor == "bz2":
-        opener, mode = tarfile.open, "r:bz2"
-    elif is_7zip_supported(path, extractor):
+    elif extractor is None or is_7zip_supported(path, extractor):
         opener = "7zip"
     else:
         raise RuntimeError(
-            "Could not extract `%s` as no appropriate extractor is found" % path
+            "Could not extract `%s` - unknown format specified" % path
         )
+
     temp_name = ".extract-" + str(uuid.uuid4())[:8]
     temp_path = temp_dir = os.path.join(to_directory, temp_name)
     try:

{"golden_diff": "diff --git a/lutris/util/extract.py b/lutris/util/extract.py\n--- a/lutris/util/extract.py\n+++ b/lutris/util/extract.py\n@@ -67,23 +67,42 @@\n     mode = None\n     logger.debug(\"Extracting %s to %s\", path, to_directory)\n \n-    if path.endswith(\".tar.gz\") or path.endswith(\".tgz\") or extractor == \"tgz\":\n+    if extractor is None:\n+        if path.endswith(\".tar.gz\") or path.endswith(\".tgz\"):\n+            extractor = \"tgz\"\n+        elif path.endswith(\".tar.xz\") or path.endswith(\".txz\"):\n+            extractor = \"txz\"\n+        elif path.endswith(\".tar\"):\n+            extractor = \"tar\"\n+        elif path.endswith(\".tar.bz2\") or path.endswith(\".tbz\"):\n+            extractor = \"bz2\"\n+        elif path.endswith(\".gz\"):\n+            extractor = \"gzip\"\n+        elif is_7zip_supported(path, None):\n+            extractor = None\n+        else:\n+            raise RuntimeError(\n+                \"Could not extract `%s` - no appropriate extractor found\" % path\n+            )\n+\n+    if extractor == \"tgz\":\n         opener, mode = tarfile.open, \"r:gz\"\n-    elif path.endswith(\".tar.xz\") or path.endswith(\".txz\") or extractor == \"txz\":\n+    elif extractor == \"txz\":\n         opener, mode = tarfile.open, \"r:xz\"\n-    elif path.endswith(\".tar\") or extractor == \"tar\":\n+    elif extractor == \"tar\":\n         opener, mode = tarfile.open, \"r:\"\n-    elif path.endswith(\".gz\") or extractor == \"gzip\":\n+    elif extractor == \"bz2\":\n+        opener, mode = tarfile.open, \"r:bz2\"\n+    elif extractor == \"gzip\":\n         decompress_gz(path, to_directory)\n         return\n-    elif path.endswith(\".tar.bz2\") or path.endswith(\".tbz\") or extractor == \"bz2\":\n-        opener, mode = tarfile.open, \"r:bz2\"\n-    elif is_7zip_supported(path, extractor):\n+    elif extractor is None or is_7zip_supported(path, extractor):\n         opener = \"7zip\"\n     else:\n         raise RuntimeError(\n-            \"Could not extract `%s` as no appropriate extractor is found\" % path\n+            \"Could not extract `%s` - unknown format specified\" % path\n         )\n+\n     temp_name = \".extract-\" + str(uuid.uuid4())[:8]\n     temp_path = temp_dir = os.path.join(to_directory, temp_name)\n     try:\n", "issue": "Lutris fails to open .tar.gz with \"not a gzip file\" if file is a tar file\nLutris fails to open archive where 7z doesn't. If I rename the file to `TTRLinux-v1.2.3.tar` Lutris can open and extract it. `format: tar` doesn't work here.\r\n\r\n```\r\nfiles:\r\n- client: https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz\r\ngame:\r\n  exe: Launcher\r\ninstaller:\r\n- extract:\r\n    file: client\r\n    format: tar\r\n```\r\n\r\n```\r\n2019-04-04 16:53:40,809: Downloading file 1 of 1\r\n2019-04-04 16:53:40,811: Downloading [client]: https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz to /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz\r\n2019-04-04 16:53:40,814: Downloading https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz to /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz\r\n2019-04-04 16:53:40,816: Starting download of:\r\n https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz\r\n2019-04-04 16:53:44,410: Finished downloading https://download.toontownrewritten.com/launcher/linux/TTRLinux-v1.2.3.tar.gz\r\n2019-04-04 16:53:44,822: Installer command: {'extract': {'format': 'tar', 'file': 'client'}}\r\n2019-04-04 16:53:44,823: Extracting TTRLinux-v1.2.3.tar.gz\r\n2019-04-04 16:53:44,824: extracting file /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz to /home/cxf/Games/toontown-rewritten\r\n2019-04-04 16:53:44,831: Extracting /home/cxf/.cache/lutris/installer/toontown-rewritten/client/TTRLinux-v1.2.3.tar.gz to /home/cxf/Games/toontown-rewritten\r\n2019-04-04 16:53:44,832: Extraction failed: not a gzip file\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 1643, in gzopen\r\n    t = cls.taropen(name, mode, fileobj, **kwargs)\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 1619, in taropen\r\n    return cls(name, mode, fileobj, **kwargs)\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 1482, in __init__\r\n    self.firstmember = self.next()\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 2297, in next\r\n    tarinfo = self.tarinfo.fromtarfile(self)\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 1092, in fromtarfile\r\n    buf = tarfile.fileobj.read(BLOCKSIZE)\r\n  File \"/usr/lib/python3.6/gzip.py\", line 276, in read\r\n    return self._buffer.read(size)\r\n  File \"/usr/lib/python3.6/_compression.py\", line 68, in readinto\r\n    data = self.read(len(byte_view))\r\n  File \"/usr/lib/python3.6/gzip.py\", line 463, in read\r\n    if not self._read_gzip_header():\r\n  File \"/usr/lib/python3.6/gzip.py\", line 411, in _read_gzip_header\r\n    raise OSError('Not a gzipped file (%r)' % magic)\r\nOSError: Not a gzipped file (b'To')\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nTraceback (most recent call last):\r\n  File \"/home/cxf/.lutris/lutris/util/extract.py\", line 90, in extract_archive\r\n    _do_extract(path, temp_path, opener, mode, extractor)\r\n  File \"/home/cxf/.lutris/lutris/util/extract.py\", line 134, in _do_extract\r\n    handler = opener(archive, mode)\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 1589, in open\r\n    return func(name, filemode, fileobj, **kwargs)\r\n  File \"/usr/lib/python3.6/tarfile.py\", line 1647, in gzopen\r\n    raise ReadError(\"not a gzip file\")\r\ntarfile.ReadError: not a gzip file\r\n2019-04-04 16:53:44,835: Error while completing task <bound method CommandsMixin.extract of <lutris.installer.interpreter.ScriptInterpreter object at 0x7f0507748c88>>: not a gzip file\r\n<class 'lutris.util.extract.ExtractFailure'> not a gzip file\r\n  File \"/home/cxf/.lutris/lutris/util/jobs.py\", line 30, in target\r\n    result = self.function(*args, **kwargs)\r\n  File \"/home/cxf/.lutris/lutris/installer/commands.py\", line 180, in extract\r\n    extract.extract_archive, filename, dest_path, merge_single, extractor\r\n  File \"/home/cxf/.lutris/lutris/installer/commands.py\", line 527, in _killable_process\r\n    result = result_obj.get()  # Wait process end & reraise exceptions\r\n  File \"/usr/lib/python3.6/multiprocessing/pool.py\", line 670, in get\r\n    raise self._value\r\n2019-04-04 16:53:49,289: Install cancelled\r\n```\r\n\r\n```\r\ncxf@PC:~/Downloads$ 7z e TTRLinux-v1.2.3.tar.gz \r\n\r\n7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21\r\np7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs Intel(R) Core(TM) i5-4460  CPU @ 3.20GHz (306C3),ASM,AES-NI)\r\n\r\nScanning the drive for archives:\r\n1 file, 37233152 bytes (36 MiB)\r\n\r\nExtracting archive: TTRLinux-v1.2.3.tar.gz\r\nWARNING:\r\nTTRLinux-v1.2.3.tar.gz\r\nCan not open the file as [gzip] archive\r\nThe file is open as [tar] archive\r\n\r\n--\r\nPath = TTRLinux-v1.2.3.tar.gz\r\nOpen WARNING: Can not open the file as [gzip] archive\r\nType = tar\r\nPhysical Size = 37233152\r\nHeaders Size = 2048\r\nCode Page = UTF-8\r\n\r\nEverything is Ok\r\n\r\nArchives with Warnings: 1\r\nFolders: 1\r\nFiles: 1\r\nSize:       37231062\r\nCompressed: 37233152\r\n```\r\n\n", "before_files": [{"content": "import os\nimport uuid\nimport shutil\nimport tarfile\nimport subprocess\nimport gzip\nimport zlib\nfrom lutris.util import system\nfrom lutris.util.log import logger\nfrom lutris import settings\n\n\nclass ExtractFailure(Exception):\n    \"\"\"Exception raised when and archive fails to extract\"\"\"\n\n\ndef is_7zip_supported(path, extractor):\n    supported_extractors = (\n        \"7z\",\n        \"xz\",\n        \"bzip2\",\n        \"gzip\",\n        \"tar\",\n        \"zip\",\n        \"ar\",\n        \"arj\",\n        \"cab\",\n        \"chm\",\n        \"cpio\",\n        \"cramfs\",\n        \"dmg\",\n        \"ext\",\n        \"fat\",\n        \"gpt\",\n        \"hfs\",\n        \"ihex\",\n        \"iso\",\n        \"lzh\",\n        \"lzma\",\n        \"mbr\",\n        \"msi\",\n        \"nsis\",\n        \"ntfs\",\n        \"qcow2\",\n        \"rar\",\n        \"rpm\",\n        \"squashfs\",\n        \"udf\",\n        \"uefi\",\n        \"vdi\",\n        \"vhd\",\n        \"vmdk\",\n        \"wim\",\n        \"xar\",\n        \"z\",\n    )\n    if extractor:\n        return extractor.lower() in supported_extractors\n    _base, ext = os.path.splitext(path)\n    if ext:\n        ext = ext.lstrip(\".\").lower()\n        return ext in supported_extractors\n\n\ndef extract_archive(path, to_directory=\".\", merge_single=True, extractor=None):\n    path = os.path.abspath(path)\n    mode = None\n    logger.debug(\"Extracting %s to %s\", path, to_directory)\n\n    if path.endswith(\".tar.gz\") or path.endswith(\".tgz\") or extractor == \"tgz\":\n        opener, mode = tarfile.open, \"r:gz\"\n    elif path.endswith(\".tar.xz\") or path.endswith(\".txz\") or extractor == \"txz\":\n        opener, mode = tarfile.open, \"r:xz\"\n    elif path.endswith(\".tar\") or extractor == \"tar\":\n        opener, mode = tarfile.open, \"r:\"\n    elif path.endswith(\".gz\") or extractor == \"gzip\":\n        decompress_gz(path, to_directory)\n        return\n    elif path.endswith(\".tar.bz2\") or path.endswith(\".tbz\") or extractor == \"bz2\":\n        opener, mode = tarfile.open, \"r:bz2\"\n    elif is_7zip_supported(path, extractor):\n        opener = \"7zip\"\n    else:\n        raise RuntimeError(\n            \"Could not extract `%s` as no appropriate extractor is found\" % path\n        )\n    temp_name = \".extract-\" + str(uuid.uuid4())[:8]\n    temp_path = temp_dir = os.path.join(to_directory, temp_name)\n    try:\n        _do_extract(path, temp_path, opener, mode, extractor)\n    except (OSError, zlib.error, tarfile.ReadError) as ex:\n        logger.exception(\"Extraction failed: %s\", ex)\n        raise ExtractFailure(str(ex))\n    if merge_single:\n        extracted = os.listdir(temp_path)\n        if len(extracted) == 1:\n            temp_path = os.path.join(temp_path, extracted[0])\n\n    if os.path.isfile(temp_path):\n        destination_path = os.path.join(to_directory, extracted[0])\n        if os.path.isfile(destination_path):\n            logger.warning(\"Overwrite existing file %s\", destination_path)\n            os.remove(destination_path)\n        shutil.move(temp_path, to_directory)\n        os.removedirs(temp_dir)\n    else:\n        for archive_file in os.listdir(temp_path):\n            source_path = os.path.join(temp_path, archive_file)\n            destination_path = os.path.join(to_directory, archive_file)\n            # logger.debug(\"Moving extracted files from %s to %s\", source_path, destination_path)\n\n            if system.path_exists(destination_path):\n                logger.warning(\"Overwrite existing path %s\", destination_path)\n                if os.path.isfile(destination_path):\n                    os.remove(destination_path)\n                    shutil.move(source_path, destination_path)\n                elif os.path.isdir(destination_path):\n                    try:\n                        system.merge_folders(source_path, destination_path)\n                    except OSError as ex:\n                        logger.error(\"Failed to merge to destination %s: %s\", destination_path, ex)\n                        raise ExtractFailure(str(ex))\n            else:\n                shutil.move(source_path, destination_path)\n        system.remove_folder(temp_dir)\n    logger.debug(\"Finished extracting %s to %s\", path, to_directory)\n    return path, to_directory\n\n\ndef _do_extract(archive, dest, opener, mode=None, extractor=None):\n    if opener == \"7zip\":\n        extract_7zip(archive, dest, archive_type=extractor)\n    else:\n        handler = opener(archive, mode)\n        handler.extractall(dest)\n        handler.close()\n\n\ndef decompress_gz(file_path, dest_path=None):\n    \"\"\"Decompress a gzip file.\"\"\"\n    if dest_path:\n        dest_filename = os.path.join(dest_path, os.path.basename(file_path[:-3]))\n    else:\n        dest_filename = file_path[:-3]\n\n    gzipped_file = gzip.open(file_path, \"rb\")\n    file_content = gzipped_file.read()\n    gzipped_file.close()\n\n    dest_file = open(dest_filename, \"wb\")\n    dest_file.write(file_content)\n    dest_file.close()\n\n    return dest_path\n\n\ndef extract_7zip(path, dest, archive_type=None):\n    _7zip_path = os.path.join(settings.RUNTIME_DIR, \"p7zip/7z\")\n    if not system.path_exists(_7zip_path):\n        _7zip_path = system.find_executable(\"7z\")\n    if not system.path_exists(_7zip_path):\n        raise OSError(\"7zip is not found in the lutris runtime or on the system\")\n    command = [_7zip_path, \"x\", path, \"-o{}\".format(dest), \"-aoa\"]\n    if archive_type:\n        command.append(\"-t{}\".format(archive_type))\n    subprocess.call(command)\n", "path": "lutris/util/extract.py"}], "after_files": [{"content": "import os\nimport uuid\nimport shutil\nimport tarfile\nimport subprocess\nimport gzip\nimport zlib\nfrom lutris.util import system\nfrom lutris.util.log import logger\nfrom lutris import settings\n\n\nclass ExtractFailure(Exception):\n    \"\"\"Exception raised when and archive fails to extract\"\"\"\n\n\ndef is_7zip_supported(path, extractor):\n    supported_extractors = (\n        \"7z\",\n        \"xz\",\n        \"bzip2\",\n        \"gzip\",\n        \"tar\",\n        \"zip\",\n        \"ar\",\n        \"arj\",\n        \"cab\",\n        \"chm\",\n        \"cpio\",\n        \"cramfs\",\n        \"dmg\",\n        \"ext\",\n        \"fat\",\n        \"gpt\",\n        \"hfs\",\n        \"ihex\",\n        \"iso\",\n        \"lzh\",\n        \"lzma\",\n        \"mbr\",\n        \"msi\",\n        \"nsis\",\n        \"ntfs\",\n        \"qcow2\",\n        \"rar\",\n        \"rpm\",\n        \"squashfs\",\n        \"udf\",\n        \"uefi\",\n        \"vdi\",\n        \"vhd\",\n        \"vmdk\",\n        \"wim\",\n        \"xar\",\n        \"z\",\n    )\n    if extractor:\n        return extractor.lower() in supported_extractors\n    _base, ext = os.path.splitext(path)\n    if ext:\n        ext = ext.lstrip(\".\").lower()\n        return ext in supported_extractors\n\n\ndef extract_archive(path, to_directory=\".\", merge_single=True, extractor=None):\n    path = os.path.abspath(path)\n    mode = None\n    logger.debug(\"Extracting %s to %s\", path, to_directory)\n\n    if extractor is None:\n        if path.endswith(\".tar.gz\") or path.endswith(\".tgz\"):\n            extractor = \"tgz\"\n        elif path.endswith(\".tar.xz\") or path.endswith(\".txz\"):\n            extractor = \"txz\"\n        elif path.endswith(\".tar\"):\n            extractor = \"tar\"\n        elif path.endswith(\".tar.bz2\") or path.endswith(\".tbz\"):\n            extractor = \"bz2\"\n        elif path.endswith(\".gz\"):\n            extractor = \"gzip\"\n        elif is_7zip_supported(path, None):\n            extractor = None\n        else:\n            raise RuntimeError(\n                \"Could not extract `%s` - no appropriate extractor found\" % path\n            )\n\n    if extractor == \"tgz\":\n        opener, mode = tarfile.open, \"r:gz\"\n    elif extractor == \"txz\":\n        opener, mode = tarfile.open, \"r:xz\"\n    elif extractor == \"tar\":\n        opener, mode = tarfile.open, \"r:\"\n    elif extractor == \"bz2\":\n        opener, mode = tarfile.open, \"r:bz2\"\n    elif extractor == \"gzip\":\n        decompress_gz(path, to_directory)\n        return\n    elif extractor is None or is_7zip_supported(path, extractor):\n        opener = \"7zip\"\n    else:\n        raise RuntimeError(\n            \"Could not extract `%s` - unknown format specified\" % path\n        )\n\n    temp_name = \".extract-\" + str(uuid.uuid4())[:8]\n    temp_path = temp_dir = os.path.join(to_directory, temp_name)\n    try:\n        _do_extract(path, temp_path, opener, mode, extractor)\n    except (OSError, zlib.error, tarfile.ReadError) as ex:\n        logger.exception(\"Extraction failed: %s\", ex)\n        raise ExtractFailure(str(ex))\n    if merge_single:\n        extracted = os.listdir(temp_path)\n        if len(extracted) == 1:\n            temp_path = os.path.join(temp_path, extracted[0])\n\n    if os.path.isfile(temp_path):\n        destination_path = os.path.join(to_directory, extracted[0])\n        if os.path.isfile(destination_path):\n            logger.warning(\"Overwrite existing file %s\", destination_path)\n            os.remove(destination_path)\n        shutil.move(temp_path, to_directory)\n        os.removedirs(temp_dir)\n    else:\n        for archive_file in os.listdir(temp_path):\n            source_path = os.path.join(temp_path, archive_file)\n            destination_path = os.path.join(to_directory, archive_file)\n            # logger.debug(\"Moving extracted files from %s to %s\", source_path, destination_path)\n\n            if system.path_exists(destination_path):\n                logger.warning(\"Overwrite existing path %s\", destination_path)\n                if os.path.isfile(destination_path):\n                    os.remove(destination_path)\n                    shutil.move(source_path, destination_path)\n                elif os.path.isdir(destination_path):\n                    try:\n                        system.merge_folders(source_path, destination_path)\n                    except OSError as ex:\n                        logger.error(\"Failed to merge to destination %s: %s\", destination_path, ex)\n                        raise ExtractFailure(str(ex))\n            else:\n                shutil.move(source_path, destination_path)\n        system.remove_folder(temp_dir)\n    logger.debug(\"Finished extracting %s to %s\", path, to_directory)\n    return path, to_directory\n\n\ndef _do_extract(archive, dest, opener, mode=None, extractor=None):\n    if opener == \"7zip\":\n        extract_7zip(archive, dest, archive_type=extractor)\n    else:\n        handler = opener(archive, mode)\n        handler.extractall(dest)\n        handler.close()\n\n\ndef decompress_gz(file_path, dest_path=None):\n    \"\"\"Decompress a gzip file.\"\"\"\n    if dest_path:\n        dest_filename = os.path.join(dest_path, os.path.basename(file_path[:-3]))\n    else:\n        dest_filename = file_path[:-3]\n\n    gzipped_file = gzip.open(file_path, \"rb\")\n    file_content = gzipped_file.read()\n    gzipped_file.close()\n\n    dest_file = open(dest_filename, \"wb\")\n    dest_file.write(file_content)\n    dest_file.close()\n\n    return dest_path\n\n\ndef extract_7zip(path, dest, archive_type=None):\n    _7zip_path = os.path.join(settings.RUNTIME_DIR, \"p7zip/7z\")\n    if not system.path_exists(_7zip_path):\n        _7zip_path = system.find_executable(\"7z\")\n    if not system.path_exists(_7zip_path):\n        raise OSError(\"7zip is not found in the lutris runtime or on the system\")\n    command = [_7zip_path, \"x\", path, \"-o{}\".format(dest), \"-aoa\"]\n    if archive_type:\n        command.append(\"-t{}\".format(archive_type))\n    subprocess.call(command)\n", "path": "lutris/util/extract.py"}]}