File picker (#136)

* File picker

* Add docstring to show in help

* Update doc

* Fix update_file: invalid get msg

* Catch ValueError when removing messages

* Ask to whether to upload file as document or compressed

* Clear status after sending file
This commit is contained in:
Nameless 2020-07-15 18:50:35 +08:00 committed by GitHub
parent 39a1e59f35
commit b37bba8fb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 89 additions and 38 deletions

View file

@ -75,6 +75,7 @@ yay -S telegram-tg-git
```ini
image/webp; mpv %s
```
- [ranger](https://github.com/ranger/ranger), [nnn](https://github.com/jarun/nnn) - can be used to choose file when sending, customizable with `FILE_PICKER_CMD`
## Configuration
@ -165,6 +166,9 @@ USERS_COLORS = tuple(range(2, 16))
# cleanup cache
# Values: N days, None (never)
KEEP_MEDIA = 7
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
# FILE_PICKER_CMD = "nnn -p {file_path}"
```
### Mailcap file

View file

@ -71,6 +71,8 @@ USERS_COLORS = tuple(range(2, 16))
KEEP_MEDIA = 7
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
if os.path.isfile(CONFIG_FILE):
config_params = runpy.run_path(CONFIG_FILE)

View file

@ -15,6 +15,7 @@ from tg.msg import MsgProxy
from tg.tdlib import ChatAction, Tdlib
from tg.utils import (
get_duration,
get_mime,
get_video_resolution,
get_waveform,
is_yes,
@ -299,18 +300,6 @@ class Controller:
)
self.present_info("Message wasn't sent")
@bind(msg_handler, ["sv"])
def send_video(self) -> None:
file_path = self.view.status.get_input()
if not file_path or not os.path.isfile(file_path):
return
chat_id = self.model.chats.id_by_index(self.model.current_chat)
if not chat_id:
return
width, height = get_video_resolution(file_path)
duration = get_duration(file_path)
self.tg.send_video(file_path, chat_id, width, height, duration)
@bind(msg_handler, ["dd"])
def delete_msgs(self) -> None:
is_deleted = self.model.delete_msgs()
@ -320,18 +309,70 @@ class Controller:
return
self.present_info("Message deleted")
@bind(msg_handler, ["S"])
def choose_and_send_file(self) -> None:
"""Call file picker and send chosen file based on mimetype"""
chat_id = self.model.chats.id_by_index(self.model.current_chat)
file_path = None
if not chat_id:
return self.present_error("No chat selected")
try:
with NamedTemporaryFile("w") as f, suspend(self.view) as s:
s.call(config.FILE_PICKER_CMD.format(file_path=f.name))
with open(f.name) as f:
file_path = f.read().strip()
except FileNotFoundError:
pass
if not file_path or not os.path.isfile(file_path):
return self.present_error("No file was selected")
mime_map = {
"image": self.tg.send_photo,
"audio": self.tg.send_audio,
"video": self._send_video,
}
mime = get_mime(file_path)
if mime in ("image", "video"):
resp = self.view.status.get_input(
f"Upload <{file_path}> compressed?[Y/n]"
)
self.render_status()
if not is_yes(resp):
mime = ""
fun = mime_map.get(mime, self.tg.send_doc)
fun(file_path, chat_id)
@bind(msg_handler, ["sd"])
def send_document(self) -> None:
"""Enter file path and send uncompressed"""
self.send_file(self.tg.send_doc)
@bind(msg_handler, ["sp"])
def send_picture(self) -> None:
"""Enter file path and send compressed image"""
self.send_file(self.tg.send_photo)
@bind(msg_handler, ["sa"])
def send_audio(self) -> None:
"""Enter file path and send as audio"""
self.send_file(self.tg.send_audio)
@bind(msg_handler, ["sv"])
def send_video(self) -> None:
"""Enter file path and send compressed video"""
file_path = self.view.status.get_input()
if not file_path or not os.path.isfile(file_path):
return
chat_id = self.model.chats.id_by_index(self.model.current_chat)
if not chat_id:
return
self._send_video(file_path, chat_id)
def _send_video(self, file_path: str, chat_id: int) -> None:
width, height = get_video_resolution(file_path)
duration = get_duration(file_path)
self.tg.send_video(file_path, chat_id, width, height, duration)
def send_file(
self, send_file_fun: Callable[[str, int], AsyncResult],
) -> None:
@ -627,8 +668,14 @@ class Controller:
self.queue.put(self._render)
def _render(self) -> None:
self.render_chats()
self.render_msgs()
self._render_chats()
self._render_msgs()
self._render_status()
def render_status(self) -> None:
self.queue.put(self._render_status)
def _render_status(self) -> None:
self.view.status.draw()
def render_chats(self) -> None:

View file

@ -362,7 +362,10 @@ class MsgModel:
def remove_messages(self, chat_id: int, msg_ids: List[int]) -> None:
log.info(f"removing msg {msg_ids=}")
for msg_id in msg_ids:
try:
self.msg_ids[chat_id].remove(msg_id)
except ValueError:
pass
self.msgs[chat_id].pop(msg_id, None)
def add_message(self, chat_id: int, msg: Dict[str, Any]) -> None:

View file

@ -229,20 +229,19 @@ def update_file(controller: Controller, update: Dict[str, Any]) -> None:
file_id = update["file"]["id"]
local = update["file"]["local"]
chat_id, msg_id = controller.model.downloads.get(file_id, (None, None))
if chat_id is None:
if chat_id is None or msg_id is None:
log.warning(
"Can't find information about file with file_id=%s", file_id
)
return
msgs = controller.model.msgs.msgs[chat_id]
for msg_id, msg in msgs.items():
if msg["id"] == msg_id:
msg = controller.model.msgs.msgs[chat_id].get(msg_id)
if not msg:
return
proxy = MsgProxy(msg)
proxy.local = local
controller.render_msgs()
if proxy.is_downloaded:
controller.model.downloads.pop(file_id)
break
@update_handler("updateMessageContentOpened")

View file

@ -72,6 +72,13 @@ def setup_log() -> None:
logging.captureWarnings(True)
def get_mime(file_path: str) -> str:
mtype, _ = mimetypes.guess_type(file_path)
if not mtype:
return ""
return mtype.split("/")[0]
def get_file_handler(file_path: str) -> str:
mtype, _ = mimetypes.guess_type(file_path)
if not mtype:

View file

@ -5,16 +5,7 @@ from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple, Union, cast
from tg import config
from tg.colors import (
blue,
bold,
cyan,
get_color,
magenta,
reverse,
white,
yellow,
)
from tg.colors import bold, cyan, get_color, magenta, reverse, white, yellow
from tg.models import Model, UserModel
from tg.msg import MsgProxy
from tg.tdlib import ChatType, get_chat_type
@ -98,10 +89,8 @@ class StatusView:
self.win.resize(self.h, self.w)
self.win.mvwin(self.y, self.x)
def draw(self, msg: Optional[str] = None) -> None:
def draw(self, msg: str = "") -> None:
self.win.clear()
if not msg:
return
self.win.addstr(0, 0, msg.replace("\n", " ")[: self.w])
self._refresh()