Change VideoInfo functions to accessor style
This commit is contained in:
@@ -14,6 +14,7 @@ items_sticker = [
|
||||
'item',
|
||||
'liveChatPaidStickerRenderer'
|
||||
]
|
||||
|
||||
class SuperchatCalculator(ChatProcessor):
|
||||
"""
|
||||
Calculate the amount of SuperChat by currency.
|
||||
|
||||
@@ -24,7 +24,7 @@ class Extractor:
|
||||
def _get_duration_of_video(self, video_id):
|
||||
duration = 0
|
||||
try:
|
||||
duration = VideoInfo(video_id).duration
|
||||
duration = VideoInfo(video_id).get_duration()
|
||||
except InvalidVideoIdException:
|
||||
raise
|
||||
return duration
|
||||
|
||||
@@ -54,7 +54,7 @@ class SuperChatMiner:
|
||||
def extract(video_id, div = 1, callback = None, processor = None):
|
||||
duration = 0
|
||||
try:
|
||||
duration = VideoInfo(video_id).duration
|
||||
duration = VideoInfo(video_id).get_duration()
|
||||
except InvalidVideoIdException:
|
||||
raise
|
||||
if duration == 0:
|
||||
|
||||
@@ -4,8 +4,10 @@ import requests
|
||||
from .. import config
|
||||
from .. import util
|
||||
from ..exceptions import InvalidVideoIdException
|
||||
|
||||
headers = config.headers
|
||||
pattern=re.compile(r"yt\.setConfig\({'PLAYER_CONFIG': ({.*})}\);")
|
||||
|
||||
pattern = re.compile(r"yt\.setConfig\({'PLAYER_CONFIG': ({.*})}\);")
|
||||
|
||||
item_channel_id =[
|
||||
"videoDetails",
|
||||
@@ -36,7 +38,6 @@ item_author_image =[
|
||||
"url"
|
||||
]
|
||||
|
||||
|
||||
item_thumbnail = [
|
||||
"defaultThumbnail",
|
||||
"thumbnails",
|
||||
@@ -63,24 +64,27 @@ item_moving_thumbnail = [
|
||||
]
|
||||
|
||||
class VideoInfo:
|
||||
def __init__(self,video_id):
|
||||
'''
|
||||
VideoInfo object retrieves YouTube video informations
|
||||
from the video page.
|
||||
|
||||
Parameter
|
||||
---------
|
||||
video_id : str
|
||||
|
||||
Exception
|
||||
---------
|
||||
InvalidVideoIdException :
|
||||
Occurs when video_id does not exist on YouTube.
|
||||
'''
|
||||
def __init__(self, video_id):
|
||||
self.video_id = video_id
|
||||
text = self._get_page_text(video_id)
|
||||
self._parse(text)
|
||||
self._get_attributes()
|
||||
|
||||
def _get_attributes(self):
|
||||
self.duration = self._duration()
|
||||
self.channel_id = self._channel_id()
|
||||
self.channel_name = self._channel_name()
|
||||
self.thumbnail = self._thumbnail()
|
||||
self.author_image = self._author_image()
|
||||
self.title = self._title()
|
||||
self.moving_thumbnail = self._moving_thumbnail()
|
||||
|
||||
def _get_page_text(self,video_id):
|
||||
def _get_page_text(self, video_id):
|
||||
url = f"https://www.youtube.com/embed/{video_id}"
|
||||
resp= requests.get(url, headers = headers)
|
||||
resp = requests.get(url, headers = headers)
|
||||
resp.raise_for_status()
|
||||
return resp.text
|
||||
|
||||
@@ -91,8 +95,8 @@ class VideoInfo:
|
||||
if response is None:
|
||||
raise InvalidVideoIdException(
|
||||
f"Specified video_id [{self.video_id}] is invalid.")
|
||||
self.renderer = self._get_item(json.loads(response), item_renderer)
|
||||
if self.renderer is None:
|
||||
self._renderer = self._get_item(json.loads(response), item_renderer)
|
||||
if self._renderer is None:
|
||||
raise InvalidVideoIdException(
|
||||
f"No renderer found in video_id: [{self.video_id}].")
|
||||
|
||||
@@ -111,29 +115,35 @@ class VideoInfo:
|
||||
return None
|
||||
return dict_body
|
||||
|
||||
def _duration(self):
|
||||
return int(self.renderer.get("videoDurationSeconds") or 0)
|
||||
|
||||
def _title(self):
|
||||
if self.renderer.get("title"):
|
||||
return [''.join(run["text"])
|
||||
for run in self.renderer["title"]["runs"]][0]
|
||||
def get_duration(self):
|
||||
duration_seconds = self._renderer.get("videoDurationSeconds")
|
||||
if duration_seconds:
|
||||
'''Fetched value is string, so cast to integer.'''
|
||||
return int(duration_seconds)
|
||||
'''When key is not found, explicitly returns None.'''
|
||||
return None
|
||||
|
||||
def _channel_id(self):
|
||||
channel_url = self._get_item(self.renderer, item_channel_id)
|
||||
def get_title(self):
|
||||
print(self._renderer)
|
||||
if self._renderer.get("title"):
|
||||
return [''.join(run["text"])
|
||||
for run in self._renderer["title"]["runs"]][0]
|
||||
return None
|
||||
|
||||
def get_channel_id(self):
|
||||
channel_url = self._get_item(self._renderer, item_channel_id)
|
||||
if channel_url:
|
||||
return channel_url[9:]
|
||||
return None
|
||||
|
||||
def _author_image(self):
|
||||
return self._get_item(self.renderer, item_author_image)
|
||||
def get_author_image(self):
|
||||
return self._get_item(self._renderer, item_author_image)
|
||||
|
||||
def _thumbnail(self):
|
||||
return self._get_item(self.renderer, item_thumbnail)
|
||||
def get_thumbnail(self):
|
||||
return self._get_item(self._renderer, item_thumbnail)
|
||||
|
||||
def _channel_name(self):
|
||||
return self._get_item(self.renderer, item_channel_name)
|
||||
def get_channel_name(self):
|
||||
return self._get_item(self._renderer, item_channel_name)
|
||||
|
||||
def _moving_thumbnail(self):
|
||||
return self._get_item(self.renderer, item_moving_thumbnail)
|
||||
def get_moving_thumbnail(self):
|
||||
return self._get_item(self._renderer, item_moving_thumbnail)
|
||||
|
||||
@@ -71,6 +71,7 @@ items_test_list3 = [
|
||||
'root',
|
||||
'node4'
|
||||
]
|
||||
|
||||
items_test_list_nest = [
|
||||
'root',
|
||||
'node5',
|
||||
@@ -114,7 +115,8 @@ def test_get_items_2():
|
||||
assert get_item(dict_test, items_test_nest) == 'value2-0'
|
||||
|
||||
def test_get_items_3():
|
||||
assert get_item(dict_test, items_test_list0) == {'node3-1-0' : 'value3-1-0'}
|
||||
assert get_item(
|
||||
dict_test, items_test_list0) == {'node3-1-0' : 'value3-1-0'}
|
||||
|
||||
def test_get_items_4():
|
||||
assert get_item(dict_test, items_test_list1) == 'value3-1-0'
|
||||
|
||||
62
tests/test_videoinfo.py
Normal file
62
tests/test_videoinfo.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from pytchat.tool.videoinfo import VideoInfo
|
||||
from pytchat.exceptions import InvalidVideoIdException
|
||||
import pytest
|
||||
|
||||
def _open_file(path):
|
||||
with open(path,mode ='r',encoding = 'utf-8') as f:
|
||||
return f.read()
|
||||
|
||||
def _set_test_data(filepath, mocker):
|
||||
_text = _open_file(filepath)
|
||||
response_mock = mocker.Mock()
|
||||
response_mock.status_code = 200
|
||||
response_mock.text = _text
|
||||
mocker.patch('requests.get').return_value = response_mock
|
||||
|
||||
def test_archived_page(mocker):
|
||||
_set_test_data('tests/testdata/videoinfo/archived_page.txt', mocker)
|
||||
info = VideoInfo('test_id')
|
||||
actual_thumbnail_url = 'https://i.ytimg.com/vi/fzI9FNjXQ0o/hqdefault.jpg'
|
||||
assert info.video_id == 'test_id'
|
||||
assert info.get_channel_name() == 'GitHub'
|
||||
assert info.get_thumbnail() == actual_thumbnail_url
|
||||
assert info.get_title() == 'GitHub Arctic Code Vault'
|
||||
assert info.get_channel_id() == 'UC7c3Kb6jYCRj4JOHHZTxKsQ'
|
||||
assert info.get_duration() == 148
|
||||
|
||||
def test_live_page(mocker):
|
||||
_set_test_data('tests/testdata/videoinfo/live_page.txt', mocker)
|
||||
info = VideoInfo('test_id')
|
||||
'''live page :duration = 0'''
|
||||
assert info.get_duration() == 0
|
||||
assert info.video_id == 'test_id'
|
||||
assert info.get_channel_name() == 'BGM channel'
|
||||
assert info.get_thumbnail() == \
|
||||
'https://i.ytimg.com/vi/fEvM-OUbaKs/hqdefault_live.jpg'
|
||||
assert info.get_title() == (
|
||||
'Coffee Jazz Music - Chill Out Lounge Jazz Music Radio'
|
||||
' - 24/7 Live Stream - Slow Jazz')
|
||||
assert info.get_channel_id() == 'UCQINXHZqCU5i06HzxRkujfg'
|
||||
|
||||
def test_invalid_video_id(mocker):
|
||||
'''Test case invalid video_id is specified.'''
|
||||
_set_test_data(
|
||||
'tests/testdata/videoinfo/invalid_video_id_page.txt', mocker)
|
||||
try:
|
||||
_ = VideoInfo('test_id')
|
||||
assert False
|
||||
except InvalidVideoIdException:
|
||||
assert True
|
||||
|
||||
def test_no_info(mocker):
|
||||
'''Test case the video page has renderer, but no info.'''
|
||||
_set_test_data(
|
||||
'tests/testdata/videoinfo/no_info_page.txt', mocker)
|
||||
info = VideoInfo('test_id')
|
||||
assert info.video_id == 'test_id'
|
||||
assert info.get_channel_name() is None
|
||||
assert info.get_thumbnail() is None
|
||||
assert info.get_title() is None
|
||||
assert info.get_channel_id() is None
|
||||
assert info.get_duration() is None
|
||||
|
||||
15
tests/testdata/videoinfo/archived_page.txt
vendored
Normal file
15
tests/testdata/videoinfo/archived_page.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
14
tests/testdata/videoinfo/invalid_video_id_page.txt
vendored
Normal file
14
tests/testdata/videoinfo/invalid_video_id_page.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
15
tests/testdata/videoinfo/live_page.txt
vendored
Normal file
15
tests/testdata/videoinfo/live_page.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
15
tests/testdata/videoinfo/no_info_page.txt
vendored
Normal file
15
tests/testdata/videoinfo/no_info_page.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user