mirror of
https://github.com/paul-nameless/tg
synced 2024-11-22 03:43:19 +00:00
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:
parent
39a1e59f35
commit
b37bba8fb7
7 changed files with 89 additions and 38 deletions
|
@ -75,6 +75,7 @@ yay -S telegram-tg-git
|
||||||
```ini
|
```ini
|
||||||
image/webp; mpv %s
|
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
|
## Configuration
|
||||||
|
@ -165,6 +166,9 @@ USERS_COLORS = tuple(range(2, 16))
|
||||||
# cleanup cache
|
# cleanup cache
|
||||||
# Values: N days, None (never)
|
# Values: N days, None (never)
|
||||||
KEEP_MEDIA = 7
|
KEEP_MEDIA = 7
|
||||||
|
|
||||||
|
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
|
||||||
|
# FILE_PICKER_CMD = "nnn -p {file_path}"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Mailcap file
|
### Mailcap file
|
||||||
|
|
|
@ -71,6 +71,8 @@ USERS_COLORS = tuple(range(2, 16))
|
||||||
|
|
||||||
KEEP_MEDIA = 7
|
KEEP_MEDIA = 7
|
||||||
|
|
||||||
|
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
|
||||||
|
|
||||||
|
|
||||||
if os.path.isfile(CONFIG_FILE):
|
if os.path.isfile(CONFIG_FILE):
|
||||||
config_params = runpy.run_path(CONFIG_FILE)
|
config_params = runpy.run_path(CONFIG_FILE)
|
||||||
|
|
|
@ -15,6 +15,7 @@ from tg.msg import MsgProxy
|
||||||
from tg.tdlib import ChatAction, Tdlib
|
from tg.tdlib import ChatAction, Tdlib
|
||||||
from tg.utils import (
|
from tg.utils import (
|
||||||
get_duration,
|
get_duration,
|
||||||
|
get_mime,
|
||||||
get_video_resolution,
|
get_video_resolution,
|
||||||
get_waveform,
|
get_waveform,
|
||||||
is_yes,
|
is_yes,
|
||||||
|
@ -299,18 +300,6 @@ class Controller:
|
||||||
)
|
)
|
||||||
self.present_info("Message wasn't sent")
|
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"])
|
@bind(msg_handler, ["dd"])
|
||||||
def delete_msgs(self) -> None:
|
def delete_msgs(self) -> None:
|
||||||
is_deleted = self.model.delete_msgs()
|
is_deleted = self.model.delete_msgs()
|
||||||
|
@ -320,18 +309,70 @@ class Controller:
|
||||||
return
|
return
|
||||||
self.present_info("Message deleted")
|
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"])
|
@bind(msg_handler, ["sd"])
|
||||||
def send_document(self) -> None:
|
def send_document(self) -> None:
|
||||||
|
"""Enter file path and send uncompressed"""
|
||||||
self.send_file(self.tg.send_doc)
|
self.send_file(self.tg.send_doc)
|
||||||
|
|
||||||
@bind(msg_handler, ["sp"])
|
@bind(msg_handler, ["sp"])
|
||||||
def send_picture(self) -> None:
|
def send_picture(self) -> None:
|
||||||
|
"""Enter file path and send compressed image"""
|
||||||
self.send_file(self.tg.send_photo)
|
self.send_file(self.tg.send_photo)
|
||||||
|
|
||||||
@bind(msg_handler, ["sa"])
|
@bind(msg_handler, ["sa"])
|
||||||
def send_audio(self) -> None:
|
def send_audio(self) -> None:
|
||||||
|
"""Enter file path and send as audio"""
|
||||||
self.send_file(self.tg.send_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(
|
def send_file(
|
||||||
self, send_file_fun: Callable[[str, int], AsyncResult],
|
self, send_file_fun: Callable[[str, int], AsyncResult],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -627,8 +668,14 @@ class Controller:
|
||||||
self.queue.put(self._render)
|
self.queue.put(self._render)
|
||||||
|
|
||||||
def _render(self) -> None:
|
def _render(self) -> None:
|
||||||
self.render_chats()
|
self._render_chats()
|
||||||
self.render_msgs()
|
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()
|
self.view.status.draw()
|
||||||
|
|
||||||
def render_chats(self) -> None:
|
def render_chats(self) -> None:
|
||||||
|
|
|
@ -362,7 +362,10 @@ class MsgModel:
|
||||||
def remove_messages(self, chat_id: int, msg_ids: List[int]) -> None:
|
def remove_messages(self, chat_id: int, msg_ids: List[int]) -> None:
|
||||||
log.info(f"removing msg {msg_ids=}")
|
log.info(f"removing msg {msg_ids=}")
|
||||||
for msg_id in msg_ids:
|
for msg_id in msg_ids:
|
||||||
self.msg_ids[chat_id].remove(msg_id)
|
try:
|
||||||
|
self.msg_ids[chat_id].remove(msg_id)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
self.msgs[chat_id].pop(msg_id, None)
|
self.msgs[chat_id].pop(msg_id, None)
|
||||||
|
|
||||||
def add_message(self, chat_id: int, msg: Dict[str, Any]) -> None:
|
def add_message(self, chat_id: int, msg: Dict[str, Any]) -> None:
|
||||||
|
|
|
@ -229,20 +229,19 @@ def update_file(controller: Controller, update: Dict[str, Any]) -> None:
|
||||||
file_id = update["file"]["id"]
|
file_id = update["file"]["id"]
|
||||||
local = update["file"]["local"]
|
local = update["file"]["local"]
|
||||||
chat_id, msg_id = controller.model.downloads.get(file_id, (None, None))
|
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(
|
log.warning(
|
||||||
"Can't find information about file with file_id=%s", file_id
|
"Can't find information about file with file_id=%s", file_id
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
msgs = controller.model.msgs.msgs[chat_id]
|
msg = controller.model.msgs.msgs[chat_id].get(msg_id)
|
||||||
for msg_id, msg in msgs.items():
|
if not msg:
|
||||||
if msg["id"] == msg_id:
|
return
|
||||||
proxy = MsgProxy(msg)
|
proxy = MsgProxy(msg)
|
||||||
proxy.local = local
|
proxy.local = local
|
||||||
controller.render_msgs()
|
controller.render_msgs()
|
||||||
if proxy.is_downloaded:
|
if proxy.is_downloaded:
|
||||||
controller.model.downloads.pop(file_id)
|
controller.model.downloads.pop(file_id)
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
@update_handler("updateMessageContentOpened")
|
@update_handler("updateMessageContentOpened")
|
||||||
|
|
|
@ -72,6 +72,13 @@ def setup_log() -> None:
|
||||||
logging.captureWarnings(True)
|
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:
|
def get_file_handler(file_path: str) -> str:
|
||||||
mtype, _ = mimetypes.guess_type(file_path)
|
mtype, _ = mimetypes.guess_type(file_path)
|
||||||
if not mtype:
|
if not mtype:
|
||||||
|
|
15
tg/views.py
15
tg/views.py
|
@ -5,16 +5,7 @@ from datetime import datetime
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
||||||
|
|
||||||
from tg import config
|
from tg import config
|
||||||
from tg.colors import (
|
from tg.colors import bold, cyan, get_color, magenta, reverse, white, yellow
|
||||||
blue,
|
|
||||||
bold,
|
|
||||||
cyan,
|
|
||||||
get_color,
|
|
||||||
magenta,
|
|
||||||
reverse,
|
|
||||||
white,
|
|
||||||
yellow,
|
|
||||||
)
|
|
||||||
from tg.models import Model, UserModel
|
from tg.models import Model, UserModel
|
||||||
from tg.msg import MsgProxy
|
from tg.msg import MsgProxy
|
||||||
from tg.tdlib import ChatType, get_chat_type
|
from tg.tdlib import ChatType, get_chat_type
|
||||||
|
@ -98,10 +89,8 @@ class StatusView:
|
||||||
self.win.resize(self.h, self.w)
|
self.win.resize(self.h, self.w)
|
||||||
self.win.mvwin(self.y, self.x)
|
self.win.mvwin(self.y, self.x)
|
||||||
|
|
||||||
def draw(self, msg: Optional[str] = None) -> None:
|
def draw(self, msg: str = "") -> None:
|
||||||
self.win.clear()
|
self.win.clear()
|
||||||
if not msg:
|
|
||||||
return
|
|
||||||
self.win.addstr(0, 0, msg.replace("\n", " ")[: self.w])
|
self.win.addstr(0, 0, msg.replace("\n", " ")[: self.w])
|
||||||
self._refresh()
|
self._refresh()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue