browser: open last download with external commands
This commit is contained in:
parent
f1c5d8dfc9
commit
3a88a898c9
13
BOARD.txt
13
BOARD.txt
|
@ -4,7 +4,7 @@ TODO DONE
|
||||||
links
|
links
|
||||||
redirections
|
redirections
|
||||||
web links
|
web links
|
||||||
history (back/forward)
|
history (back)
|
||||||
simple caching
|
simple caching
|
||||||
simple text files
|
simple text files
|
||||||
encodings
|
encodings
|
||||||
|
@ -14,21 +14,26 @@ TODO DONE
|
||||||
configuration
|
configuration
|
||||||
help page
|
help page
|
||||||
TOFU
|
TOFU
|
||||||
open last download
|
view history
|
||||||
|
open last download
|
||||||
|
media files
|
||||||
home page
|
home page
|
||||||
media files
|
|
||||||
view history
|
|
||||||
identity management
|
identity management
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
BACKLOG
|
BACKLOG
|
||||||
click on links to open them
|
click on links to open them
|
||||||
download to disk, not in memory
|
download to disk, not in memory
|
||||||
|
download in the background
|
||||||
|
download view instead of last download
|
||||||
does encoding really work? cf. egsam
|
does encoding really work? cf. egsam
|
||||||
margins / centering
|
margins / centering
|
||||||
pre blocks folding
|
pre blocks folding
|
||||||
buffers (tabs)
|
buffers (tabs)
|
||||||
|
a11y? tts?
|
||||||
handle soft-hyphens on wrapping
|
handle soft-hyphens on wrapping
|
||||||
bug: combining chars reduce lengths
|
bug: combining chars reduce lengths
|
||||||
non shit command-line
|
non shit command-line
|
||||||
response code 11 (if still there)
|
response code 11 (if still there)
|
||||||
gopher?
|
gopher?
|
||||||
|
save history
|
||||||
|
history (forward) (useful?)
|
||||||
|
|
22
README.md
22
README.md
|
@ -57,7 +57,7 @@ It also provide these features:
|
||||||
|
|
||||||
- History
|
- History
|
||||||
- Caching
|
- Caching
|
||||||
- Bookmarks: it's just a text file with bindings.
|
- Bookmarks (it's just a text file with bindings)
|
||||||
- Downloads
|
- Downloads
|
||||||
|
|
||||||
Check out [this board](BOARD.txt) for what's done and coming next.
|
Check out [this board](BOARD.txt) for what's done and coming next.
|
||||||
|
@ -73,12 +73,14 @@ you want, just restart Bebop to take changes into account.
|
||||||
|
|
||||||
Here are the available options:
|
Here are the available options:
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
| Key | Type | Default | Description |
|
||||||
|-------------------|-------------|----------|---------------------------------------|
|
|----------------------------|--------------|----------------|---------------------------------------|
|
||||||
| `connect_timeout` | int | 10 | Seconds before connection times out. |
|
| `connect_timeout` | int | 10 | Seconds before connection times out. |
|
||||||
| `text_width` | int | 80 | Rendered line length. |
|
| `text_width` | int | 80 | Rendered line length. |
|
||||||
| `source_editor` | string list | `["vi"]` | Command to use for editing sources. |
|
| `source_editor` | string list | `["vi"]` | Command to use for editing sources. |
|
||||||
| `command_editor` | string list | `["vi"]` | Command to use for editing CLI input. |
|
| `command_editor` | string list | `["vi"]` | Command to use for editing CLI input. |
|
||||||
|
| `external_commands` | (see note 2) | {} | Commands to open various files. |
|
||||||
|
| `external_command_default` | string list | `["xdg-open"]` | Default command to open files. |
|
||||||
|
|
||||||
Note: for the "command" parameters such as `source_editor` and `command_editor`,
|
Note: for the "command" parameters such as `source_editor` and `command_editor`,
|
||||||
a string list is used to separate the different program arguments, e.g. if you
|
a string list is used to separate the different program arguments, e.g. if you
|
||||||
|
@ -86,6 +88,12 @@ wish to use `vim -c 'startinsert'`, you should write the list `["vim", "-c",
|
||||||
"startinsert"]`. In both case, a temporary or regular file name will be appended
|
"startinsert"]`. In both case, a temporary or regular file name will be appended
|
||||||
to this command when run.
|
to this command when run.
|
||||||
|
|
||||||
|
2: the `external_commands` dict maps MIME types to commands just as above. For
|
||||||
|
example, if you want to open video files with VLC and audio files in Clementine,
|
||||||
|
you can use the following dict: `{"audio": ["clementine"], "video", ["vlc"]}`.
|
||||||
|
For now only "main" MIME types are supported, i.e. you cannot specify precise
|
||||||
|
types like "audio/flac", just "audio".
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FAQ
|
FAQ
|
||||||
|
|
|
@ -4,8 +4,11 @@ import curses
|
||||||
import curses.ascii
|
import curses.ascii
|
||||||
import curses.textpad
|
import curses.textpad
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from math import inf
|
from math import inf
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from bebop.bookmarks import (
|
from bebop.bookmarks import (
|
||||||
get_bookmarks_path, get_bookmarks_document, save_bookmark
|
get_bookmarks_path, get_bookmarks_document, save_bookmark
|
||||||
|
@ -16,6 +19,7 @@ from bebop.external import open_external_program
|
||||||
from bebop.help import HELP_PAGE
|
from bebop.help import HELP_PAGE
|
||||||
from bebop.history import History
|
from bebop.history import History
|
||||||
from bebop.links import Links
|
from bebop.links import Links
|
||||||
|
from bebop.mime import MimeType
|
||||||
from bebop.mouse import ButtonState
|
from bebop.mouse import ButtonState
|
||||||
from bebop.navigation import (
|
from bebop.navigation import (
|
||||||
get_parent_url, get_root_url, join_url, parse_url, unparse_url
|
get_parent_url, get_root_url, join_url, parse_url, unparse_url
|
||||||
|
@ -44,6 +48,7 @@ class Browser:
|
||||||
values are dicts as well: the "open" key maps to a callable to use when
|
values are dicts as well: the "open" key maps to a callable to use when
|
||||||
the page is accessed, and the optional "source" key maps to callable
|
the page is accessed, and the optional "source" key maps to callable
|
||||||
returning the page source path.
|
returning the page source path.
|
||||||
|
- last_download: tuple of MimeType and path, or None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config, cert_stash):
|
def __init__(self, config, cert_stash):
|
||||||
|
@ -59,6 +64,7 @@ class Browser:
|
||||||
self.history = History(self.config["history_limit"])
|
self.history = History(self.config["history_limit"])
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.special_pages = self.setup_special_pages()
|
self.special_pages = self.setup_special_pages()
|
||||||
|
self.last_download: Optional[Tuple[MimeType, Path]] = None
|
||||||
self._current_url = ""
|
self._current_url = ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -168,6 +174,8 @@ class Browser:
|
||||||
self.scroll_page_vertically(inf)
|
self.scroll_page_vertically(inf)
|
||||||
elif char == ord("o"):
|
elif char == ord("o"):
|
||||||
self.quick_command("open")
|
self.quick_command("open")
|
||||||
|
elif char == ord("O"):
|
||||||
|
self.open_last_download()
|
||||||
elif char == ord("p"):
|
elif char == ord("p"):
|
||||||
self.go_back()
|
self.go_back()
|
||||||
elif char == ord("u"):
|
elif char == ord("u"):
|
||||||
|
@ -574,3 +582,23 @@ class Browser:
|
||||||
def open_history(self):
|
def open_history(self):
|
||||||
"""Show a generated history of visited pages."""
|
"""Show a generated history of visited pages."""
|
||||||
self.open_internal_page("history", self.history.to_gemtext())
|
self.open_internal_page("history", self.history.to_gemtext())
|
||||||
|
|
||||||
|
def open_last_download(self):
|
||||||
|
"""Open the last downloaded file."""
|
||||||
|
if not self.last_download:
|
||||||
|
return
|
||||||
|
mime_type, path = self.last_download
|
||||||
|
command = self.config["external_commands"].get(mime_type.main_type)
|
||||||
|
if not command:
|
||||||
|
command = self.config["external_command_default"]
|
||||||
|
command = command + [str(path)]
|
||||||
|
self.set_status(f"Running '{' '.join(command)}'...")
|
||||||
|
try:
|
||||||
|
subprocess.Popen(
|
||||||
|
command,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
start_new_session=True
|
||||||
|
)
|
||||||
|
except FileNotFoundError as exc:
|
||||||
|
self.set_status_error(f"Failed to run command: {exc}")
|
||||||
|
|
|
@ -193,6 +193,7 @@ def _handle_successful_response(browser: Browser, response: Response, url: str):
|
||||||
browser.set_status_error(f"Failed to save {url} ({exc})")
|
browser.set_status_error(f"Failed to save {url} ({exc})")
|
||||||
else:
|
else:
|
||||||
browser.set_status(f"Downloaded {url} ({mime_type.short}).")
|
browser.set_status(f"Downloaded {url} ({mime_type.short}).")
|
||||||
|
browser.last_download = mime_type, filepath
|
||||||
return True
|
return True
|
||||||
elif error:
|
elif error:
|
||||||
browser.set_status_error(error)
|
browser.set_status_error(error)
|
||||||
|
|
|
@ -10,6 +10,8 @@ DEFAULT_CONFIG = {
|
||||||
"source_editor": ["vi"],
|
"source_editor": ["vi"],
|
||||||
"command_editor": ["vi"],
|
"command_editor": ["vi"],
|
||||||
"history_limit": 1000,
|
"history_limit": 1000,
|
||||||
|
"external_commands": {},
|
||||||
|
"external_command_default": ["xdg-open"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ name, not the symbol itself.
|
||||||
- gg: go to the top of the page
|
- gg: go to the top of the page
|
||||||
- G: go to the bottom of the page
|
- G: go to the bottom of the page
|
||||||
- o: open an URL
|
- o: open an URL
|
||||||
|
- O: open last download with an external command
|
||||||
- p: go to the previous page
|
- p: go to the previous page
|
||||||
- u: go to the parent page (up a level in URL)
|
- u: go to the parent page (up a level in URL)
|
||||||
- U: go to the root page (root URL for the current domain)
|
- U: go to the root page (root URL for the current domain)
|
||||||
|
|
|
@ -6,7 +6,6 @@ turned into a basic re-implementation of the RFC.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from ssl import RAND_pseudo_bytes
|
|
||||||
from typing import Any, Dict, Optional
|
from typing import Any, Dict, Optional
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ class TestNavigation(unittest.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
remove_dot_segments(path),
|
remove_dot_segments(path),
|
||||||
expected,
|
expected,
|
||||||
msg=f"path was " + path
|
msg="path was " + path
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_remove_last_segment(self):
|
def test_remove_last_segment(self):
|
||||||
|
@ -123,7 +123,7 @@ class TestNavigation(unittest.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
get_parent_url(url),
|
get_parent_url(url),
|
||||||
parent,
|
parent,
|
||||||
msg=f"URL was " + url)
|
msg="URL was " + url)
|
||||||
|
|
||||||
def test_get_root_url(self):
|
def test_get_root_url(self):
|
||||||
urls_and_roots = [
|
urls_and_roots = [
|
||||||
|
@ -140,5 +140,5 @@ class TestNavigation(unittest.TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
get_root_url(url),
|
get_root_url(url),
|
||||||
root,
|
root,
|
||||||
msg=f"URL was " + url
|
msg="URL was " + url
|
||||||
)
|
)
|
||||||
|
|
Reference in a new issue