Trending videos
This commit is contained in:
		
							parent
							
								
									d17a0cca73
								
							
						
					
					
						commit
						50cc8eb956
					
				
					 7 changed files with 128 additions and 40 deletions
				
			
		
							
								
								
									
										42
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										42
									
								
								README.md
									
										
									
									
									
								
							|  | @ -2,7 +2,47 @@ | ||||||
| 
 | 
 | ||||||
| [](https://github.com/CWKevo/python-piped-api-client/actions/workflows/pytest.yml) | [](https://github.com/CWKevo/python-piped-api-client/actions/workflows/pytest.yml) | ||||||
| 
 | 
 | ||||||
| A Python API wrapper for [Piped](https://piped-docs.kavin.rocks/). | A Python API wrapper for [Piped](https://piped-docs.kavin.rocks/). This can essentially be used as an alternative way to access YouTube's API, without needing to use an API key. | ||||||
|  | 
 | ||||||
|  | ## Installation | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | pip install piped-api | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Quickstart | ||||||
|  | 
 | ||||||
|  | Getting started is very easy: | ||||||
|  | 
 | ||||||
|  | ```python | ||||||
|  | from piped_api import PipedClient | ||||||
|  | 
 | ||||||
|  | CLIENT = PipedClient() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Print out the first audio stream URL for a video: | ||||||
|  | video = CLIENT.get_video(video_id) | ||||||
|  | audio_stream = video.get_streams('audio')[0] | ||||||
|  | 
 | ||||||
|  | print(f"Audio stream URL: {audio_stream.url} ({audio_stream.mime_type})") | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Why? | ||||||
|  | 
 | ||||||
|  | This package has allowed me to start creating my open-source project, [ArchiveTube](https://github.com/CWKevo/ArchiveTube) - a scrapper and archive for YouTube content (videos and comments) - to preserve them and make them available to anyone, with ability to search for comments and videos. View hall of fame (most liked comments and videos), bring back dislikes via [ReturnYouTubeDislike.com](https://returnyoutubedislike.com), view deleted content and much more! | ||||||
|  | Google has showed us that they make YouTube own us by harvesting our data. This is also followed by non-throught out decisions, which their users aren't happy with. Let's do it the other way around this time by reclaiming our content and entertainment back & make YouTube great again! | ||||||
|  | 
 | ||||||
|  | The creation of this package was also primarily fueled by the same type of motivation [Piped has](https://piped-docs.kavin.rocks/docs/why/). | ||||||
|  | 
 | ||||||
|  | Google's API is not very easy-to-use - you must obtain some JSON thingy to use it, and it is very low-level and not very user-friendly. | ||||||
|  | On the other hand, this package accessed the [Piped API](https://piped.kavin.rocks/), which has a much more high-level API and doesn't need an account or API keys. | ||||||
|  | 
 | ||||||
|  | It is not meant to be a replacement for the official YouTube API, but it can help you to cut the strings that Google attaches to you when using their API. | ||||||
|  | 
 | ||||||
|  | ## Useful links | ||||||
|  | 
 | ||||||
|  | - [Piped's official API documentation](https://piped-docs.kavin.rocks/docs/api-documentation/) | ||||||
|  | - [Documentation for this package](https://cwkevo.github.io/python-piped-api-client/) | ||||||
| 
 | 
 | ||||||
| ## 🎁 Support me | ## 🎁 Support me | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,9 @@ from .client import PipedClient | ||||||
| from .models.comments import Comments | from .models.comments import Comments | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | from setup import __doc__ as __sdoc__ | ||||||
|  | __doc__ = __sdoc__ | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| # Supress unused-import warnings: | # Supress unused-import warnings: | ||||||
| if t.TYPE_CHECKING: | if t.TYPE_CHECKING: | ||||||
|  |  | ||||||
|  | @ -75,3 +75,16 @@ class PipedClient: | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|         return self._get_json(f"/streams/{video_id}", Video) |         return self._get_json(f"/streams/{video_id}", Video) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def get_trending(self, country_code: str='US') -> t.List[Video.RelatedStream]: | ||||||
|  |         """ | ||||||
|  |             Obtains trending videos for a specific country. If there are no trending videos (or `country_code` is invalid), | ||||||
|  |             an empty list is returned. | ||||||
|  | 
 | ||||||
|  |             ### Parameters: | ||||||
|  |             - `country_code` - The country code ([ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements)) to get trending videos for. This is automatically capitalized by this package, | ||||||
|  |                 since Piped for some reason doesn't accept lowercase country codes. Note: countries such as China or North Korea don't have trending videos, so they will always return an empty list. | ||||||
|  |         """ | ||||||
|  | 
 | ||||||
|  |         return [Video.RelatedStream(trending_video) for trending_video in self._get_json(f"/trending", params={'region': country_code.upper()})] | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| from re import S |  | ||||||
| import typing as t | import typing as t | ||||||
| 
 | 
 | ||||||
| from datetime import datetime, date, timedelta | from datetime import datetime, date, timedelta | ||||||
|  | @ -306,9 +305,9 @@ class Video(BasePipedModel): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     class RelatedVideo(BasePipedModel): |     class RelatedStream(BasePipedModel): | ||||||
|         """ |         """ | ||||||
|             A related video to the current video (e. g.: from the right sidebar) |             A related stream (e. g.: related video to the current one from the right sidebar, video related to/uploaded by a channel and trending video). | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|         @property |         @property | ||||||
|  | @ -411,10 +410,10 @@ class Video(BasePipedModel): | ||||||
|                 The date the related video was uploaded (as a `datetime.datetime` object). |                 The date the related video was uploaded (as a `datetime.datetime` object). | ||||||
| 
 | 
 | ||||||
|                 ### Note: |                 ### Note: | ||||||
|                 The original value was in POSIX timestamp (`Video.data['uploaded']`), but this package converts it to a `datetime.datetime` object. |                 The original value was in milliseconds since epoch (`Video.data['uploaded']`), but this package converts it to a `datetime.datetime` object. | ||||||
|             """ |             """ | ||||||
| 
 | 
 | ||||||
|             return datetime.fromtimestamp(self.data['uploaded']) |             return datetime.fromtimestamp(self.data['uploaded'] / 1000) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         @property |         @property | ||||||
|  | @ -428,12 +427,12 @@ class Video(BasePipedModel): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def related_videos(self) -> t.List[RelatedVideo]: |     def related_videos(self) -> t.List[RelatedStream]: | ||||||
|         """ |         """ | ||||||
|             List of related streams |             List of related streams | ||||||
|         """ |         """ | ||||||
| 
 | 
 | ||||||
|         return [self.RelatedVideo(video_data) for video_data in self.data['relatedVideos']] |         return [self.RelatedStream(video_data) for video_data in self.data['relatedVideos']] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								setup.py
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								setup.py
									
										
									
									
									
								
							|  | @ -31,8 +31,8 @@ __doc__ = __readme__ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| setuptools.setup( | setuptools.setup( | ||||||
|     name = 'piped_api', |     name = 'piped-api', | ||||||
|     packages = setuptools.find_packages(exclude=('tests',)), |     packages = setuptools.find_packages(exclude=('tests',), include=('piped_api',)), | ||||||
| 
 | 
 | ||||||
|     long_description=__readme__, |     long_description=__readme__, | ||||||
|     long_description_content_type='text/markdown', |     long_description_content_type='text/markdown', | ||||||
|  |  | ||||||
|  | @ -1,30 +0,0 @@ | ||||||
| from tests import CLIENT |  | ||||||
| 
 |  | ||||||
| from datetime import datetime |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def test_video(video_id: str='dQw4w9WgXcQ') -> None: |  | ||||||
|     """ |  | ||||||
|         Prints out information about a video. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     video = CLIENT.get_video(video_id) |  | ||||||
|     short_description = video.description[:100].replace('\n', '') |  | ||||||
| 
 |  | ||||||
|     print(f""" |  | ||||||
|         Video ID: {video_id} |  | ||||||
|         Title: {video.title} |  | ||||||
|         Description: {short_description}... |  | ||||||
|         Views: {video.views} |  | ||||||
| 
 |  | ||||||
|         Uploaded by: {video.uploader} |  | ||||||
|         Uploaded on: {video.upload_date} ({datetime.now().year - video.upload_date.year} years ago) |  | ||||||
| 
 |  | ||||||
|         Duration: {video.duration} |  | ||||||
|         FPS: {video.get_streams('video')[0].fps} |  | ||||||
|     """) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     test_video() |  | ||||||
							
								
								
									
										63
									
								
								tests/test_videos.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								tests/test_videos.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | import typing as t | ||||||
|  | 
 | ||||||
|  | from tests import CLIENT | ||||||
|  | from datetime import datetime | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_video(video_id: str='dQw4w9WgXcQ') -> None: | ||||||
|  |     """ | ||||||
|  |         Prints out information about a video. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     video = CLIENT.get_video(video_id) | ||||||
|  |     short_description = video.description[:100].replace('\n', '') | ||||||
|  | 
 | ||||||
|  |     print(f""" | ||||||
|  |         Video ID: {video_id} | ||||||
|  |         Title: {video.title} | ||||||
|  |         Description: {short_description}... | ||||||
|  |         Views: {video.views} | ||||||
|  | 
 | ||||||
|  |         Uploaded by: {video.uploader} | ||||||
|  |         Uploaded on: {video.upload_date} ({datetime.now().year - video.upload_date.year} years ago) | ||||||
|  | 
 | ||||||
|  |         Duration: {video.duration} | ||||||
|  |         FPS: {video.get_streams('video')[0].fps} | ||||||
|  |     """) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_trending(country_codes: t.List[str]=['US', 'SK', 'CN']) -> None: | ||||||
|  |     """ | ||||||
|  |         Prints out trending videos for a specific country. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     for country_code in country_codes: | ||||||
|  |         videos = CLIENT.get_trending(country_code) | ||||||
|  | 
 | ||||||
|  |         # Nothing ever trends in China's YouTube: | ||||||
|  |         if country_code == 'CN': | ||||||
|  |             assert len(videos) == 0 | ||||||
|  |             print("\nYes, empty list works.") | ||||||
|  | 
 | ||||||
|  |         for video in videos: | ||||||
|  |             print(f"{video.uploader_name} >> {video.title} ({video.views} views)") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_get_audio(video_id: str='dQw4w9WgXcQ') -> None: | ||||||
|  |     """ | ||||||
|  |         Prints out the first audio stream URL for a video. | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     video = CLIENT.get_video(video_id) | ||||||
|  |     audio_stream = video.get_streams('audio')[0] | ||||||
|  | 
 | ||||||
|  |     print(f"Audio stream URL: {audio_stream.url} ({audio_stream.mime_type})") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     test_video() | ||||||
|  |     test_trending() | ||||||
|  |     test_get_audio() | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue