Improving MIME type detection for some common file types when they are missing from the system MIME database

Also allow the user to specify their own database via mime.types in the
kitty config directory. See #3056
This commit is contained in:
Kovid Goyal 2020-10-25 13:42:11 +05:30
parent e160cbf32b
commit 75a94bcd96
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 62 additions and 13 deletions

View File

@ -24,6 +24,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- Fix selections created by dragging upwards not being auto-cleared when
screen contents change (:pull:`3028`)
- Allow adding MIME definitions to kitty by placing a ``mime.types`` file in
the kitty config directory (:iss:`3056`)
0.19.1 [2020-10-06]
-------------------

View File

@ -81,7 +81,11 @@ lines. The various available criteria are:
``mime``
A comma separated list of MIME types, for example: ``text/*, image/*,
application/pdf``
application/pdf``. You can add MIME types to kitty by creating the
:file:`mime.types` in the kitty configuration directory. Useful if your
system MIME database does not have definitions you need. This file is
in the standard format of one definition per line, like: ``text/plain rst
md``.
``ext``
A comma separated list of file extensions, for example: ``jpeg, tar.gz``

View File

@ -7,7 +7,7 @@ import re
from contextlib import suppress
from functools import lru_cache
from hashlib import md5
from mimetypes import guess_type
from kitty.guess_mime_type import guess_type
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union
if TYPE_CHECKING:
@ -136,7 +136,7 @@ def sanitize(text: str) -> str:
@lru_cache(maxsize=1024)
def mime_type_for_path(path: str) -> str:
return guess_type(path)[0] or 'application/octet-stream'
return guess_type(path) or 'application/octet-stream'
@lru_cache(maxsize=1024)

View File

@ -3,7 +3,6 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import contextlib
import mimetypes
import os
import re
import socket
@ -18,6 +17,7 @@ from typing import (
Dict, Generator, List, NamedTuple, Optional, Pattern, Tuple, Union
)
from kitty.guess_mime_type import guess_type
from kitty.cli import parse_args
from kitty.cli_stub import IcatCLIOptions
from kitty.constants import appname
@ -273,7 +273,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
def scan(d: str) -> Generator[Tuple[str, str], None, None]:
for dirpath, dirnames, filenames in os.walk(d):
for f in filenames:
mt = mimetypes.guess_type(f)[0]
mt = guess_type(f)
if mt and mt.startswith('image/'):
yield os.path.join(dirpath, f), mt

View File

@ -394,10 +394,10 @@ def complete_files_and_dirs(
def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None:
from mimetypes import guess_type
from .guess_mime_type import guess_type
def icat_file_predicate(filename: str) -> bool:
mt = guess_type(filename)[0]
mt = guess_type(filename)
if mt and mt.startswith('image/'):
return True
return False

45
kitty/guess_mime_type.py Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import os
from contextlib import suppress
from typing import Optional
known_extensions = {
'asciidoc': 'text/asciidoctor',
'conf': 'text/config',
'md': 'text/markdown',
'pyj': 'text/rapydscript-ng',
'recipe': 'text/python',
'rst': 'text/restructured-text',
'toml': 'text/toml',
'vim': 'text/vim',
'yaml': 'text/yaml',
}
def is_rc_file(path: str) -> bool:
name = os.path.basename(path)
return '.' not in name and name.endswith('rc')
def guess_type(path: str) -> Optional[str]:
if not hasattr(guess_type, 'inited'):
setattr(guess_type, 'inited', True)
from mimetypes import init
init(None)
from kitty.constants import config_dir
local_defs = os.path.join(config_dir, 'mime.types')
if os.path.exists(local_defs):
init((local_defs,))
from mimetypes import guess_type as stdlib_guess_type
mt = None
with suppress(Exception):
mt = stdlib_guess_type(path)[0]
if not mt:
ext = path.rpartition('.')[-1].lower()
mt = known_extensions.get(ext)
if not mt and is_rc_file(path):
mt = 'text/plain'
return mt

View File

@ -17,6 +17,7 @@ from .config import KeyAction, parse_key_action
from .constants import config_dir
from .typing import MatchType
from .utils import expandvars, log_error
from .guess_mime_type import guess_type
class MatchCriteria(NamedTuple):
@ -75,12 +76,8 @@ def url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc:
if mc.type == 'mime':
import fnmatch
from mimetypes import guess_type
try:
mt = guess_type(unquoted_path)[0]
except Exception:
return False
if mt is None:
mt = guess_type(unquoted_path)
if not mt:
return False
mt = mt.lower()
for mpat in mc.value.split(','):