Merge branch 'master' into woorst-imgur_api

This commit is contained in:
Michael Lazar
2017-07-23 01:01:48 -04:00
145 changed files with 132727 additions and 119887 deletions

View File

@@ -11,6 +11,18 @@ import warnings
import six
import requests
# Need to check for curses comparability before performing the rtv imports
try:
import curses
except ImportError:
if sys.platform == 'win32':
sys.exit('Fatal Error: This program is not compatible with Windows '
'Operating Systems.\nPlease try installing on either Linux '
'or Mac OS')
else:
sys.exit('Fatal Error: Your python distribution appears to be missing '
'_curses.so.\nWas it compiled without support for curses?')
from . import docs
from . import packages
from .packages import praw

View File

@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
__version__ = '1.15.1'
__version__ = '1.16.0'

View File

@@ -159,7 +159,7 @@ class Content(object):
else:
# Saved comments don't have a nested level and are missing a couple
# of fields like ``submission``. As a result, we can only load a
# subset of fields to avoid triggering a seperate api call to load
# subset of fields to avoid triggering a separate api call to load
# the full comment.
author = getattr(comment, 'author', '[deleted]')
stickied = getattr(comment, 'stickied', False)
@@ -167,10 +167,11 @@ class Content(object):
data['type'] = 'SavedComment'
data['level'] = None
data['title'] = comment.body
data['comments'] = ''
data['title'] = '[Comment] {0}'.format(comment.body)
data['comments'] = None
data['url_full'] = comment._fast_permalink
data['url'] = comment._fast_permalink
data['permalink'] = comment._fast_permalink
data['nsfw'] = comment.over_18
data['subreddit'] = six.text_type(comment.subreddit)
data['url_type'] = 'selfpost'
@@ -565,7 +566,7 @@ class SubredditContent(Content):
raise exceptions.AccountError('Not logged in')
else:
order = order or 'new'
submissions = reddit.user.get_submitted(sort=order, limit=None)
submissions = reddit.user.get_overview(sort=order, limit=None)
elif resource_root == 'u' and resource == 'saved':
if not reddit.is_oauth_session():
@@ -578,7 +579,7 @@ class SubredditContent(Content):
order = order or 'new'
period = period or 'all'
redditor = reddit.get_redditor(resource)
submissions = redditor.get_submitted(
submissions = redditor.get_overview(
sort=order, time=period, limit=None)
elif resource == 'front':

View File

@@ -30,6 +30,8 @@ https://github.com/michael-lazar/rtv
m : Move up one page
gg : Jump to the first post
G : Jump to the last post
J : Jump to the next sibling comment
K : Jump to the parent comment
1 : Sort by hot
2 : Sort by top
3 : Sort by rising

View File

@@ -48,4 +48,8 @@ class TemporaryFileError(RTVError):
class MailcapEntryNotFound(RTVError):
"A valid mailcap entry could not be coerced from the given url"
"A valid mailcap entry could not be coerced from the given url"
class InvalidRefreshToken(RTVError):
"The refresh token is corrupt and cannot be used to login"

View File

@@ -273,6 +273,36 @@ class VidmeMIMEParser(BaseMIMEParser):
return url, None
class LiveleakMIMEParser(BaseMIMEParser):
"""
https://www.liveleak.com/view?i=12c_3456789
<video>
<source src="https://cdn.liveleak.com/..mp4" res="HD" type="video/mp4">
<source src="https://cdn.liveleak.com/..mp4" res="SD" type="video/mp4">
</video>
Sometimes only one video source is available
"""
pattern = re.compile(r'https?://((www|m)\.)?liveleak\.com/view\?i=\w+$')
@staticmethod
def get_mimetype(url):
page = requests.get(url)
soup = BeautifulSoup(page.content, 'html.parser')
urls = []
videos = soup.find_all('video')
for vid in videos:
source = vid.find('source', attr={'res': 'HD'}) \
or vid.find('source')
if source:
urls.append((source.get('src'), source.get('type')))
# TODO: Handle pages with multiple videos
# TODO: Handle pages with youtube embeds
if urls:
return urls[0]
else:
return url, None
# Parsers should be listed in the order they will be checked
parsers = [
StreamableMIMEParser,
@@ -282,5 +312,6 @@ parsers = [
ImgurApiMIMEParser,
RedditUploadsMIMEParser,
YoutubeMIMEParser,
LiveleakMIMEParser,
GifvMIMEParser,
BaseMIMEParser]

View File

@@ -15,6 +15,8 @@ from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from . import docs
from .config import TEMPLATES
from .exceptions import InvalidRefreshToken
from .packages.praw.errors import HTTPException, OAuthException
_logger = logging.getLogger(__name__)
@@ -131,8 +133,25 @@ class OAuthHelper(object):
# If we already have a token, request new access credentials
if self.config.refresh_token:
with self.term.loader('Logging in'):
self.reddit.refresh_access_information(
self.config.refresh_token)
try:
self.reddit.refresh_access_information(
self.config.refresh_token)
except (HTTPException, OAuthException) as e:
# Reddit didn't accept the refresh-token
# This appears to throw a generic 400 error instead of the
# more specific invalid_token message that it used to send
if isinstance(e, HTTPException):
if e._raw.status_code != 400:
# No special handling if the error is something
# temporary like a 5XX.
raise e
# Otherwise we know the token is bad, so we can remove it.
_logger.exception(e)
self.clear_oauth_data()
raise InvalidRefreshToken(
' Invalid user credentials!\n'
'The cached refresh token has been removed')
return
state = uuid.uuid4().hex
@@ -207,4 +226,4 @@ class OAuthHelper(object):
def clear_oauth_data(self):
self.reddit.clear_authentication()
self.config.delete_refresh_token()
self.config.delete_refresh_token()

View File

@@ -10,7 +10,7 @@ from __future__ import absolute_import
import sys
__praw_hash__ = '3bc535e4778047b78d9aeb2e3a5b108f96a091b2'
__praw_hash__ = 'ad0dbcf49d5937ffd39e13e45ebcb404b00c582a'
__praw_bundled__ = True

View File

@@ -113,7 +113,7 @@ class RedditContentObject(object):
def __setattr__(self, name, value):
"""Set the `name` attribute to `value."""
if value and name == 'subreddit':
if value and name == 'subreddit' and isinstance(value, six.string_types):
value = Subreddit(self.reddit_session, value, fetch=False)
elif value and name in REDDITOR_KEYS:
if isinstance(value, bool):

View File

@@ -390,14 +390,7 @@ class Page(object):
self._draw_content()
self._draw_footer()
self._add_cursor()
# Note: There used to be a call to stdscr.touchwin() here. However, a
# bug was discovered in tmux when $TERM was set to `xterm-256color`,
# where only part of the screen got redrawn when scrolling. The correct
# solution is to use `screen-256color` (which gets set automatically by
# tmux) but many people override ther $TERM in their tmux.conf or
# .bashrc file. Using clearok() instead seems to fix the problem, at
# the expense of slightly more expensive screen refreshes.
self.term.stdscr.clearok(True)
self.term.clear_screen()
self.term.stdscr.refresh()
def _draw_header(self):

View File

@@ -181,6 +181,52 @@ class SubmissionPage(Page):
else:
self.term.flash()
@PageController.register(Command('SUBMISSION_GOTO_PARENT'))
def move_parent_up(self):
"""
Move the cursor up to the comment's parent. If the comment is
top-level, jump to the previous top-level comment.
"""
cursor = self.nav.absolute_index
if cursor > 0:
level = max(self.content.get(cursor)['level'], 1)
while self.content.get(cursor - 1)['level'] >= level:
self._move_cursor(-1)
cursor -= 1
self._move_cursor(-1)
else:
self.term.flash()
self.clear_input_queue()
@PageController.register(Command('SUBMISSION_GOTO_SIBLING'))
def move_sibling_next(self):
"""
Jump to the next comment that's at the same level as the selected
comment and shares the same parent.
"""
cursor = self.nav.absolute_index
if cursor >= 0:
level = self.content.get(cursor)['level']
try:
move = 1
while self.content.get(cursor + move)['level'] > level:
move += 1
except IndexError:
self.term.flash()
else:
if self.content.get(cursor + move)['level'] == level:
for _ in range(move):
self._move_cursor(1)
else:
self.term.flash()
else:
self.term.flash()
self.clear_input_queue()
def _draw_item(self, win, data, inverted):
if data['type'] == 'MoreComments':

View File

@@ -260,9 +260,11 @@ class SubredditPage(Page):
text, attr = self.term.get_arrow(data['likes'])
self.term.add_line(win, text, attr=attr)
self.term.add_line(win, ' {created} '.format(**data))
text, attr = '-', curses.A_BOLD
self.term.add_line(win, text, attr=attr)
self.term.add_line(win, ' {comments} '.format(**data))
if data['comments'] is not None:
text, attr = '-', curses.A_BOLD
self.term.add_line(win, text, attr=attr)
self.term.add_line(win, ' {comments} '.format(**data))
if data['saved']:
text, attr = '[saved]', Color.GREEN

View File

@@ -14,7 +14,7 @@
# - The first entry with a matching MIME type will be executed, * is a wildcard
# - %s will be replaced with the image or video url
# - Add ``test=test -n "$DISPLAY"`` if your command opens a new window
# - Add ``needstermial`` for commands that use the terminal
# - Add ``needsterminal`` for commands that use the terminal
# - Add ``copiousoutput`` for commands that dump text to stdout
###############################################################################

View File

@@ -133,6 +133,8 @@ SUBMISSION_POST = c
SUBMISSION_EXIT = h, <KEY_LEFT>
SUBMISSION_OPEN_IN_PAGER = l, <KEY_RIGHT>
SUBMISSION_OPEN_IN_URLVIEWER = b
SUBMISSION_GOTO_PARENT = K
SUBMISSION_GOTO_SIBLING = J
; Subreddit page
SUBREDDIT_SEARCH = f

View File

@@ -59,6 +59,7 @@ class Terminal(object):
self.loader = LoadScreen(self)
self._display = None
self._mailcap_dict = mailcap.getcaps()
self._term = os.environ['TERM']
# Hack to allow setting the Imgur OAuth cred in the config file
mime_parsers.ImgurApiMIMEParser.client_id = config['imgur_client_id']
@@ -772,3 +773,30 @@ class Terminal(object):
out = '\n'.join(stack)
return out
def clear_screen(self):
"""
In the beginning this always called touchwin(). However, a bug
was discovered in tmux when TERM was set to `xterm-256color`, where
only part of the screen got redrawn when scrolling. tmux automatically
sets TERM to `screen-256color`, but many people choose to override
this in their tmux.conf or .bashrc file which can cause issues.
Using clearok() instead seems to fix the problem, with the trade off
of slightly more expensive screen refreshes.
Update: It was discovered that using clearok() introduced a
separate bug for urxvt users in which their screen flashed when
scrolling. Heuristics were added to make it work with as many
configurations as possible. It's still not perfect
(e.g. urxvt + xterm-256color) will screen flash, but it should
work in all cases if the user sets their TERM correctly.
Reference:
https://github.com/michael-lazar/rtv/issues/343
https://github.com/michael-lazar/rtv/issues/323
"""
if self._term != 'xterm-256color':
self.stdscr.touchwin()
else:
self.stdscr.clearok(True)