From 7a2d248875918a69a1fb5bd1e39a4fae4bdc5ebf Mon Sep 17 00:00:00 2001 From: Josue Ortega Date: Mon, 17 Apr 2017 15:16:27 -0600 Subject: [PATCH 1/2] Add hotkeys to copy submission permalink and submission url --- rtv/docs.py | 2 ++ rtv/page.py | 23 +++++++++++++++++++++++ rtv/templates/rtv.cfg | 2 ++ setup.py | 1 + 4 files changed, 28 insertions(+) diff --git a/rtv/docs.py b/rtv/docs.py index 49f9c1f..e07f882 100644 --- a/rtv/docs.py +++ b/rtv/docs.py @@ -57,6 +57,8 @@ https://github.com/michael-lazar/rtv o : Open the submission or comment url SPACE : Fold or expand the selected comment tree b : Display urls with urlview + y : Copy submission permalink to clipboard + Y : Copy submission link to clipboard [Prompt] The `/` prompt accepts subreddits in the following formats diff --git a/rtv/page.py b/rtv/page.py index 1d400d1..2ecd556 100644 --- a/rtv/page.py +++ b/rtv/page.py @@ -5,6 +5,7 @@ import os import sys import time import curses +import pyperclip from functools import wraps import six @@ -318,6 +319,28 @@ class Page(object): message = 'New Messages' if inbox > 0 else 'No New Messages' self.term.show_notification(message) + @PageController.register(Command('COPY_SUBMISSION_PERMALINK')) + def copy_submission_permalink(self): + """ + Copies submission permalink to OS clipboard + """ + + data = self.get_selected_item() + url = data.get('permalink') + if url is not None: + pyperclip.copy(url) + + @PageController.register(Command('COPY_SUBMISSION_URL')) + def copy_post_permalink(self): + """ + Copies submission url to OS clipboard + """ + + data = self.get_selected_item() + url = data.get('url') + if url is not None: + pyperclip.copy(url) + def clear_input_queue(self): """ Clear excessive input caused by the scroll wheel or holding down a key diff --git a/rtv/templates/rtv.cfg b/rtv/templates/rtv.cfg index 7395d1c..3de6cbc 100644 --- a/rtv/templates/rtv.cfg +++ b/rtv/templates/rtv.cfg @@ -118,6 +118,8 @@ INBOX = i REFRESH = r, PROMPT = / SAVE = w +COPY_SUBMISSION_PERMALINK = y +COPY_SUBMISSION_URL = Y ; Submission page SUBMISSION_TOGGLE_COMMENT = 0x20 diff --git a/setup.py b/setup.py index ce0eb00..1fb777f 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ install_requires = [ 'kitchen', 'requests >=2.4.0', # https://github.com/michael-lazar/rtv/issues/325 'six', + 'pyperclip' ] tests_require = [ From 512b3cebe9842e601c1fdc37f914f45d1021bcd0 Mon Sep 17 00:00:00 2001 From: Josue Ortega Date: Wed, 3 May 2017 19:47:24 -0600 Subject: [PATCH 2/2] Adds own implementation of copy --- README.rst | 6 ++++++ rtv/clipboard.py | 43 +++++++++++++++++++++++++++++++++++++++++++ rtv/page.py | 21 +++++++++++++++++---- 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 rtv/clipboard.py diff --git a/README.rst b/README.rst index 19188cb..6796c7a 100644 --- a/README.rst +++ b/README.rst @@ -162,6 +162,12 @@ RTV will respect the following environment variables when accessing external pro ``$RTV_URLVIEWER`` A url viewer is a tool that can be used to extract hyperlinks from inside of blocks of text. `urlview `_ and `urlscan `_ are known to be compatible with rtv. These applications don't come pre-installed, but are available through most systems' package managers. +---- +Copy +---- +RTV supports copying submission links to the OS clipboard. For macOS it is supported out of the box, +in Linux systems RTV will need `xsel `_ or `xclip `_ commands to be installed in the system. + === FAQ === diff --git a/rtv/clipboard.py b/rtv/clipboard.py new file mode 100644 index 0000000..fa16c61 --- /dev/null +++ b/rtv/clipboard.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +import os +import sys +import platform +import subprocess + +from .exceptions import ProgramError + +def _subprocess_copy(text, args_list): + p = subprocess.Popen(args_list, stdin=subprocess.PIPE, close_fds=True) + p.communicate(input=text.encode('utf-8')) + +def copy(text): + """ + Copy text to OS clipboard. + """ + + if os.name == 'mac' or platform.system() == 'Darwin': + return copy_osx(text) + elif os.name == 'posix' or platform.system() == 'Linux': + return copy_linux(text) + else: + raise NotImplementedError + +def copy_osx(text): + _subprocess_copy(text, ['pbcopy', 'w']) + +def copy_linux(text): + def get_command_name(): + # Checks for the installation of xsel or xclip + for cmd in ['xsel', 'xclip']: + cmd_exists = subprocess.call( + ['which', cmd], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) is 0 + if cmd_exists: + return cmd + return None + cmd_args = {'xsel' : ['xsel', '-b', '-i'], + 'xclip' : ['xclip', '-selection', 'c']} + cmd_name = get_command_name() + if cmd_name is None: + raise ProgramError("External copy application not installed") + _subprocess_copy(text, cmd_args.get(cmd_name)) diff --git a/rtv/page.py b/rtv/page.py index 2ecd556..b2ad471 100644 --- a/rtv/page.py +++ b/rtv/page.py @@ -5,7 +5,7 @@ import os import sys import time import curses -import pyperclip +import logging from functools import wraps import six @@ -13,9 +13,11 @@ from kitchen.text.display import textual_width from . import docs from .objects import Controller, Color, Command -from .exceptions import TemporaryFileError +from .clipboard import copy +from .exceptions import TemporaryFileError, ProgramError from .__version__ import __version__ +_logger = logging.getLogger(__name__) def logged_in(f): """ @@ -51,6 +53,7 @@ class Page(object): self.active = True self._row = 0 self._subwindows = None + self.copy_to_clipboard = copy def refresh_content(self, order=None, name=None): raise NotImplementedError @@ -328,7 +331,12 @@ class Page(object): data = self.get_selected_item() url = data.get('permalink') if url is not None: - pyperclip.copy(url) + try: + self.copy_to_clipboard(url) + except ProgramError as e: + _logger.exception(e) + self.term.show_notification( + 'Failed to copy {} to clipboard, {}'.format(url, str(e))) @PageController.register(Command('COPY_SUBMISSION_URL')) def copy_post_permalink(self): @@ -339,7 +347,12 @@ class Page(object): data = self.get_selected_item() url = data.get('url') if url is not None: - pyperclip.copy(url) + try: + self.copy_to_clipboard(url) + except ProgramError as e: + _logger.exception(e) + self.term.show_notification( + 'Failed to copy {} to clipboard, {}'.format(url, str(e))) def clear_input_queue(self): """