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): """