Skip to content

Commit 15453b6

Browse files
committed
Added video pagination, which resolves #3.
1 parent 3d3e066 commit 15453b6

File tree

2 files changed

+83
-28
lines changed

2 files changed

+83
-28
lines changed

twitch/helix/videos.py

Lines changed: 80 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,95 @@
77

88

99
class Videos(Resource[helix.Video]):
10+
DEFAULT_FIRST: int = 20
11+
MAX_FIRST: int = 100
1012

1113
def __init__(self,
1214
api: API,
1315
video_ids: Union[str, int, List[Union[str, int]]] = None,
1416
**kwargs):
1517
super().__init__(api=api, path='videos')
1618

19+
self._kwargs = kwargs
20+
1721
if video_ids:
18-
kwargs['id'] = list(set(video_ids))
19-
20-
# Custom video caching
21-
if self._api.use_cache:
22-
cache_hits: list = []
23-
for video_id in list(kwargs['id']):
24-
cache_key: str = f'helix.video.{video_id}'
25-
cache_data: dict = API.SHARED_CACHE.get(cache_key)
26-
if cache_data:
27-
self._data.append(helix.Video(api=self._api, data=cache_data))
28-
cache_hits.append(video_id)
29-
30-
kwargs['id'] = [n for n in kwargs['id'] if n not in cache_hits]
31-
32-
# Download from API
33-
if len(kwargs['id']):
34-
for data in self._api.get(self._path, params=kwargs, ignore_cache=True)['data']:
35-
36-
# Create and append user
37-
video = helix.Video(api=self._api, data=data)
38-
self._data.append(video)
39-
40-
# Save to cache
41-
if self._api.use_cache:
42-
API.SHARED_CACHE.set(f'helix.videos.{video.id}', data)
22+
self._kwargs['id'] = list(set(video_ids))
23+
self._download_video_ids(**self._kwargs)
4324
else:
44-
self._data = [helix.Video(api=self._api, data=video) for video in
45-
self._api.get(self._path, params=kwargs)['data']]
25+
# Limit videos if first parameter is specified.
26+
# If not limit is specified, the pagination cursor
27+
# is used to continuously fetch videos.
28+
29+
# The Helix API has a maximum of 100 videos per API call.
30+
# To fetch >100, we split the request in chunks of =<100 videos
31+
first: int = kwargs['first'] if 'first' in kwargs else 0
32+
33+
while first > 0:
34+
kwargs['first'] = min(Videos.MAX_FIRST, first)
35+
kwargs['after'] = self._cursor or None
36+
self._download_by_login(**kwargs)
37+
first -= kwargs['first']
38+
39+
def __iter__(self) -> Generator['helix.Video', None, None]:
40+
# if first or ids are specified, yield data
41+
# that was downloader in the constructor
42+
if 'first' in self._kwargs or 'id' in self._kwargs:
43+
for video in self._data:
44+
yield video
45+
46+
else:
47+
# Fetch the maximum amount of videos from
48+
# API to minimize the number of requests
49+
self._kwargs['first'] = Videos.MAX_FIRST
50+
51+
while True:
52+
for video in self._videos_fragment(**self._kwargs):
53+
yield video
54+
55+
def __getitem__(self, item: int) -> 'helix.Video':
56+
for index, value in enumerate(self):
57+
if index == item:
58+
return value
59+
60+
def _videos_fragment(self, ignore_cache: bool = False, **kwargs) -> List['helix.Video']:
61+
# Set potential cursor
62+
kwargs['after'] = self._cursor or None
63+
64+
# API Response
65+
response: dict = self._api.get(self._path, params=kwargs, ignore_cache=ignore_cache)
66+
67+
# Set pagination cursor
68+
self._cursor = response.get('pagination', {}).get('cursor', None)
69+
70+
# Return video data
71+
return [helix.Video(api=self._api, data=video) for video in response['data']]
72+
73+
def _download_video_ids(self, **kwargs):
74+
# Custom video caching
75+
if self._api.use_cache:
76+
cache_hits: list = []
77+
for video_id in list(kwargs['id']):
78+
cache_key: str = f'helix.video.{video_id}'
79+
cache_data: dict = API.SHARED_CACHE.get(cache_key)
80+
if cache_data:
81+
self._data.append(helix.Video(api=self._api, data=cache_data))
82+
cache_hits.append(video_id)
83+
84+
kwargs['id'] = [n for n in kwargs['id'] if n not in cache_hits]
85+
86+
# Download from API
87+
if len(kwargs['id']):
88+
# Video data
89+
# Ignore cache, as we want to cache individual videos and not a collection of videos.
90+
for video in self._videos_fragment(ignore_cache=True, **kwargs):
91+
self._data.append(video)
92+
93+
# Save individual videos to cache
94+
if self._api.use_cache:
95+
API.SHARED_CACHE.set(f'helix.videos.{video.id}', video.data)
96+
97+
def _download_by_login(self, **kwargs):
98+
self._data.extend(self._videos_fragment(**kwargs))
4699

47100
def comments(self) -> Generator[Tuple[helix.Video, v5.Comments], None, None]:
48101
for video in self:

twitch/resource.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TypeVar, Generic, Generator, List
1+
from typing import TypeVar, Generic, Generator, List, Any
22

33
from twitch.api import API
44

@@ -11,6 +11,8 @@ def __init__(self, path: str, api: API, data: List[T] = None):
1111
self._path: str = path
1212
self._api: API = api
1313
self._data: List[T] = data or []
14+
self._cursor: str = None
15+
self._kwargs: Any = None
1416

1517
def __iter__(self) -> Generator[T, None, None]:
1618
for entry in self._data:

0 commit comments

Comments
 (0)