diff --git a/rtv/content.py b/rtv/content.py index 6321564..6c9515c 100644 --- a/rtv/content.py +++ b/rtv/content.py @@ -4,6 +4,8 @@ from contextlib import contextmanager import praw +from errors import SubmissionURLError, SubredditNameError + def clean(unicode_string): """ Convert unicode string into ascii-safe characters. @@ -159,8 +161,12 @@ class SubmissionContent(BaseContent): indent_size=2, max_indent_level=4): - with loader(): - submission = reddit.get_submission(url) + try: + with loader(): + submission = reddit.get_submission(url) + + except praw.errors.APIException: + raise SubmissionURLError(url) return cls(submission, loader, indent_size, max_indent_level) @@ -273,9 +279,13 @@ class SubredditContent(BaseContent): if name == 'all': sub = reddit.get_subreddit(name) + else: - with loader(): - sub = reddit.get_subreddit(name, fetch=True) + try: + with loader(): + sub = reddit.get_subreddit(name, fetch=True) + except praw.errors.ClientException: + raise SubredditNameError(name) return cls('/r/'+sub.display_name, sub.get_hot(limit=None), loader) diff --git a/rtv/errors.py b/rtv/errors.py new file mode 100644 index 0000000..6e70a57 --- /dev/null +++ b/rtv/errors.py @@ -0,0 +1,12 @@ +class EscapePressed(Exception): + pass + +class SubmissionURLError(Exception): + + def __init__(self, url): + self.url = url + +class SubredditNameError(Exception): + + def __init__(self, name): + self.name = name \ No newline at end of file diff --git a/rtv/main.py b/rtv/main.py index cdcc835..56f27ba 100644 --- a/rtv/main.py +++ b/rtv/main.py @@ -1,30 +1,58 @@ import argparse import praw +from requests.exceptions import ConnectionError +from errors import SubmissionURLError, SubredditNameError from utils import curses_session from subreddit import SubredditPage +from submission import SubmissionPage parser = argparse.ArgumentParser(description='Reddit Terminal Viewer') parser.add_argument('-s', dest='subreddit', default='front', help='subreddit name') -parser.add_argument('-l', dest='link', help='full link to a specific submission') +parser.add_argument('-l', dest='link', help='full link to a submission') group = parser.add_argument_group('authentication (optional)') group.add_argument('-u', dest='username', help='reddit username') group.add_argument('-p', dest='password', help='reddit password') +group.add_argument('--debug', action='store_true') def main(args): - reddit = praw.Reddit(user_agent='reddit terminal viewer v0.0') - reddit.config.decode_html_entities = True + try: + reddit = praw.Reddit(user_agent='reddit terminal viewer v0.0') + reddit.config.decode_html_entities = True - if args.username and args.password: - reddit.login(args.username, args.password) + if args.username and args.password: + reddit.login(args.username, args.password) - with curses_session() as stdscr: + with curses_session() as stdscr: - page = SubredditPage(stdscr, reddit, args.subreddit) - page.loop() + if args.link: + # Go directly to submission + page = SubmissionPage(stdscr, reddit, url=args.link) + page.loop() + + page = SubredditPage(stdscr, reddit, args.subreddit) + page.loop() + + except KeyboardInterrupt: + return + + except ConnectionError: + print 'Timeout: Could not connect to website' + + except SubmissionURLError as e: + print 'Could not reach submission URL: {}'.format(e.url) + + except SubredditNameError as e: + print 'Could not reach subreddit: {}'.format(e.name) + + # except Exception: + # if not args.debug: + # print 'Unhandled exception' + # else: + # raise if __name__ == '__main__': args = parser.parse_args() - main(args) \ No newline at end of file + main(args) diff --git a/rtv/page.py b/rtv/page.py index ffb2df8..5cb2bd2 100644 --- a/rtv/page.py +++ b/rtv/page.py @@ -75,6 +75,9 @@ class BasePage(object): Base terminal viewer incorperates a cursor to navigate content """ + MIN_HEIGHT = 10 + MIN_WIDTH = 20 + def __init__(self, stdscr, content, **kwargs): self.stdscr = stdscr @@ -107,21 +110,27 @@ class BasePage(object): def draw(self): n_rows, n_cols = self.stdscr.getmaxyx() + if n_rows < self.MIN_HEIGHT or n_cols < self.MIN_WIDTH: + return + self._header_window = self.stdscr.derwin(1, n_cols, 0, 0) self._content_window = self.stdscr.derwin(1, 0) - self.draw_header() - self.draw_content() + self._draw_header() + self._draw_content() self.add_cursor() - def draw_header(self): + def draw_item(self, window, data, inverted): + raise NotImplementedError + + def _draw_header(self): n_rows, n_cols = self._header_window.getmaxyx() self._header_window.erase() self._header_window.addnstr(0, 0, self.content.name, n_cols-1) - def draw_content(self): + def _draw_content(self): """ Loop through submissions and fill up the content page. """ @@ -153,9 +162,6 @@ class BasePage(object): self._content_window.refresh() - def draw_item(self, window, data, inverted): - raise NotImplementedError - def _move_cursor(self, direction): self.remove_cursor() @@ -167,7 +173,7 @@ class BasePage(object): # If we don't redraw, ACS_VLINE gets screwed up when changing the # attr back to normal. There may be a way around this. if True: #if redraw - self.draw_content() + self._draw_content() self.add_cursor() diff --git a/rtv/subreddit.py b/rtv/subreddit.py index 631501e..0f50e90 100644 --- a/rtv/subreddit.py +++ b/rtv/subreddit.py @@ -1,10 +1,11 @@ import curses import sys +from errors import SubredditNameError from page import BasePage from submission import SubmissionPage from content import SubredditContent -from utils import LoadScreen, text_input +from utils import LoadScreen, text_input, display_message class SubredditPage(BasePage): @@ -56,11 +57,21 @@ class SubredditPage(BasePage): def refresh_content(self, name=None): - self.nav.page_index, self.nav.cursor_index = 0, 0 - self.nav.inverted = False - self.name = name if name else self.name - self.content = SubredditContent.from_name(self.reddit, self.name, self.loader) - self.stdscr.clear() + name = name or self.name + + try: + self.content = SubredditContent.from_name( + self.reddit, name, self.loader) + + except SubredditNameError: + display_message(self.stdscr, 'Invalid Subreddit') + + else: + self.nav.page_index, self.nav.cursor_index = 0, 0 + self.nav.inverted = False + self.name = name + + #self.stdscr.clear() self.draw() def prompt_subreddit(self): diff --git a/rtv/utils.py b/rtv/utils.py index 8c2b780..8166aa9 100644 --- a/rtv/utils.py +++ b/rtv/utils.py @@ -5,8 +5,7 @@ import threading from curses import textpad from contextlib import contextmanager -class EscapePressed(Exception): - pass +from errors import EscapePressed def text_input(window): """ @@ -40,6 +39,27 @@ def text_input(window): return out +def display_message(stdscr, message): + "Display a message box at the center of the screen and wait for a keypress" + + message_len = len(message) + n_rows, n_cols = stdscr.getmaxyx() + s_row = (n_rows - 2) / 2 + s_col = (n_cols - message_len - 1) / 2 + window = stdscr.derwin(3, message_len+2, s_row, s_col) + + window.erase() + window.border() + window.addstr(1, 1, message) + window.refresh() + + stdscr.getch() + + window.clear() + window = None + stdscr.refresh() + + class LoadScreen(object): def __init__(self, stdscr):