diff --git a/rtv/content.py b/rtv/content.py index 4d226ac..7d6807a 100644 --- a/rtv/content.py +++ b/rtv/content.py @@ -50,7 +50,7 @@ def humanize_timestamp(utc_timestamp, verbose=False): return ('%d years ago' % years) if verbose else ('%dyr' % years) @contextmanager -def default_loader(self): +def default_loader(): yield class BaseContent(object): @@ -85,6 +85,8 @@ class BaseContent(object): retval = [] while stack: item = stack.pop(0) + if isinstance(item, praw.objects.MoreComments) and (item.count==0): + continue nested = getattr(item, 'replies', None) if nested: for n in nested: @@ -104,11 +106,6 @@ class BaseContent(object): data['object'] = comment data['level'] = comment.nested_level - if getattr(comment.submission, 'author'): - sub_author = comment.submission.author.name - else: - sub_author = '[deleted]' - if isinstance(comment, praw.objects.MoreComments): data['type'] = 'MoreComments' data['count'] = comment.count @@ -119,7 +116,7 @@ class BaseContent(object): data['created'] = humanize_timestamp(comment.created_utc) data['score'] = '{} pts'.format(comment.score) data['author'] = (comment.author.name if getattr(comment, 'author') else '[deleted]') - data['is_author'] = (data['author'] == sub_author) + data['is_author'] = (data['author'] == getattr(comment.submission, 'author')) data['flair'] = (comment.author_flair_text if comment.author_flair_text else '') data['likes'] = comment.likes @@ -170,11 +167,11 @@ class SubmissionContent(BaseContent): self.max_indent_level = max_indent_level self._loader = loader self._submission = submission - self._submission_data = None - self._comment_data = None - self.name = None - self.reset() + self._submission_data = self.strip_praw_submission(self._submission) + self.name = self._submission_data['permalink'] + comments = self.flatten_comments(self._submission.comments) + self._comment_data = [self.strip_praw_comment(c) for c in comments] @classmethod def from_url( @@ -187,22 +184,12 @@ class SubmissionContent(BaseContent): try: with loader(): - submission = reddit.get_submission(url) - + submission = reddit.get_submission(url, comment_sort='hot') except praw.errors.APIException: raise SubmissionURLError(url) return cls(submission, loader, indent_size, max_indent_level) - def reset(self): - - with self._loader(): - self._submission.refresh() - self._submission_data = self.strip_praw_submission(self._submission) - self.name = self._submission_data['permalink'] - comments = self.flatten_comments(self._submission.comments) - self._comment_data = [self.strip_praw_comment(c) for c in comments] - def get(self, index, n_cols=70): """ Grab the `i`th submission, with the title field formatted to fit inside @@ -269,7 +256,7 @@ class SubmissionContent(BaseContent): elif data['type'] == 'MoreComments': with self._loader(): - comments = data['object'].comments() + comments = data['object'].comments(update=False) comments = self.flatten_comments(comments, root_level=data['level']) comment_data = [self.strip_praw_comment(c) for c in comments] self._comment_data[index:index+1] = comment_data diff --git a/rtv/submission.py b/rtv/submission.py index 9ca1105..2a07ce8 100644 --- a/rtv/submission.py +++ b/rtv/submission.py @@ -3,7 +3,8 @@ import sys from .content import SubmissionContent from .page import BasePage -from .utils import LoadScreen, Color, Symbol, display_help, open_browser +from .utils import Color, Symbol, display_help +from .workers import LoadScreen, open_browser class SubmissionPage(BasePage): @@ -80,7 +81,8 @@ class SubmissionPage(BasePage): def refresh_content(self): - self.content.reset() + url = self.content.name + self.content = SubmissionContent.from_url(self.reddit, url, self.loader) self.nav.page_index, self.nav.cursor_index = -1, 0 self.nav.inverted = False diff --git a/rtv/subreddit.py b/rtv/subreddit.py index 8fa7c70..8a3a0dc 100644 --- a/rtv/subreddit.py +++ b/rtv/subreddit.py @@ -7,8 +7,8 @@ from .errors import SubredditNameError from .page import BasePage from .submission import SubmissionPage from .content import SubredditContent -from .utils import (LoadScreen, Symbol, Color, text_input, display_message, - display_help, open_browser) +from .utils import Symbol, Color, text_input, display_message, display_help +from .workers import LoadScreen, open_browser # Used to keep track of browsing history across the current session _opened_links = set() @@ -108,7 +108,7 @@ class SubredditPage(BasePage): "Select the current submission to view posts" data = self.content.get(self.nav.absolute_index) - page = SubmissionPage(self.stdscr, self.reddit, submission=data['object']) + page = SubmissionPage(self.stdscr, self.reddit, url=data['permalink']) page.loop() if data['url'] == 'selfpost': diff --git a/rtv/utils.py b/rtv/utils.py index 3309415..04fe46f 100644 --- a/rtv/utils.py +++ b/rtv/utils.py @@ -1,9 +1,5 @@ import os -import sys -import subprocess import curses -import time -import threading from curses import textpad, ascii from contextlib import contextmanager @@ -193,82 +189,6 @@ def display_help(stdscr): help_msgs = HELP.split("\n") display_message(stdscr, help_msgs) -class LoadScreen(object): - - def __init__(self, stdscr): - - self._stdscr = stdscr - - self._args = None - self._animator = None - self._is_running = None - - def __call__( - self, - delay=0.5, - interval=0.4, - message='Downloading', - trail='...'): - - self._args = (delay, interval, message, trail) - return self - - def __enter__(self): - - self._animator = threading.Thread(target=self.animate, args=self._args) - self._animator.daemon = True - - self._is_running = True - self._animator.start() - - def __exit__(self, exc_type, exc_val, exc_tb): - - self._is_running = False - self._animator.join() - - def animate(self, delay, interval, message, trail): - - # Delay before starting animation to avoid wasting resources if the - # wait time is very short - start = time.time() - while (time.time() - start) < delay: - if not self._is_running: - return - - message_len = len(message) + len(trail) - n_rows, n_cols = self._stdscr.getmaxyx() - s_row = (n_rows - 3) // 2 - s_col = (n_cols - message_len - 1) // 2 - window = self._stdscr.derwin(3, message_len+2, s_row, s_col) - - while True: - for i in range(len(trail)+1): - - if not self._is_running: - window.clear() - window = None - self._stdscr.refresh() - return - - window.erase() - window.border() - window.addstr(1, 1, message + trail[:i]) - window.refresh() - time.sleep(interval) - -def open_browser(url): - """ - Call webbrowser.open_new_tab(url) and redirect stdout/stderr to devnull. - - This is a workaround to stop firefox from spewing warning messages to the - console. See http://bugs.python.org/issue22277 for a better description - of the problem. - """ - command = "import webbrowser; webbrowser.open_new_tab('%s')" % url - args = [sys.executable, '-c', command] - with open(os.devnull, 'ab+', 0) as null: - subprocess.check_call(args, stdout=null, stderr=null) - @contextmanager def curses_session(): @@ -316,4 +236,4 @@ def curses_session(): stdscr.keypad(0) curses.echo() curses.nocbreak() - curses.endwin() + curses.endwin() \ No newline at end of file diff --git a/rtv/workers.py b/rtv/workers.py new file mode 100644 index 0000000..9bdeeaf --- /dev/null +++ b/rtv/workers.py @@ -0,0 +1,95 @@ +import time +import sys +import os +import threading +import subprocess + +def open_browser(url): + """ + Call webbrowser.open_new_tab(url) and redirect stdout/stderr to devnull. + + This is a workaround to stop firefox from spewing warning messages to the + console. See http://bugs.python.org/issue22277 for a better description + of the problem. + """ + command = "import webbrowser; webbrowser.open_new_tab('%s')" % url + args = [sys.executable, '-c', command] + with open(os.devnull, 'ab+', 0) as null: + subprocess.check_call(args, stdout=null, stderr=null) + + +class LoadScreen(object): + """ + Display a loading dialog while waiting for a blocking action to complete. + + This class spins off a seperate thread to animate the loading screen in the + background. + + Usage: + #>>> loader = LoadScreen(stdscr) + #>>> with loader(...): + #>>> blocking_request(...) + """ + + def __init__(self, stdscr): + + self._stdscr = stdscr + + self._args = None + self._animator = None + self._is_running = None + + def __call__( + self, + delay=0.5, + interval=0.4, + message='Downloading', + trail='...'): + + self._args = (delay, interval, message, trail) + return self + + def __enter__(self): + + self._animator = threading.Thread(target=self.animate, args=self._args) + self._animator.daemon = True + + self._is_running = True + self._animator.start() + + def __exit__(self, exc_type, exc_val, exc_tb): + + self._is_running = False + self._animator.join() + + # Check for timeout error + + def animate(self, delay, interval, message, trail): + + # Delay before starting animation to avoid wasting resources if the + # wait time is very short + start = time.time() + while (time.time() - start) < delay: + if not self._is_running: + return + + message_len = len(message) + len(trail) + n_rows, n_cols = self._stdscr.getmaxyx() + s_row = (n_rows - 3) // 2 + s_col = (n_cols - message_len - 1) // 2 + window = self._stdscr.derwin(3, message_len+2, s_row, s_col) + + while True: + for i in range(len(trail)+1): + + if not self._is_running: + window.clear() + window = None + self._stdscr.refresh() + return + + window.erase() + window.border() + window.addstr(1, 1, message + trail[:i]) + window.refresh() + time.sleep(interval) \ No newline at end of file