Skip to content
This repository has been archived by the owner on Oct 25, 2020. It is now read-only.

Commit

Permalink
Use HTTPS when calling get_video_url API endpoint
Browse files Browse the repository at this point in the history
We also use requests for this since it has much better SSL support than Python
2's urllib/urllib2.
  • Loading branch information
cdown committed Sep 5, 2015
1 parent f8ed72b commit 479207f
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 21 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -1,6 +1,7 @@
include LICENSE
include MANIFEST.in
include README.rst
include requirements.txt
recursive-include tests *

recursive-exclude * *.py[co]
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
@@ -0,0 +1 @@
requests
5 changes: 5 additions & 0 deletions setup.py
Expand Up @@ -6,6 +6,9 @@
with open('README.rst') as readme_f:
README = readme_f.read()

with open('requirements.txt') as requirements_f:
REQUIREMENTS = requirements_f.readlines()

with open('tests/requirements.txt') as test_requirements_f:
TEST_REQUIREMENTS = test_requirements_f.readlines()

Expand Down Expand Up @@ -40,6 +43,8 @@
'Topic :: Utilities',
],

install_requires=REQUIREMENTS,

test_suite='nose.collector',
tests_require=TEST_REQUIREMENTS,
)
30 changes: 23 additions & 7 deletions tests/e2e_tests.py
@@ -1,36 +1,52 @@
#!/usr/bin/env python2

import json
import httpretty
import os
import yturl
from nose.tools import assert_raises, eq_ as eq
from mock import patch


SCRIPT_DIR = os.path.dirname(__file__)
FAKE_URL = 'http://foo.com/' + 'x' * 11
VIDEO_ID = 'x' * 11
FAKE_URL = 'http://foo.com/' + VIDEO_ID


@httpretty.activate
@patch("yturl.urlopen")
def test_quality_as_word_ok(urlopen_mock):
with open(os.path.join(SCRIPT_DIR, 'files/success_output')) as output_f:
expected = dict(json.load(output_f))[43]

with open(os.path.join(SCRIPT_DIR, 'files/success_input'), 'rb') as mock_f:
urlopen_mock.return_value = mock_f
chosen_uri = yturl.main(['-q', 'high', FAKE_URL], force_return=True)
eq(chosen_uri, expected)
fake_api_output = mock_f.read()

httpretty.register_uri(
httpretty.GET, yturl.GVI_BASE_URL + VIDEO_ID,
body=fake_api_output, content_type='application/x-www-form-urlencoded',
)

chosen_uri = yturl.main(['-q', 'high', FAKE_URL], force_return=True)
eq(chosen_uri, expected)


def test_unknown_quality():
with assert_raises(yturl.UnknownQualityError):
yturl.main(['-q', '123456', FAKE_URL], force_return=True)


@httpretty.activate
@patch('yturl.urlopen')
def test_youtube_api_error_exit(urlopen_mock):
mock_filename = os.path.join(SCRIPT_DIR, 'files/embed_restricted')
with open(mock_filename, 'rb') as mock_f:
urlopen_mock.return_value = mock_f
with assert_raises(yturl.YouTubeAPIError):
yturl.main([FAKE_URL], force_return=True)
fake_api_output = mock_f.read()

httpretty.register_uri(
httpretty.GET, yturl.GVI_BASE_URL + VIDEO_ID,
body=fake_api_output, content_type='application/x-www-form-urlencoded',
)

with assert_raises(yturl.YouTubeAPIError):
yturl.main([FAKE_URL], force_return=True)
1 change: 1 addition & 0 deletions tests/requirements.txt
@@ -1,3 +1,4 @@
mock
nose
nose-parameterized
httpretty
25 changes: 20 additions & 5 deletions tests/unit_tests.py
Expand Up @@ -3,6 +3,7 @@
import os
import yturl
import json
import httpretty
from nose.tools import assert_raises, eq_ as eq, assert_true
from mock import patch
from nose_parameterized import parameterized
Expand Down Expand Up @@ -69,6 +70,7 @@ def test_video_id_from_url_unparseable(url):
yturl.video_id_from_url(url)


@httpretty.activate
@patch("yturl.urlopen")
def test_available_itags_parsing(urlopen_mock):
with open(os.path.join(SCRIPT_DIR, 'files/success_output')) as output_f:
Expand All @@ -78,8 +80,14 @@ def test_available_itags_parsing(urlopen_mock):
expected = map(tuple, expected_raw)

with open(os.path.join(SCRIPT_DIR, 'files/success_input'), 'rb') as mock_f:
urlopen_mock.return_value = mock_f
eq(list(yturl.itags_for_video('fake')), list(expected))
fake_api_output = mock_f.read()

httpretty.register_uri(
httpretty.GET, yturl.GVI_BASE_URL + 'fake',
body=fake_api_output, content_type='application/x-www-form-urlencoded',
)

eq(list(yturl.itags_for_video('fake')), list(expected))


def itag_quality_pos(itag_quality):
Expand Down Expand Up @@ -111,11 +119,18 @@ def test_itag_from_quality_ordering():
)


@httpretty.activate
@patch("yturl.urlopen")
def test_embed_restriction_raises(urlopen_mock):
mock_filename = os.path.join(SCRIPT_DIR, 'files/embed_restricted')

with open(mock_filename, 'rb') as mock_f:
urlopen_mock.return_value = mock_f
avail = yturl.itags_for_video('fake')
assert_raises(yturl.YouTubeAPIError, list, avail)
fake_api_output = mock_f.read()

httpretty.register_uri(
httpretty.GET, yturl.GVI_BASE_URL + 'fake',
body=fake_api_output, content_type='application/x-www-form-urlencoded',
)

avail = yturl.itags_for_video('fake')
assert_raises(yturl.YouTubeAPIError, list, avail)
16 changes: 7 additions & 9 deletions yturl.py
Expand Up @@ -19,6 +19,7 @@

import argparse
import sys
import requests
from collections import namedtuple


Expand Down Expand Up @@ -58,6 +59,7 @@ class VideoIDParserError(YturlError): pass
}

VIDEO_ID_LEN = 11
GVI_BASE_URL = 'https://youtube.com/get_video_info?hl=en&video_id='


def video_id_from_url(url):
Expand Down Expand Up @@ -113,19 +115,15 @@ def itags_for_video(video_id):
Return the available itags for a video with their associated URLs.
'''

url = "http://youtube.com/get_video_info?hl=en&video_id=" + video_id

# No Content-Encoding header is sent by the server, so we can't use that to
# dynamically determine the encoding to use. Thankfully, everything is
# percent encoded, so it should be legal ASCII.
res_data = dict(parse_qsl(urlopen(url).read().decode('ascii')))
gvi_url = GVI_BASE_URL + video_id
api_response_raw = requests.get(gvi_url).text
api_response = dict(parse_qsl(api_response_raw))

try:
streams_raw = res_data["url_encoded_fmt_stream_map"]
streams = api_response['url_encoded_fmt_stream_map'].split(',')
except KeyError:
raise YouTubeAPIError(res_data.get('reason', 'No reason given'))
raise YouTubeAPIError(api_response.get('reason', 'No reason given'))

streams = streams_raw.split(",")
for stream in streams:
video = dict(parse_qsl(stream))
yield int(video["itag"]), video["url"]
Expand Down

0 comments on commit 479207f

Please sign in to comment.