Showing preview only (1,131K chars total). Download the full file or copy to clipboard to get everything.
Repository: sns-sdks/python-youtube
Branch: master
Commit: 1694c2c91820
Files: 309
Total size: 1.0 MB
Directory structure:
gitextract_o3azcgt3/
├── .bumpversion.cfg
├── .coveragerc
├── .github/
│ ├── hack/
│ │ ├── changelog.sh
│ │ └── version.sh
│ └── workflows/
│ ├── docs.yaml
│ ├── release.yaml
│ └── test.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.rst
├── docs/
│ ├── docs/
│ │ ├── authorization.md
│ │ ├── getting_started.md
│ │ ├── index.md
│ │ ├── installation.md
│ │ ├── introduce-new-structure.md
│ │ └── usage/
│ │ ├── work-with-api.md
│ │ └── work-with-client.md
│ └── mkdocs.yml
├── examples/
│ ├── README.md
│ ├── __init__.py
│ ├── apis/
│ │ ├── __init__.py
│ │ ├── channel_videos.py
│ │ ├── get_all_videos_id_with_channel_by_search.py
│ │ ├── get_subscription_with_oauth.py
│ │ └── oauth_flow.py
│ └── clients/
│ ├── __init__.py
│ ├── channel_info.py
│ ├── oauth_flow.py
│ ├── oauth_refreshing.py
│ └── upload_video.py
├── pyproject.toml
├── pytest.ini
├── pyyoutube/
│ ├── __init__.py
│ ├── __version__.py
│ ├── api.py
│ ├── client.py
│ ├── error.py
│ ├── media.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── activity.py
│ │ ├── auth.py
│ │ ├── base.py
│ │ ├── caption.py
│ │ ├── category.py
│ │ ├── channel.py
│ │ ├── channel_banner.py
│ │ ├── channel_section.py
│ │ ├── comment.py
│ │ ├── comment_thread.py
│ │ ├── common.py
│ │ ├── i18n.py
│ │ ├── member.py
│ │ ├── memberships_level.py
│ │ ├── mixins.py
│ │ ├── playlist.py
│ │ ├── playlist_item.py
│ │ ├── search_result.py
│ │ ├── subscription.py
│ │ ├── video.py
│ │ ├── video_abuse_report_reason.py
│ │ └── watermark.py
│ ├── resources/
│ │ ├── __init__.py
│ │ ├── activities.py
│ │ ├── base_resource.py
│ │ ├── captions.py
│ │ ├── channel_banners.py
│ │ ├── channel_sections.py
│ │ ├── channels.py
│ │ ├── comment_threads.py
│ │ ├── comments.py
│ │ ├── i18n_languages.py
│ │ ├── i18n_regions.py
│ │ ├── members.py
│ │ ├── membership_levels.py
│ │ ├── playlist_items.py
│ │ ├── playlists.py
│ │ ├── search.py
│ │ ├── subscriptions.py
│ │ ├── thumbnails.py
│ │ ├── video_abuse_report_reasons.py
│ │ ├── video_categories.py
│ │ ├── videos.py
│ │ └── watermarks.py
│ ├── utils/
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ └── params_checker.py
│ └── youtube_utils.py
├── testdata/
│ ├── apidata/
│ │ ├── abuse_reasons/
│ │ │ └── abuse_reason.json
│ │ ├── access_token.json
│ │ ├── activities/
│ │ │ ├── activities_by_channel_p1.json
│ │ │ ├── activities_by_channel_p2.json
│ │ │ ├── activities_by_mine_p1.json
│ │ │ └── activities_by_mine_p2.json
│ │ ├── captions/
│ │ │ ├── captions_by_video.json
│ │ │ ├── captions_filter_by_id.json
│ │ │ ├── insert_response.json
│ │ │ └── update_response.json
│ │ ├── categories/
│ │ │ ├── guide_categories_by_region.json
│ │ │ ├── guide_category_multi.json
│ │ │ ├── guide_category_single.json
│ │ │ ├── video_category_by_region.json
│ │ │ ├── video_category_multi.json
│ │ │ └── video_category_single.json
│ │ ├── channel_banners/
│ │ │ └── insert_response.json
│ │ ├── channel_info_multi.json
│ │ ├── channel_info_single.json
│ │ ├── channel_sections/
│ │ │ ├── channel_sections_by_channel.json
│ │ │ ├── channel_sections_by_id.json
│ │ │ ├── channel_sections_by_ids.json
│ │ │ └── insert_resp.json
│ │ ├── channels/
│ │ │ ├── info.json
│ │ │ ├── info_multiple.json
│ │ │ └── update_resp.json
│ │ ├── client_secrets/
│ │ │ ├── client_secret_installed_bad.json
│ │ │ ├── client_secret_installed_good.json
│ │ │ ├── client_secret_unsupported.json
│ │ │ └── client_secret_web.json
│ │ ├── comment_threads/
│ │ │ ├── comment_thread_single.json
│ │ │ ├── comment_threads_all_to_me.json
│ │ │ ├── comment_threads_by_channel.json
│ │ │ ├── comment_threads_by_video_paged_1.json
│ │ │ ├── comment_threads_by_video_paged_2.json
│ │ │ ├── comment_threads_multi.json
│ │ │ ├── comment_threads_with_search.json
│ │ │ └── insert_response.json
│ │ ├── comments/
│ │ │ ├── comments_by_parent_paged_1.json
│ │ │ ├── comments_by_parent_paged_2.json
│ │ │ ├── comments_multi.json
│ │ │ ├── comments_single.json
│ │ │ └── insert_response.json
│ │ ├── error_permission_resp.json
│ │ ├── i18ns/
│ │ │ ├── language_res.json
│ │ │ └── regions_res.json
│ │ ├── members/
│ │ │ ├── members_data.json
│ │ │ └── membership_levels.json
│ │ ├── playlist_items/
│ │ │ ├── insert_response.json
│ │ │ ├── playlist_items_filter_video.json
│ │ │ ├── playlist_items_multi.json
│ │ │ ├── playlist_items_paged_1.json
│ │ │ ├── playlist_items_paged_2.json
│ │ │ └── playlist_items_single.json
│ │ ├── playlists/
│ │ │ ├── insert_response.json
│ │ │ ├── playlists_mine.json
│ │ │ ├── playlists_multi.json
│ │ │ ├── playlists_paged_1.json
│ │ │ ├── playlists_paged_2.json
│ │ │ └── playlists_single.json
│ │ ├── search/
│ │ │ ├── search_by_developer.json
│ │ │ ├── search_by_event.json
│ │ │ ├── search_by_keywords_p1.json
│ │ │ ├── search_by_keywords_p2.json
│ │ │ ├── search_by_location.json
│ │ │ ├── search_by_mine.json
│ │ │ ├── search_by_related_video.json
│ │ │ ├── search_channels.json
│ │ │ └── search_videos_by_channel.json
│ │ ├── subscriptions/
│ │ │ ├── insert_response.json
│ │ │ ├── subscription_zero.json
│ │ │ ├── subscriptions_by_channel_p1.json
│ │ │ ├── subscriptions_by_channel_p2.json
│ │ │ ├── subscriptions_by_channel_with_filter.json
│ │ │ ├── subscriptions_by_id.json
│ │ │ ├── subscriptions_by_mine_filter.json
│ │ │ ├── subscriptions_by_mine_p1.json
│ │ │ └── subscriptions_by_mine_p2.json
│ │ ├── user_profile.json
│ │ └── videos/
│ │ ├── get_rating_response.json
│ │ ├── insert_response.json
│ │ ├── videos_chart_paged_1.json
│ │ ├── videos_chart_paged_2.json
│ │ ├── videos_info_multi.json
│ │ ├── videos_info_single.json
│ │ ├── videos_myrating_paged_1.json
│ │ └── videos_myrating_paged_2.json
│ ├── error_response.json
│ ├── error_response_simple.json
│ └── modeldata/
│ ├── abuse_report_reason/
│ │ ├── abuse_reason.json
│ │ └── abuse_reason_res.json
│ ├── activities/
│ │ ├── activity.json
│ │ ├── activity_contentDetails.json
│ │ ├── activity_response.json
│ │ └── activity_snippet.json
│ ├── captions/
│ │ ├── caption.json
│ │ ├── caption_response.json
│ │ └── caption_snippet.json
│ ├── categories/
│ │ ├── guide_category_info.json
│ │ ├── guide_category_response.json
│ │ ├── video_category_info.json
│ │ └── video_category_response.json
│ ├── channel_sections/
│ │ ├── channel_section_info.json
│ │ └── channel_section_response.json
│ ├── channels/
│ │ ├── channel_api_response.json
│ │ ├── channel_branding_settings.json
│ │ ├── channel_content_details.json
│ │ ├── channel_info.json
│ │ ├── channel_snippet.json
│ │ ├── channel_statistics.json
│ │ ├── channel_status.json
│ │ └── channel_topic_details.json
│ ├── comments/
│ │ ├── comment_api_response.json
│ │ ├── comment_info.json
│ │ ├── comment_snippet.json
│ │ ├── comment_thread_api_response.json
│ │ ├── comment_thread_info.json
│ │ ├── comment_thread_replies.json
│ │ └── comment_thread_snippet.json
│ ├── common/
│ │ ├── thumbnail_info.json
│ │ └── thumbnails_info.json
│ ├── i18ns/
│ │ ├── language_info.json
│ │ ├── language_res.json
│ │ ├── region_info.json
│ │ └── region_res.json
│ ├── members/
│ │ ├── member_info.json
│ │ └── membership_level.json
│ ├── playlist_items/
│ │ ├── playlist_item_api_response.json
│ │ ├── playlist_item_content_details.json
│ │ ├── playlist_item_info.json
│ │ ├── playlist_item_snippet.json
│ │ └── playlist_item_status.json
│ ├── playlists/
│ │ ├── playlist_api_response.json
│ │ ├── playlist_content_details.json
│ │ ├── playlist_info.json
│ │ ├── playlist_snippet.json
│ │ └── playlist_status.json
│ ├── search_result/
│ │ ├── search_result.json
│ │ ├── search_result_api_response.json
│ │ ├── search_result_id.json
│ │ └── search_result_snippet.json
│ ├── subscriptions/
│ │ ├── contentDetails.json
│ │ ├── resp.json
│ │ ├── snippet.json
│ │ ├── subscriberSnippet.json
│ │ └── subscription.json
│ ├── users/
│ │ ├── access_token.json
│ │ └── user_profile.json
│ └── videos/
│ ├── video_api_response.json
│ ├── video_category_info.json
│ ├── video_content_details.json
│ ├── video_info.json
│ ├── video_paid_product_placement_details.json
│ ├── video_recording_details.json
│ ├── video_snippet.json
│ ├── video_statistics.json
│ ├── video_status.json
│ └── video_topic_details.json
└── tests/
├── __init__.py
├── apis/
│ ├── __init__.py
│ ├── test_activities.py
│ ├── test_auth.py
│ ├── test_captions.py
│ ├── test_categories.py
│ ├── test_channel_sections.py
│ ├── test_channels.py
│ ├── test_comment_threads.py
│ ├── test_comments.py
│ ├── test_i18ns.py
│ ├── test_members.py
│ ├── test_playlist_items.py
│ ├── test_playlists.py
│ ├── test_search.py
│ ├── test_subscriptions.py
│ ├── test_video_abuse_reason.py
│ └── test_videos.py
├── clients/
│ ├── __init__.py
│ ├── base.py
│ ├── test_activities.py
│ ├── test_captions.py
│ ├── test_channel_banners.py
│ ├── test_channel_sections.py
│ ├── test_channels.py
│ ├── test_client.py
│ ├── test_comment_threads.py
│ ├── test_comments.py
│ ├── test_i18n.py
│ ├── test_media.py
│ ├── test_members.py
│ ├── test_membership_levels.py
│ ├── test_playlist_items.py
│ ├── test_playlists.py
│ ├── test_search.py
│ ├── test_subscriptions.py
│ ├── test_thumbnails.py
│ ├── test_video_abuse_report_reasons.py
│ ├── test_video_categories.py
│ ├── test_videos.py
│ └── test_watermarks.py
├── conftest.py
├── models/
│ ├── __init__.py
│ ├── test_abuse_reason.py
│ ├── test_activities.py
│ ├── test_auth_models.py
│ ├── test_captions.py
│ ├── test_category.py
│ ├── test_channel.py
│ ├── test_channel_sections.py
│ ├── test_comments.py
│ ├── test_i18n_models.py
│ ├── test_members.py
│ ├── test_playlist.py
│ ├── test_playlist_item.py
│ ├── test_search_result.py
│ ├── test_subscriptions.py
│ └── test_videos.py
├── test_error_handling.py
├── test_youtube_utils.py
└── utils/
├── __init__.py
└── test_params_checker.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .bumpversion.cfg
================================================
[bumpversion]
current_version = 0.9.9
commit = True
tag = True
[bumpversion:file:pyyoutube/__version__.py]
[bumpversion:file:pyproject.toml]
================================================
FILE: .coveragerc
================================================
[run]
omit = pyyoutube/__version__.py
================================================
FILE: .github/hack/changelog.sh
================================================
#!/bin/sh
MARKER_PREFIX="## Version"
VERSION=$(echo "$1" | sed 's/^v//g')
IFS=''
found=0
while read -r "line"; do
# If not found and matching heading
if [ $found -eq 0 ] && echo "$line" | grep -q "$MARKER_PREFIX $VERSION"; then
echo "$line"
found=1
continue
fi
# If needed version if found, and reaching next delimter - stop
if [ $found -eq 1 ] && echo "$line" | grep -q -E "$MARKER_PREFIX [[:digit:]]+\.[[:digit:]]+\.[[:digit:]]"; then
found=0
break
fi
# Keep printing out lines as no other version delimiter found
if [ $found -eq 1 ]; then
echo "$line"
fi
done < CHANGELOG.md
================================================
FILE: .github/hack/version.sh
================================================
#!/bin/sh
LATEST_TAG_REV=$(git rev-list --tags --max-count=1)
LATEST_COMMIT_REV=$(git rev-list HEAD --max-count=1)
if [ -n "$LATEST_TAG_REV" ]; then
LATEST_TAG=$(git describe --tags "$(git rev-list --tags --max-count=1)")
else
LATEST_TAG="v0.0.0"
fi
if [ "$LATEST_TAG_REV" != "$LATEST_COMMIT_REV" ]; then
echo "$LATEST_TAG+$(git rev-list HEAD --max-count=1 --abbrev-commit)"
else
echo "$LATEST_TAG"
fi
================================================
FILE: .github/workflows/docs.yaml
================================================
name: Publish docs via GitHub Pages
on:
push:
branches:
- master
jobs:
build:
name: Deploy docs
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy docs
uses: mhausenblas/mkdocs-deploy-gh-pages@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CONFIG_FILE: docs/mkdocs.yml
EXTRA_PACKAGES: build-base
================================================
FILE: .github/workflows/release.yaml
================================================
name: Publish Pypi
on:
push:
tags:
- 'v*.*.*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and publish to pypi
uses: JRubics/poetry-publish@v1.17
with:
pypi_token: ${{ secrets.PYPI_TOKEN }}
- name: Generate Changelog
run: |
VERSION=$(.github/hack/version.sh)
.github/hack/changelog.sh $VERSION > NEW-VERSION-CHANGELOG.md
- name: Publish
uses: softprops/action-gh-release@v1
with:
body_path: NEW-VERSION-CHANGELOG.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/test.yaml
================================================
name: Test
on:
pull_request:
push:
branches:
- master
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
include:
- python-version: '3.12'
update-coverage: true
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ matrix.python-version }}-poetry-${{ hashFiles('pyproject.toml') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip poetry
poetry install
- name: Test with pytest
run: |
poetry run pytest
- name: Upload coverage to Codecov
if: ${{ matrix.update-coverage }}
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
lint:
name: black
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: lintenv-v2
- name: Install dependencies
run: python -m pip install --upgrade pip black
- name: Black test
run: make lint-check
================================================
FILE: .gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
poetry.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
# PyCharm
.idea/
# for git commitizen
node_modules/
package-lock.json
package.json
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
## Version 0.9.9 (2026-04-17)
### What's New
- Add new part field `paidProductPlacementDetail` for video resource. Thanks for [@sarod](https://github.com/sarod)
- Add new method to subscribe or unsubscribe to a YouTube channel's push notifications via PubSubHubbub.
- Upgrade to python 3.9
## Version 0.9.8 (2025-08-22)
### What's New
- Fix dependencies and update docs. Thanks for [@sagarvora](https://github.com/sagarvora)
## Version 0.9.7 (2024-10-28)
### What's New
- Fix dependencies.
## Version 0.9.6 (2024-09-09)
### What's New
-Add new part field `recordingDetails` for video resource. Thanks for [@vmx](https://github.com/vmx)
## Version 0.9.5 (2024-08-09)
### What's New
- Make video regionRestriction fields to Optional. Thanks for [@pidi3000](https://github.com/pidi3000)
- Modify some examples. Thanks for [@pidi3000](https://github.com/pidi3000)
- fix enf_parts for part with whitespaces. Thanks for [@pidi3000](https://github.com/pidi3000)
## Version 0.9.4 (2024-02-18)
### What's New
- Add new parameter `for_handle` to get channel by handle.
- fix some wrong error message.
## Version 0.9.3 (2023-11-22)
### What's New
- Add initial client with client_secret file. Thanks for [@pidi3000](https://github.com/pidi3000)
## Version 0.9.2 (2023-09-26)
### What's New
- Add new parameter for search method
- Mark some parameter or method to be deprecated.
## Version 0.9.1 (2023-07-19)
### What's New
- upgrade poetry. Thanks for [@blaggacao](https://github.com/blaggacao)
## Version 0.9.0 (2022-12-26)
### What's New
- Introduce new `Client` to operate YouTube DATA API. [#120](https://github.com/sns-sdks/python-youtube/issues/120).
- More example to show library usage.
## Version 0.8.3 (2022-10-17)
### What's New
- Add parts for video, thanks for [@Omer](https://github.com/dusking)
## Version 0.8.2 (2022-03-16)
### What's New
- Update OAuthorize functions.
- Update for examples.
## Version 0.8.1 (2021-05-14)
### Deprecation
Detail at: https://developers.google.com/youtube/v3/revision_history#may-12,-2021
- Remove channel resource in brandingSettings for channel.
- Remove localizations,targeting resource and some snippet resource for channelSection.
- Remove tags in snippet for playlist.
### Broken Change
Methods `get_channel_sections_by_channel`, `get_channel_section_by_id` has remove parameter `hl`.
## Version 0.8.0
### Broken Change
Modify the auth flow methods.
### What's New
1. add python3.9 tests
2. New docs
## Version 0.7.0
### What's New
1. Add api methods for members and membership levels
2. Add more examples for api
3. Add fields for playlist item api
4. fix some.
## Version 0.6.1
### What's New
Remove deprecated api.
## Version 0.6.0
### What's New
Provide remain get apis. like activities, captions, channel_sections, i18n, video_abuse_report_reason, search resource and so on.
You can see the `README`_ to get more detail for those api.
## Version 0.5.3
### What's New
Provide the page token parameter to skip data have retrieved.
This for follow api methods
```python
api.get_playlists()
api.get_playlist_items()
api.get_videos_by_chart()
api.get_videos_by_myrating()
api.get_comment_threads()
api.get_comments()
api.get_subscription_by_channel()
api.get_subscription_by_me()
```
example
```
In[1]: r = api.get_subscription_by_channel(channel_id="UCAuUUnT6oDeKwE6v1NGQxug", limit=5, count=None, page_token="CAUQAA")
In[2]:r.prevPageToken
Out[2]: 'CAUQAQ'
```
## Version 0.5.2
### What's New
Now you can use authorized access token to get your subscriptions.
You can to the demo [A demo for get my subscription](https://github.com/sns-sdks/python-youtube/blob/master/examples/subscription.py) to see simple usage.
Or you can see the [subscriptions usage](https://github.com/sns-sdks/python-youtube/blob/master/README.rst#subscriptions) docs.
#43 add api for get my subscriptions
#41 add api for channel subscriptions
## Version 0.5.1
### What's New
Now some apis can get all target items just by one method call.
For example, you can get playlist's all items by follow call
```
In [1]: r = api.get_playlist_items(playlist_id="PLWz5rJ2EKKc_xXXubDti2eRnIKU0p7wHd", parts=["id", "snippet"], count=None)
In [2]: r.pageInfo
Out[2]: PageInfo(totalResults=73, resultsPerPage=50)
In [3]: len(r.items)
Out[4]: 73
```
You can see the [README](https://github.com/sns-sdks/python-youtube/blob/master/README.rst) to find which methods support this.
## Version 0.5.0
### **Broken Change**
Now introduce new model ApiResponse representing the response from youtube, so previous usage has been invalidated.
You need to read the docs [README](https://github.com/sns-sdks/python-youtube/blob/master/README.rst) to get the simple new usage.
### What's New
Split some method into multiple usage, for example get video has been split three methods:
* api.get_video_by_id()
* api.get_videos_by_chart()
* api.get_videos_by_myrating()
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 sns-sdks
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Makefile
================================================
all: help clean lint test
.PHONY: all
help:
@echo " env install all dependencies"
@echo " clean remove unwanted stuff"
@echo " docs build documentation"
@echo " lint check style with black"
@echo " test run tests with cov"
env:
pip install --upgrade pip
pip install poetry
poetry install
clean: clean-build clean-pyc clean-test
clean-build:
rm -fr build/
rm -fr dist/
rm -fr .eggs/
find . -name '*.egg-info' -exec rm -fr {} +
find . -name '*.egg' -exec rm -f {} +
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
find . -name '__pycache__' -exec rm -fr {} +
clean-test:
rm -fr .pytest_cache
rm -f .coverage
rm -fr htmlcov/
docs:
$(MAKE) -C docs html
lint:
black .
lint-check:
black --check .
test:
pytest -s
tests-html:
pytest -s --cov-report term --cov-report html
# v0.1.0 -> v0.2.0
bump-minor:
bump2version minor
# v0.1.0 -> v0.1.1
bump-patch:
bump2version patch
================================================
FILE: README.rst
================================================
Python YouTube
A Python wrapper for the YouTube Data API V3.
.. image:: https://github.com/sns-sdks/python-youtube/workflows/Test/badge.svg
:target: https://github.com/sns-sdks/python-youtube/actions
.. image:: https://img.shields.io/badge/Docs-passing-brightgreen
:target: https://sns-sdks.github.io/python-youtube/
:alt: Documentation Status
.. image:: https://codecov.io/gh/sns-sdks/python-youtube/branch/master/graph/badge.svg
:target: https://codecov.io/gh/sns-sdks/python-youtube
.. image:: https://img.shields.io/pypi/v/python-youtube.svg
:target: https://img.shields.io/pypi/v/python-youtube
======
THANKS
======
Inspired by `Python-Twitter <https://github.com/bear/python-twitter>`_.
Thanks a lot to Python-Twitter Developers.
============
Introduction
============
This library provides an easy way to use the YouTube Data API V3.
..
We have recently been working on the new structure for the library. `Read docs <docs/docs/introduce-new-structure.md>`_ to get more detail.
=============
Documentation
=============
You can view the latest ``python-youtube`` documentation at: https://sns-sdks.github.io/python-youtube/.
Also view the full ``YouTube DATA API`` docs at: https://developers.google.com/youtube/v3/docs/.
==========
Installing
==========
You can install this lib from PyPI:
.. code:: shell
pip install --upgrade python-youtube
# ✨🍰✨
=====
Using
=====
The library covers all resource methods, including ``insert``,``update``, and so on.
We recommend using the ``pyyoutube.Client`` to operate DATA API. It is more modern and feature rich than ``pyyoutube.Api``.
Work with Client
----------------
You can initialize with an api key:
.. code-block:: python
>>> from pyyoutube import Client
>>> client = Client(api_key="your api key")
To access additional data that requires authorization, you need to initialize with an access token:
.. code-block:: python
>>> from pyyoutube import Client
>>> client = Client(access_token='your access token')
You can read the docs to see how to get an access token.
Or you can ask for user to do OAuth:
.. code-block:: python
>>> from pyyoutube import Client
>>> client = Client(client_id="client key", client_secret="client secret")
>>> client.get_authorize_url()
('https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=id&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=scope&state=PyYouTube&access_type=offline&prompt=select_account', 'PyYouTube')
>>> client.generate_access_token(authorization_response="link for response")
AccessToken(access_token='token', expires_in=3599, token_type='Bearer')
Now you can use the instance to get data from YouTube.
Get channel detail:
>>> cli.channels.list(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
ChannelListResponse(kind='youtube#channelListResponse')
>>> cli.channels.list(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw", return_json=True)
{'kind': 'youtube#channelListResponse',
'etag': 'eHSYpB_FqHX8vJiGi_sLCu0jkmE',
...
}
See the `client docs <docs/docs/usage/work-with-client.md>`_, or `client examples <examples/clients>`_, for additional usage
Work with API
----------------
..
For compatibility with older code, we continue to support the old way.
You can just initialize with an api key:
.. code-block:: python
>>> from pyyoutube import Api
>>> api = Api(api_key="your api key")
To access additional data that requires authorization, you need to initialize with an access token:
.. code-block:: python
>>> from pyyoutube import Api
>>> api = Api(access_token='your access token')
You can read the docs to see how to get an access token.
Or you can ask for user to do OAuth flow:
.. code-block:: python
>>> from pyyoutube import Api
>>> api = Api(client_id="client key", client_secret="client secret")
# Get authorization url
>>> api.get_authorization_url()
('https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=id&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=scope&state=PyYouTube&access_type=offline&prompt=select_account', 'PyYouTube')
# user to do
# copy the response url
>>> api.generate_access_token(authorization_response="link for response")
AccessToken(access_token='token', expires_in=3599, token_type='Bearer')
Now you can use the instance to get data from YouTube.
Get channel detail:
.. code-block:: python
>>> channel_by_id = api.get_channel_info(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
>>> channel_by_id.items
[Channel(kind='youtube#channel', id='UC_x5XG1OV2P6uZZ5FSM9Ttw')]
>>> channel_by_id.items[0].to_dict()
{'kind': 'youtube#channel',
'etag': '"j6xRRd8dTPVVptg711_CSPADRfg/AW8QEqbNRoIJv9KuzCIg0CG6aJA"',
'id': 'UC_x5XG1OV2P6uZZ5FSM9Ttw',
'snippet': {'title': 'Google Developers',
'description': 'The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.',
'customUrl': 'googlecode',
'publishedAt': '2007-08-23T00:34:43.000Z',
'thumbnails': {'default': {'url': 'https://yt3.ggpht.com/a/AGF-l78iFtAxyRZcUBzG91kbKMES19z-zGW5KT20_g=s88-c-k-c0xffffffff-no-rj-mo',
'width': 88,
'height': 88},
'medium': {'url': 'https://yt3.ggpht.com/a/AGF-l78iFtAxyRZcUBzG91kbKMES19z-zGW5KT20_g=s240-c-k-c0xffffffff-no-rj-mo',
'width': 240,
'height': 240},
'high': {'url': 'https://yt3.ggpht.com/a/AGF-l78iFtAxyRZcUBzG91kbKMES19z-zGW5KT20_g=s800-c-k-c0xffffffff-no-rj-mo',
'width': 800,
'height': 800},
'standard': None,
'maxres': None},
'defaultLanguage': None,
'localized': {'title': 'Google Developers',
'description': 'The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.'},
'country': 'US'},
...
}
# Get json response from youtube
>>> api.get_channel_info(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw", return_json=True)
{'kind': 'youtube#channelListResponse',
'etag': '17FOkdjp-_FPTiIJXdawBS4jWtc',
...
}
See the `api docs <docs/docs/usage/work-with-api.md>`_, or `api examples <examples/apis>`_, for additional usage.
================================================
FILE: docs/docs/authorization.md
================================================
If you want to get more data for your channel, You need provide the authorization.
This doc shows how to authorize a client.
## Prerequisite
To begin with, you must know what authorization is.
You can see some information at the [Official Documentation](https://developers.google.com/youtube/v3/guides/authentication).
You will need to create an app with [Access scope](https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps#identify-access-scopes) approval by YouTube.
Once complete, you will be able to do a simple authorize with `Python-Youtube` library.
## Get authorization url
Suppose now we want to get user's permission to manage their YouTube account.
For the `Python-YouTube` library, the default scopes are:
- https://www.googleapis.com/auth/youtube
- https://www.googleapis.com/auth/userinfo.profile
You can get more scope information at [Access scopes](https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps#identify-access-scopes).
(The defailt redirect URI used in PyYoutube is `https://localhost/`)
We can now perform the following steps:
Initialize the api instance with your app credentials
```
In [1]: from pyyoutube import Client
In [2]: cli = Client(client_id="you client id", client_secret="you client secret")
In [3]: cli.get_authorize_url()
Out[3]:
('https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=client_id&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&state=PyYouTube&access_type=offline&prompt=select_account',
'PyYouTube')
```
Open your broswer of choice and copy the link returned by `get_authorize_url()` into the searchbar.
## Do authorization
On entering the URL, you will see the following:

Select the account to authorize your app to read data from.
If your app is not approved for use, you will recieve a warning. You can prevent this by adding your chosen Google account as a test member on your created OAuth application.
Otherwise, you will see the following:

You will need to click ``Advanced``, then click the ``Go to Python-YouTube (unsafe)``.

You should now see a window to select permissions granted to the application.

Click `allow` to give the permission.
You will see a Connection Error, as the link is redirecting to `localhost`. This is standard behaviour, so don't close the window or return to a previous page!
## Retrieve access token
Copy the full redicted URL from the browser address bar, and return to your original console.
```
In [4]: token = cli.generate_access_token(authorization_response="$redirect_url")
In [5]: token
Out[5]: AccessToken(access_token='access token', expires_in=3600, token_type='Bearer')
```
(Replace `$redirect_url` with the URL you copied)
You now have an access token to view your account data.
## Get your data
For example, you can get your playlists.
```
In [6]: playlists = cli.playlists.list(mine=True)
In [7]: playlists.items
Out[7]:
[Playlist(kind='youtube#playlist', id='PLBaidt0ilCManGDIKr8UVBFZwN_UvMKvS'),
Playlist(kind='youtube#playlist', id='PLBaidt0ilCMbUdj0EppB710c_X5OuCP2g')]
```
!!! note "Tips"
If you are confused, it is beneficial to read the [Authorize Requests](https://developers.google.com/youtube/v3/guides/authentication) guide first.
================================================
FILE: docs/docs/getting_started.md
================================================
This document is a simple tutorial to show how to use this library to get data from YouTube data API.
You can get the whole description for the YouTube API at [YouTube API Reference](https://developers.google.com/youtube/v3/docs/).
## Prerequisite
To begin, you need to create a [Google Project](https://console.cloud.google.com) with your google account.
Every new account has a free quota of 12 projects.
## Create your project
Click `Select a project-> NEW PROJECT` to create a new project to use our library.
Fill in the basic info and create the project.

## Enable YouTube DATA API service
Once the project created, the browser will redirect you to the project home page.
Click the `≡≡` symbol on the top left and select the `APIs & Services` tab.
You will see following info:

Click the `+ ENABLE APIS AND SERVICES` symbol, and input `YouTube DATA API` to search.

Chose the ``YouTube DATA API`` item.

Then click the `ENABLE` blue button. After a short period where the API is added to your project, the service will be activated.
## Create credentials
To use this API, you need credentials. Click `Create credentials` to get started.

You need to fill in some information to create credentials.
Just chose `YouTube DATA API v3`, `Other non-UI (e.g. cron job, daemon)` and `Public data`.
Then click the blue button `What credentials do I need?` to create.

You have now generated an api key.
Using this key, you can retrieve public YouTube data with our library
```python
from pyyoutube import Client
cli = Client(api_key="your api key")
```
Check out the [examples](https://github.com/sns-sdks/python-youtube/tree/master/examples) directory for some examples of using the library.
If you have an open source application using python-youtube, send me a link. I am very happy to add a link to it here.
If you want to get user data by OAuth. You need create the credential for ``OAuth client ID``.
You will find more information on OAth at the [Authorization](authorization.md) page.
================================================
FILE: docs/docs/index.md
================================================
# Welcome to Python-Youtube's documentation!
**A Python wrapper around for YouTube Data API.**
Author: IkarosKun <merle.liukun@gmail.com>
## Introduction
With the YouTube Data API, you can add a variety of YouTube features to your application.
Use the API to upload videos, manage playlists and subscriptions, update channel settings, and more.
This library provides a Python interface for the [YouTube DATA API](https://developers.google.com/youtube/v3).
This library has works on all Python versions 3.6 and newer.
!!! tip "Tips"
This library only supports `DATA API`, It does not support `Analytics and Reporting APIs` and `Live Streaming API`.
================================================
FILE: docs/docs/installation.md
================================================
This library supports Python 3.6 and newer.
## Dependencies
These following distributions will be installed automatically when installing Python-Youtube.
- [requests](https://2.python-requests.org/en/master/): is an elegant and simple HTTP library for Python, built for human beings.
- [Requests-OAuthlib](https://requests-oauthlib.readthedocs.io/en/latest/): uses the Python Requests and OAuthlib libraries to provide an easy-to-use Python interface for building OAuth1 and OAuth2 clients.
- [isodate](https://pypi.org/project/isodate/): implements ISO 8601 date, time and duration parsing.
## Installation
You can install this library from **PyPI**
```shell
$ pip install --upgrade python-youtube
```
You can also build this library from source
```shell
$ git clone https://github.com/sns-sdks/python-youtube.git
$ cd python-youtube
$ make env
$ make build
```
## Testing
Run `make env` after you have installed the project requirements.
Once completed, you can run code tests with
```shell
$ make tests-html
```
================================================
FILE: docs/docs/introduce-new-structure.md
================================================
This doc will show you the new api structure for this library.
## Brief
To make the package easier to maintain and easy to use. We have shifted to using classes for different YouTube resources in an easier, higher-level, programming experience.

In this structure, every resource has a self class.
## Simple usage
### Initial Client
```python
from pyyoutube import Client
client = Client(api_key="your api key")
```
### Get data.
for example to get channel data.
```python
resp = client.channels.list(
parts=["id", "snippet"],
channel_id="UCa-vrCLQHviTOVnEKDOdetQ"
)
# resp output
# ChannelListResponse(kind='youtube#channelListResponse')
# resp.items[0].id output
# UCa-vrCLQHviTOVnEKDOdetQ
```
================================================
FILE: docs/docs/usage/work-with-api.md
================================================
# Work with Api
!!! note "Tips"
This is the previous version to operate YouTube DATA API.
We recommend using the latest version of methods to operate YouTube DATA API.
The API is exposed via the ``pyyoutube.Api`` class.
## INSTANTIATE
We provide two method to create instances of the ``pyyoutube.Api``.
You can just initialize with an api key.
```
>>> from pyyoutube import Api
>>> api = Api(api_key="your api key")
```
If you want to get authorization data, you will need to initialize with an access token.
```
>>> from pyyoutube import Api
>>> api = Api(access_token='your api key')
```
You can read the docs to see how to get an access token.
Or you can ask for the user to do oauth flow:
```
>>> from pyyoutube import Api
>>> api = Api(client_id="client key", client_secret="client secret")
# Get authorization url
>>> api.get_authorization_url()
# ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=id&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=scope&state=PyYouTube&access_type=offline&prompt=select_account', 'PyYouTube')
# user to do
# copy the response url
>>> api.generate_access_token(authorization_response="link for response")
# AccessToken(access_token='token', expires_in=3599, token_type='Bearer')
```
## Usage
Now you can use the instance to get data from YouTube.
### CHANNEL DATA
The library provides several ways to get a channels data.
If a channel is not found, the property ``items`` will return an empty list.
You can use channel id:
```
>>> channel_by_id = api.get_channel_info(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
>>> channel_by_id.items
[Channel(kind='youtube#channel', id='UC_x5XG1OV2P6uZZ5FSM9Ttw')]
>>> channel_by_id.items[0].to_dict()
{'kind': 'youtube#channel',
'etag': '"j6xRRd8dTPVVptg711_CSPADRfg/AW8QEqbNRoIJv9KuzCIg0CG6aJA"',
'id': 'UC_x5XG1OV2P6uZZ5FSM9Ttw',
'snippet': {'title': 'Google Developers',
'description': 'The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.',
'customUrl': 'googlecode',
'publishedAt': '2007-08-23T00:34:43.000Z',
'thumbnails': {'default': {'url': 'https://yt3.ggpht.com/a/AGF-l78iFtAxyRZcUBzG91kbKMES19z-zGW5KT20_g=s88-c-k-c0xffffffff-no-rj-mo',
'width': 88,
'height': 88},
'medium': {'url': 'https://yt3.ggpht.com/a/AGF-l78iFtAxyRZcUBzG91kbKMES19z-zGW5KT20_g=s240-c-k-c0xffffffff-no-rj-mo',
'width': 240,
'height': 240},
'high': {'url': 'https://yt3.ggpht.com/a/AGF-l78iFtAxyRZcUBzG91kbKMES19z-zGW5KT20_g=s800-c-k-c0xffffffff-no-rj-mo',
'width': 800,
'height': 800},
'standard': None,
'maxres': None},
'defaultLanguage': None,
'localized': {'title': 'Google Developers',
'description': 'The Google Developers channel features talks from events, educational series, best practices, tips, and the latest updates across our products and platforms.'},
'country': 'US'},
...
}
```
To get multiple channels, you can pass any of: a string containing comma-seperated ids; or an enumarable (list, tuple, or set) of ids
Many other methods also provide this functionality.
with ids:
```
>>> channel_by_ids = api.get_channel_info(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw,UCa-vrCLQHviTOVnEKDOdetQ")
>>> channel_by_ids.items
[Channel(kind='youtube#channel', id='UC_x5XG1OV2P6uZZ5FSM9Ttw'),
Channel(kind='youtube#channel', id='UCa-vrCLQHviTOVnEKDOdetQ')]
```
You can also use a channel name:
```
>>> channel_by_username = api.get_channel_info(for_username="GoogleDevelopers")
>>> channel_by_username.items[0]
Channel(kind='youtube#channel', id='UC_x5XG1OV2P6uZZ5FSM9Ttw')
```
If you have authorized your client, you can get your channels directly:
```
>>> channel_by_mine = api_with_authorization.get_channel_info(mine=True)
>>> channel_by_mine.items[0]
Channel(kind='youtube#channel', id='UCa-vrCLQHviTOVnEKDOdetQ')
```
!!! note "Tips"
To get your channel, you must do authorization first, otherwise you will get an error.
### PLAYLIST
There are methods to get playlists by playlist id, channel id, or get your own playlists.
Get playlists by id:
```
>>> playlists_by_id = api.get_playlist_by_id(playlist_id="PLOU2XLYxmsIKpaV8h0AGE05so0fAwwfTw")
>>> playlists_by_id.items
[Playlist(kind='youtube#playlist', id='PLOU2XLYxmsIKpaV8h0AGE05so0fAwwfTw')]
```
Get playlists by channel (If you want to get all playlists for the target channels, provide the
parameter `count=None`):
```
>>> playlists_by_channel = api.get_playlists(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
>>> playlists_by_channel.items
[Playlist(kind='youtube#playlist', id='PLOU2XLYxmsIKpaV8h0AGE05so0fAwwfTw'),
Playlist(kind='youtube#playlist', id='PLOU2XLYxmsIJO83u2UmyC8ud41AvUnhgj'),
Playlist(kind='youtube#playlist', id='PLOU2XLYxmsILfV1LiUhDjbh1jkFjQWrYB'),
Playlist(kind='youtube#playlist', id='PLOU2XLYxmsIKNr3Wfhm8o0TSojW7hEPPY'),
Playlist(kind='youtube#playlist', id='PLOU2XLYxmsIJ8ItHmK4bRlY4GCzMgXLAJ')]
```
Get your playlists (this requires authorization):
```
>>> playlists_by_mine = api.get_playlists(mine=True)
```
### PLAYLIST ITEM
Similarly, you can get playlist items by playlist item id or playlist id.
Get playlist items by id:
```
>>> playlist_item_by_id = api.get_playlist_item_by_id(playlist_item_id="UExPVTJYTFl4bXNJS3BhVjhoMEFHRTA"
... "1c28wZkF3d2ZUdy41NkI0NEY2RDEwNTU3Q0M2")
>>> playlist_item_by_id.items
[PlaylistItem(kind='youtube#playlistItem', id='UExPVTJYTFl4bXNJS3BhVjhoMEFHRTA1c28wZkF3d2ZUdy41NkI0NEY2RDEwNTU3Q0M2')]
```
Get playlist items by playlist id (If you want to get return all items in a playlist, provide the
parameter `count=None`):
```
>>> playlist_item_by_playlist = api.get_playlist_items(playlist_id="PLOU2XLYxmsIKpaV8h0AGE05so0fAwwfTw", count=2)
>>> playlist_item_by_playlist.items
[PlaylistItem(kind='youtube#playlistItem', id='UExPVTJYTFl4bXNJS3BhVjhoMEFHRTA1c28wZkF3d2ZUdy41NkI0NEY2RDEwNTU3Q0M2'),
PlaylistItem(kind='youtube#playlistItem', id='UExPVTJYTFl4bXNJS3BhVjhoMEFHRTA1c28wZkF3d2ZUdy4yODlGNEE0NkRGMEEzMEQy')]
>>> playlist_item_by_id.items[0].snippet.resourceId
ResourceId(kind='youtube#video', videoId='CvTApw9X8aA')
```
### VIDEO
You can get a video's information by several methods.
Get videos by video id(s):
```
>>> video_by_id = api.get_video_by_id(video_id="CvTApw9X8aA")
>>> video_by_id
VideoListResponse(kind='youtube#videoListResponse')
>>> video_by_id.items
[Video(kind='youtube#video', id='CvTApw9X8aA')]
```
Get videos by chart (If you want to get all videos, just provide the parameter `count=None`):
```
>>> video_by_chart = api.get_videos_by_chart(chart="mostPopular", region_code="US", count=2)
>>> video_by_chart.items
[Video(kind='youtube#video', id='RwnN2FVaHmw'),
Video(kind='youtube#video', id='hDeuSfo_Ys0')]
```
Get videos by your rating (this requires authorization. If you also want to get all videos, provide the
parameter `count=None`):
```
>>> videos_by_rating = api.get_videos_by_myrating(rating="like", count=2)
```
### COMMENT THREAD
You can get comment thread information by id or by a filter.
Get comment thread by id(s):
```
>>> ct_by_id = api.get_comment_thread_by_id(comment_thread_id='Ugz097FRhsQy5CVhAjp4AaABAg,UgzhytyP79_Pwa
... Dd4UB4AaABAg')
>>> ct_by_id.items
[CommentThread(kind='youtube#commentThread', id='Ugz097FRhsQy5CVhAjp4AaABAg'),
CommentThread(kind='youtube#commentThread', id='UgzhytyP79_PwaDd4UB4AaABAg')]
```
Get all comment threads related to a channel (including comment threads for the channel's video. If you want to get
all comment threads, provide the parameter `count=None`):
```
>>> ct_by_all = api.get_comment_threads(all_to_channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw", count=2)
>>> ct_by_all.items
[CommentThread(kind='youtube#commentThread', id='UgwlB_Cza9WtzUWahYN4AaABAg'),
CommentThread(kind='youtube#commentThread', id='UgyvoQJ2LsxCBwGEpMB4AaABAg')]
```
Get comment threads only for the channel (If you want to get all comment threads, provide the
parameter `count=None`):
```
>>> ct_by_channel = api.get_comment_threads(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw", count=2)
>>> ct_by_channel.items
[CommentThread(kind='youtube#commentThread', id='UgyUBI0HsgL9emxcZpR4AaABAg'),
CommentThread(kind='youtube#commentThread', id='Ugzi3lkqDPfIOirGFLh4AaABAg')]
```
Get comment threads only for the video (If you want to get all comment threads, provide the
parameter `count=None`):
```
>>> ct_by_video = api.get_comment_threads(video_id="D-lhorsDlUQ", count=2)
>>> ct_by_video.items
[CommentThread(kind='youtube#commentThread', id='UgydxWWoeA7F1OdqypJ4AaABAg'),
CommentThread(kind='youtube#commentThread', id='UgxKREWxIgDrw8w2e_Z4AaABAg')]
```
### COMMENT
You can get comment information by id or use the top-level comment id to get replies.
!!! note "Tips"
The reply has the same structure as a comment.
Get comments by id(s):
```
>>> comment_by_id = api.get_comment_by_id(comment_id='UgxKREWxIgDrw8w2e_Z4AaABAg,UgyrVQaFfEdvaSzstj14AaABAg')
>>> comment_by_id.items
[Comment(kind='youtube#comment', id='UgxKREWxIgDrw8w2e_Z4AaABAg', snippet=CommentSnippet(authorDisplayName='Hieu Nguyen', likeCount=0)),
Comment(kind='youtube#comment', id='UgyrVQaFfEdvaSzstj14AaABAg', snippet=CommentSnippet(authorDisplayName='Mani Kanta', likeCount=0))]
```
Get replies by comment id (If you want to get all comments, just provide the parameter `count=None`):
```
>>> comment_by_parent = api.get_comments(parent_id="UgwYjZXfNCUTKPq9CZp4AaABAg")
>>> comment_by_parent.items
[Comment(kind='youtube#comment', id='UgwYjZXfNCUTKPq9CZp4AaABAg.8yxhlQJogG18yz_cXK9Kcj', snippet=CommentSnippet(authorDisplayName='Marlon López', likeCount=0))]
```
### VIDEO CATEGORY
You can get video category with id or region.
Get video categories with id(s):
```
>>> video_category_by_id = api.get_video_categories(category_id="17,18")
>>> video_category_by_id.items
[VideoCategory(kind='youtube#videoCategory', id='17'),
VideoCategory(kind='youtube#videoCategory', id='18')]
```
Get video categories with region code:
```
>>> video_categories_by_region = api.get_video_categories(region_code="US")
>>> video_categories_by_region.items
[VideoCategory(kind='youtube#videoCategory', id='1'),
VideoCategory(kind='youtube#videoCategory', id='2'),
VideoCategory(kind='youtube#videoCategory', id='10'),
VideoCategory(kind='youtube#videoCategory', id='15'),
...]
```
### SUBSCRIPTIONS
You can get subscription information by id, by point channel, or your own.
!!! note "Tips"
If you want to get the non-public subscriptions, you need to authorize and obtain the access token first.
See the demo [A demo for get my subscription](examples/subscription.py).
To get subscription info by id(s), your token needs to have the permission for the subscriptions belonging to a
channel or user:
```
>>> r = api.get_subscription_by_id(
... subscription_id=[
... "zqShTXi-2-Tx7TtwQqhCBwViE_j9IEgnmRmPnqJljxo",
... "zqShTXi-2-Rya5uUxEp3ZsPI3fZrFQnSXNQCwvHBGGo"])
>>> r
SubscriptionListResponse(kind='youtube#subscriptionListResponse')
>>> r.items
[Subscription(kind='youtube#subscription', id='zqShTXi-2-Tx7TtwQqhCBwViE_j9IEgnmRmPnqJljxo', snippet=SubscriptionSnippet(title='PyCon 2015', description='')),
Subscription(kind='youtube#subscription', id='zqShTXi-2-Rya5uUxEp3ZsPI3fZrFQnSXNQCwvHBGGo', snippet=SubscriptionSnippet(title='ikaros-life', description='This is a test channel.'))]
```
Get your own subscriptions, you need to authorize first, and supply the token:
```
>>> r = api.get_subscription_by_me(
... mine=True,
... parts=["id", "snippet"],
... count=2
... )
>>> r
SubscriptionListResponse(kind='youtube#subscriptionListResponse')
>>> r.items
[Subscription(kind='youtube#subscription', id='zqShTXi-2-Tx7TtwQqhCBwtJ-Aho6DZeutqZiP4Q79Q', snippet=SubscriptionSnippet(title='Next Day Video', description='')),
Subscription(kind='youtube#subscription', id='zqShTXi-2-Tx7TtwQqhCBwViE_j9IEgnmRmPnqJljxo', snippet=SubscriptionSnippet(title='PyCon 2015', description=''))]
```
Get public channel subscriptions:
```
>>> r = api.get_subscription_by_channel(
... channel_id="UCAuUUnT6oDeKwE6v1NGQxug",
... parts="id,snippet",
... count=2
... )
>>> r
SubscriptionListResponse(kind='youtube#subscriptionListResponse')
>>> r.items
[Subscription(kind='youtube#subscription', id='FMP3Mleijt-52zZDGkHtR5KhwkvCcdQKWWWIA1j5eGc', snippet=SubscriptionSnippet(title='TEDx Talks', description="TEDx is an international community that organizes TED-style events anywhere and everywhere -- celebrating locally-driven ideas and elevating them to a global stage. TEDx events are produced independently of TED conferences, each event curates speakers on their own, but based on TED's format and rules.\n\nFor more information on using TED for commercial purposes (e.g. employee learning, in a film, or in an online course), please submit a media request using the link below.")),
Subscription(kind='youtube#subscription', id='FMP3Mleijt_ZKvy5M-HhRlsqI4wXY7VmP5g8lvmRhVU', snippet=SubscriptionSnippet(title='TED Residency', description='The TED Residency program is an incubator for breakthrough ideas. It is free and open to all via a semi-annual competitive application. Those chosen as TED Residents spend four months at TED headquarters in New York City, working on their idea. Selection criteria include the strength of their idea, their character, and their ability to bring a fresh perspective and positive contribution to the diverse TED community.'))]
```
### ACTIVITIES
You can get activities by channel id. You can also get your own activities after you have completed authorization.
Get public channel activities:
```
>>> r = api.get_activities_by_channel(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw", count=2)
>>> r
ActivityListResponse(kind='youtube#activityListResponse')
>>> r.items
[Activity(kind='youtube#activity', id='MTUxNTc3NzM2MDAyODIxOTQxNDM0NjAwMA==', snippet=ActivitySnippet(title='2019 Year in Review - The Developer Show', description='Here to bring you the latest developer news from across Google this year is Developer Advocate Timothy Jordan. In this last week of the year, we’re taking a look back at some of the coolest and biggest announcements we covered in 2019! \n\nFollow Google Developers on Instagram → https://goo.gle/googledevs\n\nWatch more #DevShow → https://goo.gle/GDevShow\nSubscribe to Google Developers → https://goo.gle/developers')),
Activity(kind='youtube#activity', id='MTUxNTc3MTI4NzIzODIxOTQxNDM0NzI4MA==', snippet=ActivitySnippet(title='GDE Promo - Lara Martin', description='Meet Lara Martin, a Flutter/Dart Google Developers Expert and get inspired by her journey. Watch now for a preview of her story! #GDESpotlights #IncludedWithGoogle\n\nLearn about the GDE program → https://goo.gle/2qWOvAy\n\nGoogle Developers Experts → https://goo.gle/GDE\nSubscribe to Google Developers → https://goo.gle/developers'))]
```
Get your activities:
```
>>> r = api_with_token.get_activities_by_me()
>>> r.items
[Activity(kind='youtube#activity', id='MTUxNTc0OTk2MjI3NDE0MjYwMDY1NjAwODA=', snippet=ActivitySnippet(title='华山日出', description='冷冷的山头')),
Activity(kind='youtube#activity', id='MTUxNTc0OTk1OTAyNDE0MjYwMDY1NTc2NDg=', snippet=ActivitySnippet(title='海上日出', description='美美美'))]
```
Get your video captions:
```
>>> r = api.get_captions_by_video(video_id="oHR3wURdJ94", parts=["id", "snippet"])
>>> r
CaptionListResponse(kind='youtube#captionListResponse')
>>> r.items
[Caption(kind='youtube#caption', id='SwPOvp0r7kd9ttt_XhcHdZthMwXG7Z0I', snippet=CaptionSnippet(videoId='oHR3wURdJ94', lastUpdated='2020-01-14T09:40:49.981Z')),
Caption(kind='youtube#caption', id='fPMuDm722CIRcUAT3NTPQHQZJZJxt39kU7JvrHk8Kzs=', snippet=CaptionSnippet(videoId='oHR3wURdJ94', lastUpdated='2020-01-14T09:39:46.991Z'))]
```
If you already have caption id(s), you can get video caption by id(s):
```
>>> r = api.get_captions_by_video(video_id="oHR3wURdJ94", parts=["id", "snippet"], caption_id="SwPOvp0r7kd9ttt_XhcHdZthMwXG7Z0I")
>>> r
CaptionListResponse(kind='youtube#captionListResponse')
>>> r.items
[Caption(kind='youtube#caption', id='SwPOvp0r7kd9ttt_XhcHdZthMwXG7Z0I', snippet=CaptionSnippet(videoId='oHR3wURdJ94', lastUpdated='2020-01-14T09:40:49.981Z'))]
```
### CHANNEL SECTIONS
You can get channel sections by channel id, section id, or your own channel.
Get channel sections by channel id:
```
>>> r = api.get_channel_sections_by_channel(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
>>>> r
ChannelSectionResponse(kind='youtube#channelSectionListResponse')
>>> r.items
[ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.e-Fk7vMPqLE'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.B8DTd9ZXJqM'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.MfvRjkWLxgk'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.fEjJOXRoWwg'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.PvTmxDBxtLs'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.pmcIOsL7s98'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.c3r3vYf9uD0'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.ZJpkBl-mXfM'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.9_wU0qhEPR8'),
ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.npYvuMz0_es')]
```
Get authorized user's channel sections:
```
>>> r = api.get_channel_sections_by_channel(mine=True)
>>> r.items
[ChannelSection(kind='youtube#channelSection', id='UCa-vrCLQHviTOVnEKDOdetQ.jNQXAC9IVRw'),
ChannelSection(kind='youtube#channelSection', id='UCa-vrCLQHviTOVnEKDOdetQ.LeAltgu_pbM'),
ChannelSection(kind='youtube#channelSection', id='UCa-vrCLQHviTOVnEKDOdetQ.nGzAI5pLbMY')]
```
Get channel section detail info by id:
```
>>> r = api.get_channel_section_by_id(section_id="UC_x5XG1OV2P6uZZ5FSM9Ttw.e-Fk7vMPqLE")
>>> r
ChannelSectionResponse(kind='youtube#channelSectionListResponse')
>>> r1.items
[ChannelSection(kind='youtube#channelSection', id='UC_x5XG1OV2P6uZZ5FSM9Ttw.e-Fk7vMPqLE')]
```
### I18N RESOURCE
You can get a list of content regions that the YouTube website supports:
```
>>> r = api.get_i18n_regions(parts=["snippet"])
>>> r.items
[I18nRegion(kind='youtube#i18nRegion', id='DZ', snippet=I18nRegionSnippet(gl='DZ', name='Algeria')),
I18nRegion(kind='youtube#i18nRegion', id='AR', snippet=I18nRegionSnippet(gl='AR', name='Argentina')),
I18nRegion(kind='youtube#i18nRegion', id='AU', snippet=I18nRegionSnippet(gl='AU', name='Australia'))
...]
```
You can get a list of application languages that the YouTube website supports:
```
>>> r = api.get_i18n_languages(parts=["snippet"])
>>> r.items
[I18nLanguage(kind='youtube#i18nLanguage', id='af', snippet=I18nLanguageSnippet(hl='af', name='Afrikaans')),
I18nLanguage(kind='youtube#i18nLanguage', id='az', snippet=I18nLanguageSnippet(hl='az', name='Azerbaijani')),
I18nLanguage(kind='youtube#i18nLanguage', id='id', snippet=I18nLanguageSnippet(hl='id', name='Indonesian')),
...]
```
### MEMBER
The API request must be authorized by the channel owner.
You can retrieve a list of members (formerly known as "sponsors") for a channel:
```
>>> r = api_with_token.get_members(parts=["snippet"])
>>> r.items
[MemberListResponse(kind='youtube#memberListResponse'),
MemberListResponse(kind='youtube#memberListResponse')]
```
### MEMBERSHIP LEVEL
The API request must be authorized by the channel owner.
You can retrieve a list membership levels for a channel:
```
>>> r = api_with_token.get_membership_levels(parts=["snippet"])
>>> r.items
[MembershipsLevelListResponse(kind='youtube#membershipsLevelListResponse'),
MembershipsLevelListResponse(kind='youtube#membershipsLevelListResponse')]
```
### VIDEO ABUSE REPORT REASON
You can retrieve a list of reasons that can be used to report abusive videos:
```
>>> r = api_with_token.get_video_abuse_report_reason(parts=["snippet"])
>>> r.items
[VideoAbuseReportReason(kind='youtube#videoAbuseReportReason'),
VideoAbuseReportReason(kind='youtube#videoAbuseReportReason')]
```
### SEARCH
You can use those methods to search the video, playlist, or channel data. For more info, you can see
the [Search Request Docs](https://developers.google.com/youtube/v3/docs/search/list).
You can search different type of resource with keywords:
```
>>> r = api.search_by_keywords(q="surfing", search_type=["channel","video", "playlist"], count=5, limit=5)
>>> r.items
[SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult')]
```
You can search your app send videos:
```
>>> r = api_with_token.search_by_developer(q="news", count=1)
>>> r.items
[SearchResult(kind='youtube#searchResult')]
```
You can search your videos:
```
>>> r = api_with_token.search_by_mine(q="news", count=1)
>>> r.items
[SearchResult(kind='youtube#searchResult')]
```
Or you can build your request using the `search` method:
```
>>> r = api.search(
... location="21.5922529, -158.1147114",
... location_radius="10mi",
... q="surfing",
... parts=["snippet"],
... count=5,
... published_after="2020-02-01T00:00:00Z",
... published_before="2020-03-01T00:00:00Z",
... safe_search="moderate",
... search_type="video")
>>> r.items
[SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult')]
>>> r = api.search(
... event_type="live",
... q="news",
... count=3,
... parts=["snippet"],
... search_type="video",
... topic_id="/m/09s1f",
... order="viewCount")
>>> r.items
[SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult'),
SearchResult(kind='youtube#searchResult')]
```
================================================
FILE: docs/docs/usage/work-with-client.md
================================================
# Work with Client
We have refactored the project code to support more methods and improve code usability.
And new structure like follows.

In this structure, we identify each entity as a class of resources and perform operations on the resources.
## INSTANTIATE
Client is exposed via the ``pyyoutube.Client`` class.
You can initialize it with `api key`, to get public data.
```python
from pyyoutube import Client
cli = Client(api_key="your api key")
```
If you want to update your channel data. or upload video. You need to initialize with `access token`, or do the auth flow.
```python
from pyyoutube import Client
cli = Client(access_token="Access Token with permissions")
```
```python
from pyyoutube import Client
cli = Client(client_id="ID for app", client_secret="Secret for app")
# Get authorization url
cli.get_authorize_url()
# ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=id&redirect_uri=https%3A%2F%2Flocalhost%2F&scope=scope&state=PyYouTube&access_type=offline&prompt=select_account', 'PyYouTube')
# Click url and give permissions.
# Copy the redirected url.
cli.generate_access_token(authorization_response="redirected url")
# AccessToken(access_token='token', expires_in=3599, token_type='Bearer')
```
### from client_secret
Only `web` and some `installed` type client_secrets are supported.
The fields `client_id` and `client_secret` must be set.
`Client.DEFAULT_REDIRECT_URI` will be set the first entry of the field `redirect_uris`.
```python
from pyyoutube import Client
file_path = "path/to/client_secret.json"
cli = Client(client_secret_path=file_path)
# Then go through auth flow descriped above
```
Once initialize to the client, you can operate the API to get data.
## Usage
### Channel Resource
The API supports the following methods for the `channels` resources:
- list: Returns a collection of zero or more channel resources that match the request criteria.
- update: Updates a channel's metadata. Note that this method currently only supports updates to the channel resource's
brandingSettings and invideoPromotion objects and their child properties
#### List channel data
```python
resp = cli.channels.list(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
# ChannelListResponse(kind='youtube#channelListResponse')
print(resp.items)
# [Channel(kind='youtube#channel', id='UC_x5XG1OV2P6uZZ5FSM9Ttw')]
```
#### update channel metadata
```python
import pyyoutube.models as mds
body = mds.Channel(
id="channel id",
brandingSettings=mds.ChannelBrandingSetting(
image=mds.ChannelBrandingSettingImage(
bannerExternalUrl="new banner url"
)
)
)
channel = cli.channels.update(
part="brandingSettings",
body=body
)
print(channel.brandingSettings.image.bannerExternalUrl)
# 'https://yt3.googleusercontent.com/AegVxoIusdXEmsJ9j3bcJR3zuImOd6TngNw58iJAP0AOAXCnb1xHPcuEDOQC8J85SCZvt5i8A_g'
```
### Video Resource
The API supports the following methods for `videos` resources.
#### getRating
Retrieves the ratings that the authorized user gave to a list of specified videos.
```python
resp = cli.videos.get_rating(video_id="Z56Jmr9Z34Q")
print(resp.items)
# [VideoRatingItem(videoId='Z56Jmr9Z34Q', rating='none')]
```
#### list
Returns a list of videos that match the API request parameters.
```python
resp = cli.videos.list(video_id="Z56Jmr9Z34Q")
print(resp.items)
# [Video(kind='youtube#video', id='Z56Jmr9Z34Q')]
```
#### insert
Uploads a video to YouTube and optionally sets the video's metadata.
```python
import pyyoutube.models as mds
from pyyoutube.media import Media
body = mds.Video(
snippet=mds.VideoSnippet(
title="video title",
description="video description"
)
)
media = Media(filename="video.mp4")
upload = cli.videos.insert(
body=body,
media=media,
parts=["snippet"],
notify_subscribers=True
)
video_body = None
while video_body is None:
status, video_body = upload.next_chunk()
if status:
print(f"Upload progress: {status.progress()}")
print(video_body)
# {"kind": "youtube#video", "etag": "17W46NjVxoxtaoh1E6GmbQ2hv5c",....}
```
#### update
Updates a video's metadata.
```python
import pyyoutube.models as mds
body = mds.Video(
id="fTK1Jj6QlDw",
snippet=mds.VideoSnippet(
title="What a nice day",
description="Blue sky with cloud. updated.",
categoryId="1",
)
)
resp = cli.videos.update(
parts=["snippet"],
body=body,
return_json=True,
)
print(resp)
# {"kind": "youtube#video", "etag": "BQUtovVd0TBJwC5S8-Pu-dK_I6s", "id": "fTK1Jj6QlDw", "snippet": {"publishedAt": "2022-12-15T03:45:16Z", "channelId": "UCa-vrCLQHviTOVnEKDOdetQ", "title": "What a nice day", "description": "Blue sky with cloud. updated.", "thumbnails": {"default": {"url": "https://i.ytimg.com/vi/fTK1Jj6QlDw/default.jpg", "width": 120, "height": 90}, "medium": {"url": "https://i.ytimg.com/vi/fTK1Jj6QlDw/mqdefault.jpg", "width": 320, "height": 180}, "high": {"url": "https://i.ytimg.com/vi/fTK1Jj6QlDw/hqdefault.jpg", "width": 480, "height": 360}, "standard": {"url": "https://i.ytimg.com/vi/fTK1Jj6QlDw/sddefault.jpg", "width": 640, "height": 480}, "maxres": {"url": "https://i.ytimg.com/vi/fTK1Jj6QlDw/maxresdefault.jpg", "width": 1280, "height": 720}}, "channelTitle": "ikaros data", "categoryId": "1", "liveBroadcastContent": "none", "localized": {"title": "What a nice day", "description": "Blue sky with cloud. updated."}, "defaultAudioLanguage": "en-US"}}
```
#### delete
Deletes a YouTube video.
```python
cli.videos.delete(video_id="fTK1Jj6QlDw")
# True
```
#### rate
Add a like or dislike rating to a video or remove a rating from a video.
```python
cli.videos.rate(video_id="fTK1Jj6QlDw", rating="like")
# True
```
#### reportAbuse
Report a video for containing abusive content.
```python
import pyyoutube.models as mds
body = mds.VideoReportAbuse(
videoId="fTK1Jj6QlDw",
reasonId="32"
)
cli.videos.report_abuse(body=body)
# True
```
================================================
FILE: docs/mkdocs.yml
================================================
site_name: Python-Youtube Docs
site_description: Docs for python-youtube library
site_url: https://sns-sdks.github.io/python-youtube/
repo_url: https://github.com/sns-sdks/python-youtube
copyright: Copyright © 2019 - 2021 Ikaros kun
theme:
name: material
features:
- navigation.tabs
palette:
# Light mode
- media: "(prefers-color-scheme: light)"
scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/toggle-switch-off-outline
name: Switch to dark mode
# Dark mode
- media: "(prefers-color-scheme: dark)"
scheme: slate
primary: blue
accent: blue
toggle:
icon: material/toggle-switch
name: Switch to light mode
nav:
- Introduction: index.md
- Introduce Structure: introduce-new-structure.md
- Usage:
- Work With `Api`: usage/work-with-api.md
- Work With `Client`: usage/work-with-client.md
- Installation: installation.md
- Getting Started: getting_started.md
- Authorization: authorization.md
- Changelog: CHANGELOG.md
extra:
social:
- icon: fontawesome/brands/twitter
link: https://twitter.com/realllkk520
- icon: fontawesome/brands/github
link: https://github.com/sns-sdks/python-youtube
markdown_extensions:
- codehilite
- admonition
- pymdownx.superfences
- pymdownx.emoji
================================================
FILE: examples/README.md
================================================
# Examples
We provide two entry points to operate the YouTube DATA API.
- Api `from pyyoutube import Api`: This is an old implementation used to be compatible with older versions of code.
- Client `from pyyoutube import Client`: This is a new implementation for operating the API and provides additional
capabilities.
# Basic Usage
## API
```python
from pyyoutube import Api
api = Api(api_key="your key")
api.get_channel_info(channel_id="id for channel")
# ChannelListResponse(kind='youtube#channelListResponse')
```
You can get more examples at [api examples](/examples/apis/).
## Client
```python
from pyyoutube import Client
cli = Client(api_key="your key")
cli.channels.list(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
# ChannelListResponse(kind='youtube#channelListResponse')
```
You can get more examples at [client examples](/examples/clients/).
================================================
FILE: examples/__init__.py
================================================
================================================
FILE: examples/apis/__init__.py
================================================
================================================
FILE: examples/apis/channel_videos.py
================================================
"""
Retrieve some videos info from given channel.
Use pyyoutube.api.get_channel_info to get channel video uploads playlist id.
Then use pyyoutube.api.get_playlist_items to get playlist's videos id.
Last use get_video_by_id to get videos data.
"""
import pyyoutube
API_KEY = "xxx" # replace this with your api key.
def get_videos(channel_id):
api = pyyoutube.Api(api_key=API_KEY)
channel_info = api.get_channel_info(channel_id=channel_id)
playlist_id = channel_info.items[0].contentDetails.relatedPlaylists.uploads
uploads_playlist_items = api.get_playlist_items(
playlist_id=playlist_id, count=10, limit=6
)
videos = []
for item in uploads_playlist_items.items:
video_id = item.contentDetails.videoId
video = api.get_video_by_id(video_id=video_id)
videos.extend(video.items)
return videos
def processor():
channel_id = "UC_x5XG1OV2P6uZZ5FSM9Ttw"
videos = get_videos(channel_id)
with open("videos.json", "w+") as f:
for video in videos:
f.write(video.to_json())
f.write("\n")
if __name__ == "__main__":
processor()
================================================
FILE: examples/apis/get_all_videos_id_with_channel_by_search.py
================================================
"""
Retrieve channel's videos by search api.
Note Quota impact: A call to this method has a quota cost of 100 units.
"""
import pyyoutube
API_KEY = "xxx" # replace this with your api key.
def get_all_videos_id_by_channel(channel_id, limit=50, count=50):
api = pyyoutube.Api(api_key=API_KEY)
videos = []
next_page = None
while True:
res = api.search(
channel_id=channel_id,
limit=limit,
count=count,
page_token=next_page,
)
next_page = res.nextPageToken
for item in res.items:
if item.id.videoId:
videos.append(item.id.videoId)
if not next_page:
break
return videos
================================================
FILE: examples/apis/get_subscription_with_oauth.py
================================================
"""
This demo show how to use this library to do authorization and get your subscription.
"""
import pyyoutube
import webbrowser
CLIENT_ID = "your app id"
CLIENT_SECRET = "your app secret"
def get_subscriptions():
api = pyyoutube.Api(client_id=CLIENT_ID, client_secret=CLIENT_SECRET)
# need follows scope
scope = ["https://www.googleapis.com/auth/youtube.readonly"]
url, _ = api.get_authorization_url(scope=scope)
print(
"Try to start a browser to visit the authorization page. If not opened. you can copy and visit by hand:\n"
f"{url}"
)
webbrowser.open(url)
auth_response = input(
"\nCopy the whole url if you finished the step to authorize:\n"
)
api.generate_access_token(authorization_response=auth_response, scope=scope)
sub_res = api.get_subscription_by_me(mine=True, parts="id,snippet", count=None)
with open("subscriptions.json", "w+") as f:
f.write(sub_res.to_json())
print("Finished.")
if __name__ == "__main__":
get_subscriptions()
================================================
FILE: examples/apis/oauth_flow.py
================================================
"""
This example demonstrates how to perform authorization.
"""
from pyyoutube import Api
CLIENT_ID = "xxx" # Your app id
CLIENT_SECRET = "xxx" # Your app secret
SCOPE = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.force-ssl",
"https://www.googleapis.com/auth/userinfo.profile",
]
def do_authorize():
api = Api(client_id=CLIENT_ID, client_secret=CLIENT_SECRET)
authorize_url, state = api.get_authorization_url(scope=SCOPE)
print(f"Click url to do authorize: {authorize_url}")
response_uri = input("Input youtube redirect uri:\n")
token = api.generate_access_token(authorization_response=response_uri, scope=SCOPE)
print(f"Your token: {token}")
# get data
profile = api.get_profile()
print(f"Your channel id: {profile.id}")
if __name__ == "__main__":
do_authorize()
================================================
FILE: examples/clients/__init__.py
================================================
================================================
FILE: examples/clients/channel_info.py
================================================
"""
This example demonstrates how to retrieve information for a channel.
"""
from pyyoutube import Client
API_KEY = "Your key" # replace this with your api key.
def get_channel_info():
cli = Client(api_key=API_KEY)
channel_id = "UC_x5XG1OV2P6uZZ5FSM9Ttw"
resp = cli.channels.list(
channel_id=channel_id, parts=["id", "snippet", "statistics"], return_json=True
)
print(f"Channel info: {resp['items'][0]}")
if __name__ == "__main__":
get_channel_info()
================================================
FILE: examples/clients/oauth_flow.py
================================================
"""
This example demonstrates how to perform authorization.
"""
from pyyoutube import Client
CLIENT_ID = "xxx" # Your app id
CLIENT_SECRET = "xxx" # Your app secret
CLIENT_SECRET_PATH = None # or your path/to/client_secret_web.json
SCOPE = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.force-ssl",
"https://www.googleapis.com/auth/userinfo.profile",
]
def do_authorize():
cli = Client(client_id=CLIENT_ID, client_secret=CLIENT_SECRET)
# or if you want to use a web type client_secret.json
# cli = Client(client_secret_path=CLIENT_SECRET_PATH)
authorize_url, state = cli.get_authorize_url(scope=SCOPE)
print(f"Click url to do authorize: {authorize_url}")
response_uri = input("Input youtube redirect uri:\n")
token = cli.generate_access_token(authorization_response=response_uri, scope=SCOPE)
print(f"Your token: {token}")
# get data
resp = cli.channels.list(mine=True)
print(f"Your channel id: {resp.items[0].id}")
if __name__ == "__main__":
do_authorize()
================================================
FILE: examples/clients/oauth_refreshing.py
================================================
"""
This example demonstrates how to automatically (re)generate tokens for continuous OAuth.
We store the Access Token in a seperate .env file to be used later.
"""
from pyyoutube import Client
from json import loads, dumps
from pathlib import Path
CLIENT_ID = "xxx" # Your app id
CLIENT_SECRET = "xxx" # Your app secret
CLIENT_SECRET_PATH = None # or your path/to/client_secret_web.json
TOKEN_PERSISTENT_PATH = None # path/to/persistent_token_storage_location
SCOPE = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.force-ssl",
"https://www.googleapis.com/auth/userinfo.profile",
]
def do_refresh():
token_location = Path(TOKEN_PERSISTENT_PATH)
# Read the persistent token data if it exists
token_data = {}
if token_location.exists():
token_data = loads(token_location.read_text())
cli = Client(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
access_token=token_data.get("access_token"),
refresh_token=token_data.get("refresh_token"),
)
# or if you want to use a web type client_secret.json
# cli = Client(
# client_secret_path=CLIENT_SECRET_PATH,
# access_token=token_data.get("access_token"),
# refresh_token=token_data.get("refresh_token")
# )
# If no access token is provided, this is the same as oauth_flow.py
if not cli._has_auth_credentials():
authorize_url, state = cli.get_authorize_url(scope=SCOPE)
print(f"Click url to do authorize: {authorize_url}")
response_uri = input("Input youtube redirect uri:\n")
token = cli.generate_access_token(
authorization_response=response_uri, scope=SCOPE
)
print(f"Your token: {token}")
# Otherwise, refresh the access token if it has expired
else:
token = cli.refresh_access_token(cli.refresh_token)
# we add the token data to the client and token objects so that they are complete
token.refresh_token = cli.refresh_token
cli.access_token = token.access_token
print(f"Your token: {token}")
# Write the token data to the persistent location to be used again, ensuring the file exists
token_location.mkdir(parents=True, exist_ok=True)
token_location.write_text(
dumps(
{"access_token": token.access_token, "refresh_token": token.refresh_token}
)
)
# Now you can do things with the client
resp = cli.channels.list(mine=True)
print(f"Your channel id: {resp.items[0].id}")
if __name__ == "__main__":
do_refresh()
================================================
FILE: examples/clients/upload_video.py
================================================
"""
This example demonstrates how to upload a video.
"""
import pyyoutube.models as mds
from pyyoutube import Client
from pyyoutube.media import Media
# Access token with scope:
# https://www.googleapis.com/auth/youtube.upload
# https://www.googleapis.com/auth/youtube
# https://www.googleapis.com/auth/youtube.force-ssl
ACCESS_TOKEN = "xxx"
def upload_video():
cli = Client(access_token=ACCESS_TOKEN)
body = mds.Video(
snippet=mds.VideoSnippet(title="video title", description="video description")
)
media = Media(filename="target_video.mp4")
upload = cli.videos.insert(
body=body, media=media, parts=["snippet"], notify_subscribers=True
)
response = None
while response is None:
print(f"Uploading video...")
status, response = upload.next_chunk()
if status is not None:
print(f"Uploading video progress: {status.progress()}...")
# Use video class to representing the video resource.
video = mds.Video.from_dict(response)
print(f"Video id {video.id} was successfully uploaded.")
if __name__ == "__main__":
upload_video()
================================================
FILE: pyproject.toml
================================================
[tool.poetry]
name = "python-youtube"
version = "0.9.9"
description = "A Python wrapper around for YouTube Data API."
authors = ["ikaroskun <merle.liukun@gmail.com>"]
license = "MIT"
keywords = ["youtube-api", "youtube-v3-api", "youtube-data-api", "youtube-sdk"]
readme = "README.rst"
homepage = "https://github.com/sns-sdks/python-youtube"
repository = "https://github.com/sns-sdks/python-youtube"
classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Topic :: Software Development :: Libraries :: Python Modules",
"Programming Language :: Python",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
packages = [
{ include = "pyyoutube" },
{ include = "tests", format = "sdist" },
]
[tool.poetry.dependencies]
python = "^3.9"
requests = ">=2.28.0,<3.0.0"
requests-oauthlib = ">=1.3.0,<3.0.0"
isodate = ">=0.6.1,<1.0.0"
dataclasses-json = ">=0.6.0,<1.0.0"
[tool.poetry.group.dev.dependencies]
responses = "^0.25.0"
pytest = "^8.4.0"
pytest-cov = "^6.2.0"
[build-system]
requires = ["poetry-core>=2.0.0"]
build-backend = "poetry.core.masonry.api"
================================================
FILE: pytest.ini
================================================
[pytest]
addopts = --cov=pyyoutube --cov-report xml
================================================
FILE: pyyoutube/__init__.py
================================================
from .api import Api # noqa
from .client import Client # noqa
from .error import * # noqa
from .models import * # noqa
from .utils.constants import TOPICS # noqa
================================================
FILE: pyyoutube/__version__.py
================================================
# d8888b. db db d888888b db db .d88b. d8b db db db .d88b. db db d888888b db db d8888b. d88888b
# 88 `8D `8b d8' `~~88~~' 88 88 .8P Y8. 888o 88 `8b d8' .8P Y8. 88 88 `~~88~~' 88 88 88 `8D 88'
# 88oodD' `8bd8' 88 88ooo88 88 88 88V8o 88 `8bd8' 88 88 88 88 88 88 88 88oooY' 88ooooo
# 88~~~ 88 88 88~~~88 88 88 88 V8o88 88 88 88 88 88 88 88 88 88~~~b. 88~~~~~
# 88 88 88 88 88 `8b d8' 88 V888 88 `8b d8' 88b d88 88 88b d88 88 8D 88.
# 88 YP YP YP YP `Y88P' VP V8P YP `Y88P' ~Y8888P' YP ~Y8888P' Y8888P' Y88888P
__version__ = "0.9.9"
================================================
FILE: pyyoutube/api.py
================================================
"""
Main Api implementation.
"""
from typing import Optional, List, Union
import requests
from requests.auth import HTTPBasicAuth
from requests.models import Response
from requests_oauthlib.oauth2_session import OAuth2Session
from pyyoutube.error import ErrorCode, ErrorMessage, PyYouTubeException
from pyyoutube.models import (
AccessToken,
UserProfile,
ActivityListResponse,
CaptionListResponse,
ChannelListResponse,
ChannelSectionResponse,
PlaylistListResponse,
PlaylistItemListResponse,
VideoListResponse,
CommentThreadListResponse,
CommentListResponse,
VideoCategoryListResponse,
SearchListResponse,
SubscriptionListResponse,
I18nRegionListResponse,
I18nLanguageListResponse,
MemberListResponse,
MembershipsLevelListResponse,
VideoAbuseReportReasonListResponse,
)
from pyyoutube.utils.params_checker import enf_comma_separated, enf_parts
class Api(object):
"""
Example usage:
To create an instance of pyyoutube.Api class:
>>> import pyyoutube
>>> api = pyyoutube.Api(api_key="your api key")
To get one channel info:
>>> res = api.get_channel_info(channel_id="UC_x5XG1OV2P6uZZ5FSM9Ttw")
>>> print(res.items[0])
Now this api provide methods as follows:
>>> api.get_authorization_url()
>>> api.generate_access_token()
>>> api.refresh_token()
>>> api.get_channel_info()
>>> api.get_playlist_by_id()
>>> api.get_playlists()
>>> api.get_playlist_item_by_id()
>>> api.get_playlist_items()
>>> api.get_video_by_id()
>>> api.get_videos_by_chart()
>>> api.get_videos_by_myrating()
>>> api.get_comment_thread_by_id()
>>> api.get_comment_threads()
>>> api.get_comment_by_id()
>>> api.get_comments()
>>> api.get_video_categories()
>>> api.get_subscription_by_id()
>>> api.get_subscription_by_channel()
>>> api.get_subscription_by_me()
>>> api.get_activities_by_channel()
>>> api.get_activities_by_me()
>>> api.get_captions_by_video()
>>> api.get_channel_sections_by_id()
>>> api.get_channel_sections_by_channel()
>>> api.get_i18n_regions()
>>> api.get_i18n_languages()
>>> api.get_video_abuse_report_reason()
>>> api.search()
>>> api.search_by_keywords()
>>> api.search_by_developer()
>>> api.search_by_mine()
>>> api.search_by_related_video()
"""
BASE_URL = "https://www.googleapis.com/youtube/v3/"
AUTHORIZATION_URL = "https://accounts.google.com/o/oauth2/v2/auth"
EXCHANGE_ACCESS_TOKEN_URL = "https://oauth2.googleapis.com/token"
USER_INFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo"
DEFAULT_REDIRECT_URI = "https://localhost/"
DEFAULT_SCOPE = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/userinfo.profile",
]
DEFAULT_STATE = "PyYouTube"
DEFAULT_TIMEOUT = 10
DEFAULT_QUOTA = 10000 # this quota reset at 00:00:00(GMT-7) every day.
def __init__(
self,
client_id: Optional[str] = None,
client_secret: Optional[str] = None,
api_key: Optional[str] = None,
access_token: Optional[str] = None,
timeout: Optional[int] = None,
proxies: Optional[dict] = None,
) -> None:
"""
This Api provide two method to work. Use api key or use access token.
Args:
client_id(str, optional):
Your google app's ID.
client_secret (str, optional):
Your google app's secret.
api_key(str, optional):
The api key which you create from google api console.
access_token(str, optional):
If you not provide api key, you can do authorization to get an access token.
If all api key and access token provided. Use access token first.
timeout(int, optional):
The request timeout.
proxies(dict, optional):
If you want use proxy, need point this param.
param style like requests lib style.
Refer https://2.python-requests.org//en/latest/user/advanced/#proxies
Returns:
YouTube Api instance.
"""
self._client_id = client_id
self._client_secret = client_secret
self._api_key = api_key
self._access_token = access_token
self._refresh_token = None # This keep current user's refresh token.
self._timeout = timeout
self.session = requests.Session()
self.proxies = proxies
if not (
(self._client_id and self._client_secret)
or self._api_key
or self._access_token
):
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message="Must specify either client key info or api key.",
)
)
if self._timeout is None:
self._timeout = self.DEFAULT_TIMEOUT
def _get_oauth_session(
self,
redirect_uri: Optional[str] = None,
scope: Optional[List[str]] = None,
**kwargs,
) -> OAuth2Session:
"""
Build a request session for OAuth.
Args:
redirect_uri(str, optional)
Determines how Google's authorization server sends a response to your app.
If not provide will use default https://localhost/
scope (list, optional)
The scope you want give permission.
If you not provide, will use default scope.
kwargs(dict, optional)
Some other params you want provide.
Returns:
OAuth2 Session
"""
if redirect_uri is None:
redirect_uri = self.DEFAULT_REDIRECT_URI
if scope is None:
scope = self.DEFAULT_SCOPE
return OAuth2Session(
client_id=self._client_id,
scope=scope,
redirect_uri=redirect_uri,
state=self.DEFAULT_STATE,
**kwargs,
)
def get_authorization_url(
self,
redirect_uri: Optional[str] = None,
scope: Optional[List[str]] = None,
**kwargs,
) -> (str, str):
"""
Build authorization url to do authorize.
Args:
redirect_uri(str, optional)
Determines how Google's authorization server sends a response to your app.
If not provide will use default https://localhost/
scope (list, optional)
The scope you want give permission.
If you not provide, will use default scope.
kwargs(dict, optional)
Some other params you want provide.
Returns:
The uri you can open on browser to do authorize.
"""
oauth_session = self._get_oauth_session(
redirect_uri=redirect_uri,
scope=scope,
**kwargs,
)
authorization_url, state = oauth_session.authorization_url(
self.AUTHORIZATION_URL,
access_type="offline",
prompt="select_account",
**kwargs,
)
return authorization_url, state
def generate_access_token(
self,
authorization_response: str,
redirect_uri: Optional[str] = None,
scope: Optional[List[str]] = None,
return_json: bool = False,
**kwargs,
) -> Union[dict, AccessToken]:
"""
Use the google auth response to get access token
Args:
authorization_response (str)
The response url which google redirect.
redirect_uri(str, optional)
Determines how Google's authorization server sends a response to your app.
If not provide will use default https://localhost/
scope (list, optional)
The scope you want give permission.
If you not provide, will use default scope.
return_json(bool, optional)
The return data type. If you set True JSON data will be returned.
False will return pyyoutube.AccessToken
kwargs(dict, optional)
Some other params you want provide.
Return:
Retrieved access token's info, pyyoutube.AccessToken instance.
"""
oauth_session = self._get_oauth_session(
redirect_uri=redirect_uri,
scope=scope,
**kwargs,
)
token = oauth_session.fetch_token(
self.EXCHANGE_ACCESS_TOKEN_URL,
client_secret=self._client_secret,
authorization_response=authorization_response,
proxies=self.proxies,
)
self._access_token = oauth_session.access_token
self._refresh_token = oauth_session.token["refresh_token"]
if return_json:
return token
else:
return AccessToken.from_dict(token)
def refresh_token(
self, refresh_token: Optional[str] = None, return_json: bool = False
) -> Union[dict, AccessToken]:
"""
Refresh token by api return refresh token.
Args:
refresh_token (str)
The refresh token which the api returns.
return_json (bool, optional):
If True JSON data will be returned, instead of pyyoutube.AccessToken
Return:
Retrieved new access token's info, pyyoutube.AccessToken instance.
"""
refresh_token = refresh_token if refresh_token else self._refresh_token
if refresh_token is None:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"Must provide the refresh token or api has been authorized.",
)
)
oauth_session = OAuth2Session(client_id=self._client_id)
auth = HTTPBasicAuth(self._client_id, self._client_secret)
new_token = oauth_session.refresh_token(
self.EXCHANGE_ACCESS_TOKEN_URL,
refresh_token=refresh_token,
auth=auth,
)
self._access_token = oauth_session.access_token
if return_json:
return new_token
else:
return AccessToken.from_dict(new_token)
@staticmethod
def _parse_response(response: Response) -> dict:
"""
Parse response data and check whether errors exists.
Args:
response (Response)
The response which the request return.
Return:
response's data
"""
data = response.json()
if "error" in data:
raise PyYouTubeException(response)
return data
@staticmethod
def _parse_data(data: Optional[dict]) -> Union[dict, list]:
"""
Parse resp data.
Args:
data (dict)
The response data by response.json()
Return:
response's items
"""
items = data["items"]
return items
def _request(
self, resource, method=None, args=None, post_args=None, enforce_auth=True
) -> Response:
"""
Main request sender.
Args:
resource(str)
Resource field is which type data you want to retrieve.
Such as channels,videos and so on.
method(str, optional)
The method this request to send request.
Default is 'GET'
args(dict, optional)
The url params for this request.
post_args(dict, optional)
The Post params for this request.
enforce_auth(bool, optional)
Whether use google credentials
Returns:
response
"""
if method is None:
method = "GET"
if args is None:
args = dict()
if post_args is not None:
method = "POST"
key = None
access_token = None
if self._api_key is not None:
key = "key"
access_token = self._api_key
if self._access_token is not None:
key = "access_token"
access_token = self._access_token
if access_token is None and enforce_auth:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message="You must provide your credentials.",
)
)
if enforce_auth:
if method == "POST" and key not in post_args:
post_args[key] = access_token
elif method == "GET" and key not in args:
args[key] = access_token
try:
response = self.session.request(
method=method,
url=self.BASE_URL + resource,
timeout=self._timeout,
params=args,
data=post_args,
proxies=self.proxies,
)
except requests.HTTPError as e:
raise PyYouTubeException(
ErrorMessage(status_code=ErrorCode.HTTP_ERROR, message=e.args[0])
)
else:
return response
def get_profile(
self, access_token: Optional[str] = None, return_json: Optional[bool] = False
) -> Union[dict, UserProfile]:
"""
Get token user info.
Args:
access_token(str, optional)
user access token. If not provide, use api instance access token
return_json(bool, optional)
The return data type. If you set True JSON data will be returned.
False will return pyyoutube.UserProfile
Returns:
The data for you given access token's user info.
"""
if access_token is None:
access_token = self._access_token
if access_token is None:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"Must provide the access token or api has been authorized.",
)
)
try:
response = self.session.get(
self.USER_INFO_URL,
params={"access_token": access_token},
timeout=self._timeout,
proxies=self.proxies,
)
except requests.HTTPError as e:
raise PyYouTubeException(
ErrorMessage(status_code=ErrorCode.HTTP_ERROR, message=e.args[0])
)
data = self._parse_response(response)
if return_json:
return data
else:
return UserProfile.from_dict(data)
def paged_by_page_token(
self,
resource: str,
args: dict,
count: Optional[int] = None,
):
"""
Response paged by response's page token. If not provide response token
Args:
resource (str):
The resource string need to retrieve data.
args (dict)
The args for api.
count (int, optional):
The count for result items you want to get.
If provide this with None, will retrieve all items.
Note:
The all items maybe too much. Notice your app's cost.
Returns:
Data api origin response.
"""
res_data: Optional[dict] = None
current_items: List[dict] = []
page_token: Optional[str] = None
now_items_count: int = 0
while True:
if page_token is not None:
args["pageToken"] = page_token
resp = self._request(resource=resource, method="GET", args=args)
data = self._parse_response(resp) # origin response
# set page token
page_token = data.get("nextPageToken")
prev_page_token = data.get("prevPageToken")
# parse results.
items = self._parse_data(data)
current_items.extend(items)
now_items_count += len(items)
if res_data is None:
res_data = data
# first check the count if satisfies.
if count is not None:
if now_items_count >= count:
current_items = current_items[:count]
break
# if have no page token, mean no more data.
if page_token is None:
break
res_data["items"] = current_items
# use last request page token
res_data["nextPageToken"] = page_token
res_data["prevPageToken"] = prev_page_token
return res_data
def get_activities_by_channel(
self,
*,
channel_id: str,
parts: Optional[Union[str, list, tuple, set]] = None,
before: Optional[str] = None,
after: Optional[str] = None,
region_code: Optional[str] = None,
count: Optional[int] = 20,
limit: int = 20,
page_token: Optional[str] = None,
return_json: bool = False,
):
"""
Retrieve given channel's activities data.
Args:
channel_id (str):
The id for channel which you want to get activities data.
parts ((str,list,tuple,set) optional):
The resource parts for activities you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
before (str, optional):
Set this will only return the activities occurred before this timestamp.
This need specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
after (str, optional):
Set this will only return the activities occurred after this timestamp.
This need specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
region_code (str, optional):
Set this will only return the activities for the specified country.
This need specified with an ISO 3166-1 alpha-2 country code.
count (int, optional):
The count will retrieve activities data.
Default is 20.
If provide this with None, will retrieve all activities.
limit (int, optional):
The maximum number of items each request retrieve.
For activities, this should not be more than 50.
Default is 20.
page_token (str, optional):
The token of the page of activities result to retrieve.
You can use this retrieve point result page directly.
And you should know about the page result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.ActivityListResponse instance.
Returns:
ActivityListResponse or original data.
"""
if count is None:
limit = 50 # for activities the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"channelId": channel_id,
"part": enf_parts(resource="activities", value=parts),
"maxResults": limit,
}
if before:
args["publishedBefore"] = before
if after:
args["publishedAfter"] = after
if region_code:
args["regionCode"] = region_code
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="activities", args=args, count=count
)
if return_json:
return res_data
else:
return ActivityListResponse.from_dict(res_data)
def get_activities_by_me(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
before: Optional[str] = None,
after: Optional[str] = None,
region_code: Optional[str] = None,
count: Optional[int] = 20,
limit: int = 20,
page_token: Optional[str] = None,
return_json: bool = False,
):
"""
Retrieve authorized user's activities.
Note:
This need you do authorize first.
Args:
parts ((str,list,tuple,set) optional):
The resource parts for activities you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
before (str, optional):
Set this will only return the activities occurred before this timestamp.
This need specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
after (str, optional):
Set this will only return the activities occurred after this timestamp.
This need specified in ISO 8601 (YYYY-MM-DDThh:mm:ss.sZ) format.
region_code (str, optional):
Set this will only return the activities for the specified country.
This need specified with an ISO 3166-1 alpha-2 country code.
count (int, optional):
The count will retrieve activities data.
Default is 20.
If provide this with None, will retrieve all activities.
limit (int, optional):
The maximum number of items each request retrieve.
For activities, this should not be more than 50.
Default is 20.
page_token (str, optional):
The token of the page of activities result to retrieve.
You can use this retrieve point result page directly.
And you should know about the page result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.ActivityListResponse instance.
Returns:
ActivityListResponse or original data.
"""
if count is None:
limit = 50 # for activities the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"mine": True,
"part": enf_parts(resource="activities", value=parts),
"maxResults": limit,
}
if before:
args["publishedBefore"] = before
if after:
args["publishedAfter"] = after
if region_code:
args["regionCode"] = region_code
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="activities", args=args, count=count
)
if return_json:
return res_data
else:
return ActivityListResponse.from_dict(res_data)
def get_captions_by_video(
self,
*,
video_id: str,
parts: Optional[Union[str, list, tuple, set]] = None,
caption_id: Optional[Union[str, list, tuple, set]] = None,
return_json: bool = False,
):
"""
Retrieve authorized user's video's caption data.
Note:
This need you do authorize first.
Args:
video_id (str):
The id for video which you want to get caption.
parts ((str,list,tuple,set) optional):
The resource parts for caption you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
caption_id ((str,list,tuple,set)):
The id for caption that you want to get data.
You can pass this with single id str,comma-separated id str, or list, tuple, set of id str.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.CaptionListResponse instance.
Returns:
CaptionListResponse or original data.
"""
args = {
"videoId": video_id,
"part": enf_parts("captions", parts),
}
if caption_id is not None:
args["id"] = enf_comma_separated("caption_id", caption_id)
resp = self._request(resource="captions", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return CaptionListResponse.from_dict(data)
def get_channel_info(
self,
*,
channel_id: Optional[Union[str, list, tuple, set]] = None,
for_handle: Optional[str] = None,
for_username: Optional[str] = None,
mine: Optional[bool] = None,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: str = "en_US",
return_json: Optional[bool] = False,
):
"""
Retrieve channel data from YouTube Data API.
Note:
1. Don't know why, but now you couldn't get channel list by given an guide category.
You can only get list by parameters mine,forUsername,id.
Refer: https://developers.google.com/youtube/v3/guides/implementation/channels
2. The origin maxResult param not work for these filter method.
Args:
channel_id ((str,list,tuple,set), optional):
The id or comma-separated id string for youtube channel which you want to get.
You can also pass this with an id list, tuple, set.
for_handle (str, optional):
The parameter specifies a YouTube handle, thereby requesting the channel associated with that handle.
The parameter value can be prepended with an @ symbol. For example, to retrieve the resource for
the "Google for Developers" channel, set the forHandle parameter value to
either GoogleDevelopers or @GoogleDevelopers.
for_username (str, optional):
The name for YouTube username which you want to get.
Note: This name may the old youtube version's channel's user's username, Not the the channel name.
Refer: https://developers.google.com/youtube/v3/guides/working_with_channel_ids
mine (bool, optional):
If you have give the authorization. Will return your channels.
Must provide the access token.
parts (str, optional):
Comma-separated list of one or more channel resource properties.
If not provided. will use default public properties.
hl (str, optional):
If provide this. Will return channel's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.ChannelListResponse instance.
Returns:
ChannelListResponse instance or original data.
"""
args = {
"part": enf_parts(resource="channels", value=parts),
"hl": hl,
}
if for_handle is not None:
args["forHandle"] = for_handle
elif for_username is not None:
args["forUsername"] = for_username
elif channel_id is not None:
args["id"] = enf_comma_separated("channel_id", channel_id)
elif mine is not None:
args["mine"] = mine
else:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"Specify at least one of channel_id,channel_name or mine",
)
)
resp = self._request(resource="channels", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return ChannelListResponse.from_dict(data)
def get_channel_sections_by_id(
self,
*,
section_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
return_json: Optional[bool] = False,
) -> Union[ChannelSectionResponse, dict]:
"""
Retrieve channel section info by his ids(s).
Args:
section_id:
The id(s) for channel sections.
You can pass this with single id str, comma-separated id str, or a list,tuple,set of ids.
parts:
The resource parts for channel section you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.ChannelSectionResponse instance.
Returns:
ChannelSectionResponse or original data.
"""
args = {
"id": enf_comma_separated(field="section_id", value=section_id),
"part": enf_parts(resource="channelSections", value=parts),
}
resp = self._request(resource="channelSections", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return ChannelSectionResponse.from_dict(data)
def get_channel_sections_by_channel(
self,
*,
channel_id: Optional[str] = None,
mine: bool = False,
parts: Optional[Union[str, list, tuple, set]] = None,
return_json: Optional[bool] = False,
) -> Union[ChannelSectionResponse, dict]:
"""
Retrieve channel sections by channel id.
Args:
channel_id:
The id for channel which you want to get channel sections.
mine:
If you want to get your channel's sections, set this with True.
And this need your authorization.
parts:
The resource parts for channel section you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.ChannelSectionResponse instance.
Returns:
ChannelSectionResponse or original data.
"""
args = {
"part": enf_parts(resource="channelSections", value=parts),
}
if mine:
args["mine"] = mine
else:
args["channelId"] = channel_id
resp = self._request(resource="channelSections", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return ChannelSectionResponse.from_dict(data)
def get_comment_by_id(
self,
*,
comment_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
text_format: Optional[str] = "html",
return_json: Optional[bool] = False,
):
"""
Retrieve comment info by given comment id str.
Args:
comment_id (str, optional):
The id for comment that you want to retrieve data.
You can pass this with single id str, comma-separated id str, or a list,tuple,set of ids.
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
text_format (str, optional):
Comments left by users format style.
Acceptable values are: html, plainText.
Default is html.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.CommentListResponse instance.
Returns:
CommentListResponse or original data
"""
args = {
"id": enf_comma_separated(field="comment_id", value=comment_id),
"part": enf_parts(resource="comments", value=parts),
"textFormat": text_format,
}
resp = self._request(resource="comments", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return CommentListResponse.from_dict(data)
def get_comments(
self,
*,
parent_id: str,
parts: Optional[Union[str, list, tuple, set]] = None,
text_format: Optional[str] = "html",
count: Optional[int] = 20,
limit: Optional[int] = 20,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve comments info by given parent id.
Note: YouTube currently supports replies only for top-level comments.
However, replies to replies may be supported in the future.
Args:
parent_id (str):
Provide the ID of the comment for which replies should be retrieved.
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
text_format (str, optional):
Comments left by users format style.
Acceptable values are: html, plainText.
Default is html.
count (int, optional):
The count will retrieve videos data.
Default is 20.
If provide this with None, will retrieve all comments.
limit (int, optional):
The maximum number of items each request retrieve.
For comments, this should not be more than 100.
Default is 20.
page_token(str, optional):
The token of the page of comments result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.CommentListResponse instance.
Returns:
CommentListResponse or original data
"""
if count is None:
limit = 100 # for comments the max limit for per request is 100
else:
limit = min(count, limit)
args = {
"parentId": parent_id,
"part": enf_parts(resource="comments", value=parts),
"textFormat": text_format,
"maxResults": limit,
}
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(resource="comments", args=args, count=count)
if return_json:
return res_data
else:
return CommentListResponse.from_dict(res_data)
def get_comment_thread_by_id(
self,
*,
comment_thread_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
text_format: Optional[str] = "html",
return_json: Optional[bool] = False,
):
"""
Retrieve the comment thread info by given id.
Args:
comment_thread_id ((str,list,tuple,set)):
The id for comment thread that you want to retrieve data.
You can pass this with single id str, comma-separated id str, or a list,tuple,set of ids.
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
text_format (str, optional):
Comments left by users format style.
Acceptable values are: html, plainText.
Default is html.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.CommentThreadListResponse instance.
Returns:
CommentThreadListResponse or original data
"""
args = {
"id": enf_comma_separated("comment_thread_id", comment_thread_id),
"part": enf_parts(resource="commentThreads", value=parts),
"textFormat": text_format,
}
resp = self._request(resource="commentThreads", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return CommentThreadListResponse.from_dict(data)
def get_comment_threads(
self,
*,
all_to_channel_id: Optional[str] = None,
channel_id: Optional[str] = None,
video_id: Optional[str] = None,
parts: Optional[Union[str, list, tuple, set]] = None,
moderation_status: Optional[str] = None,
order: Optional[str] = None,
search_terms: Optional[str] = None,
text_format: Optional[str] = "html",
count: Optional[int] = 20,
limit: Optional[int] = 20,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve the comment threads info by given filter condition.
Args:
all_to_channel_id (str, optional):
If you provide this with a channel id, will return all comment threads associated with the channel.
The response can include comments about the channel or about the channel's videos.
channel_id (str, optional):
If you provide this with a channel id, will return the comment threads associated with the channel.
But the response not include comments about the channel's videos.
video_id (str, optional):
If you provide this with a video id, will return the comment threads associated with the video.
parts ((str,list,tuple,set), optional)
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
moderation_status (str, optional):
This parameter must used with authorization.
If you provide this. the response will return comment threads match this filter only.
Acceptable values are:
- heldForReview: Retrieve comment threads that are awaiting review by a moderator.
- likelySpam: Retrieve comment threads classified as likely to be spam.
- published: Retrieve threads of published comments. this is default for all.
See more: https://developers.google.com/youtube/v3/docs/commentThreads/list#parameters
order (str, optional):
Order parameter specifies the order in which the API response should list comment threads.
Acceptable values are:
- time: Comment threads are ordered by time. This is the default behavior.
- relevance: Comment threads are ordered by relevance.
search_terms (str, optional):
The searchTerms parameter instructs the API to limit the API response to only contain comments
that contain the specified search terms.
text_format (str, optional):
Comments left by users format style.
Acceptable values are: html, plainText.
Default is html.
count (int, optional):
The count will retrieve comment threads data.
Default is 20.
If provide this with None, will retrieve all comment threads.
limit (int, optional):
The maximum number of items each request retrieve.
For comment threads, this should not be more than 100.
Default is 20.
page_token(str, optional):
The token of the page of commentThreads result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.CommentThreadListResponse instance.
Returns:
CommentThreadListResponse or original data
"""
if count is None:
limit = 100 # for commentThreads the max limit for per request is 100
else:
limit = min(count, limit)
args = {
"part": enf_parts(resource="commentThreads", value=parts),
"maxResults": limit,
"textFormat": text_format,
}
if all_to_channel_id:
args["allThreadsRelatedToChannelId"] = (all_to_channel_id,)
elif channel_id:
args["channelId"] = channel_id
elif video_id:
args["videoId"] = video_id
else:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"Specify at least one of all_to_channel_id, channel_id or video_id",
)
)
if moderation_status:
args["moderationStatus"] = moderation_status
if order:
args["order"] = order
if search_terms:
args["searchTerms"] = search_terms
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="commentThreads", args=args, count=count
)
if return_json:
return res_data
else:
return CommentThreadListResponse.from_dict(res_data)
def get_i18n_languages(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
return_json: Optional[bool] = False,
) -> Union[I18nLanguageListResponse, dict]:
"""
Returns a list of application languages that the YouTube website supports.
Args:
parts:
The resource parts for i18n language you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl:
If provide this. Will return i18n language's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.I18nLanguageListResponse instance.
Returns:
I18nLanguageListResponse or original data.
"""
args = {"hl": hl, "part": enf_parts(resource="i18nLanguages", value=parts)}
resp = self._request(resource="i18nLanguages", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return I18nLanguageListResponse.from_dict(data)
def get_i18n_regions(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
return_json: Optional[bool] = False,
) -> Union[I18nRegionListResponse, dict]:
"""
Retrieve all available regions.
Args:
parts:
The resource parts for i18n region you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl:
If provide this. Will return i18n region's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.I18nRegionListResponse instance.
Returns:
I18nRegionListResponse or origin data
"""
args = {"hl": hl, "part": enf_parts(resource="i18nRegions", value=parts)}
resp = self._request(resource="i18nRegions", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return I18nRegionListResponse.from_dict(data)
def get_members(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
mode: Optional[str] = "all_current",
count: Optional[int] = 5,
limit: Optional[int] = 5,
page_token: Optional[str] = None,
has_access_to_level: Optional[str] = None,
filter_by_member_channel_id: Optional[Union[str, list, tuple, set]] = None,
return_json: Optional[bool] = False,
) -> Union[MemberListResponse, dict]:
"""
Retrieve a list of members for a channel.
Args:
parts ((str,list,tuple,set) optional):
The resource parts for member you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
mode:
The mode parameter indicates which members will be included in the API response.
Set the parameter value to one of the following values:
- all_current (default): List current members, from newest to oldest. When this value is used,
the end of the list is reached when the API response does not contain a nextPageToken.
- updates : List only members that joined or upgraded since the previous API call.
Note: The first call starts a new stream of updates but does not actually return any members.
To start retrieving the membership updates, you need to poll the endpoint using the
nextPageToken at your desired frequency.
Note that when this value is used, the API response always contains a nextPageToken.
count (int, optional):
The count will retrieve videos data.
Default is 5.
limit (int, optional):
The maximum number of items each request retrieve.
For members, this should not be more than 1000.
Default is 5.
page_token (str, optional):
The token of the page of search result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
has_access_to_level (str, optional):
The hasAccessToLevel parameter value is a level ID that specifies the minimum level
that members in the result set should have.
filter_by_member_channel_id ((str,list,tuple,set) optional):
A list of channel IDs that can be used to check the membership status of specific users.
A maximum of 100 channels can be specified per call.
return_json (bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.MemberListResponse instance.
Returns:
MemberListResponse or original data
"""
if count is None:
limit = 1000
else:
limit = min(count, limit)
args = {
"part": enf_parts(resource="members", value=parts),
"maxResults": limit,
}
if mode:
args["mode"] = mode
if page_token is not None:
args["pageToken"] = page_token
if has_access_to_level:
args["hasAccessToLevel"] = has_access_to_level
if filter_by_member_channel_id:
args["filterByMemberChannelId"] = enf_parts(
resource="filterByMemberChannelId",
value=filter_by_member_channel_id,
check=False,
)
res_data = self.paged_by_page_token(
resource="members",
args=args,
count=count,
)
if return_json:
return res_data
else:
return MemberListResponse.from_dict(res_data)
def get_membership_levels(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
return_json: Optional[bool] = False,
) -> Union[MembershipsLevelListResponse, dict]:
"""
Retrieve membership levels for a channel
Notes:
This requires your authorization.
Args:
parts ((str,list,tuple,set) optional):
The resource parts for membership level you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
return_json (bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.MembershipsLevelListResponse instance.
Returns:
MembershipsLevelListResponse or original data
"""
args = {
"part": enf_parts(resource="membershipsLevels", value=parts),
}
resp = self._request(resource="membershipsLevels", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return MembershipsLevelListResponse.from_dict(data)
def get_playlist_item_by_id(
self,
*,
playlist_item_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve playlist Items info by your given id
Args:
playlist_item_id ((str,list,tuple,set)):
The id for playlist item that you want to retrieve info.
You can pass this with single id str, comma-separated id str.
Or a list,tuple,set of ids.
parts ((str,list,tuple,set) optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.PlayListItemApiResponse instance.
Returns:
PlaylistItemListResponse or original data
"""
args = {
"id": enf_comma_separated("playlist_item_id", playlist_item_id),
"part": enf_parts(resource="playlistItems", value=parts),
}
resp = self._request(resource="playlistItems", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return PlaylistItemListResponse.from_dict(data)
def get_playlist_items(
self,
*,
playlist_id: str,
parts: Optional[Union[str, list, tuple, set]] = None,
video_id: Optional[str] = None,
count: Optional[int] = 5,
limit: Optional[int] = 5,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve playlist Items info by your given playlist id
Args:
playlist_id (str):
The id for playlist that you want to retrieve items data.
parts ((str,list,tuple,set) optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
video_id (str, Optional):
Specifies that the request should return only the playlist items that contain the specified video.
count (int, optional):
The count will retrieve playlist items data.
Default is 5.
If provide this with None, will retrieve all playlist items.
limit (int, optional):
The maximum number of items each request retrieve.
For playlistItem, this should not be more than 50.
Default is 5
page_token(str, optional):
The token of the page of playlist items result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.PlayListItemApiResponse instance.
Returns:
PlaylistItemListResponse or original data
"""
if count is None:
limit = 50 # for playlistItems the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"playlistId": playlist_id,
"part": enf_parts(resource="playlistItems", value=parts),
"maxResults": limit,
}
if video_id is not None:
args["videoId"] = video_id
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="playlistItems", args=args, count=count
)
if return_json:
return res_data
else:
return PlaylistItemListResponse.from_dict(res_data)
def get_playlist_by_id(
self,
*,
playlist_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
return_json: Optional[bool] = False,
):
"""
Retrieve playlist data by given playlist id.
Args:
playlist_id ((str,list,tuple,set)):
The id for playlist that you want to retrieve data.
You can pass this with single id str,comma-separated id str, or list, tuple, set of id str.
parts (str, optional):
Comma-separated list of one or more playlist resource properties.
You can also pass this with list, tuple, set of part str.
If not provided. will use default public properties.
hl (str, optional):
If provide this. Will return playlist's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.PlaylistListResponse instance
Returns:
PlaylistListResponse or original data
"""
args = {
"id": enf_comma_separated("playlist_id", playlist_id),
"part": enf_parts(resource="playlists", value=parts),
"hl": hl,
}
resp = self._request(resource="playlists", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return PlaylistListResponse.from_dict(data)
def get_playlists(
self,
*,
channel_id: Optional[str] = None,
mine: Optional[bool] = None,
parts: Optional[Union[str, list, tuple, set]] = None,
count: Optional[int] = 5,
limit: Optional[int] = 5,
hl: Optional[str] = "en_US",
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve channel playlists info from youtube data api.
Args:
channel_id (str, optional):
If provide channel id, this will return pointed channel's playlist info.
mine (bool, optional):
If you have given the authorization. Will return your playlists.
Must provide the access token.
parts (str, optional):
Comma-separated list of one or more playlist resource properties.
You can also pass this with list, tuple, set of part str.
If not provided. will use default public properties.
count (int, optional):
The count will retrieve playlist data.
Default is 5.
If provide this with None, will retrieve all playlists.
limit (int, optional):
The maximum number of items each request to retrieve.
For playlist, this should not be more than 50.
Default is 5
hl (str, optional):
If provide this. Will return playlist's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
page_token(str, optional):
The token of the page of playlists result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.PlaylistListResponse instance.
Returns:
PlaylistListResponse or original data
"""
if count is None:
limit = 50 # for playlists the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"part": enf_parts(resource="playlists", value=parts),
"hl": hl,
"maxResults": limit,
}
if channel_id is not None:
args["channelId"] = channel_id
elif mine is not None:
args["mine"] = mine
else:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"Specify at least one of channel_id,playlist_id or mine",
)
)
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="playlists", args=args, count=count
)
if return_json:
return res_data
else:
return PlaylistListResponse.from_dict(res_data)
def search(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
for_developer: Optional[bool] = None,
for_mine: Optional[bool] = None,
related_to_video_id: Optional[str] = None,
channel_id: Optional[str] = None,
channel_type: Optional[str] = None,
event_type: Optional[str] = None,
location: Optional[str] = None,
location_radius: Optional[str] = None,
count: Optional[int] = 10,
limit: Optional[int] = 10,
order: Optional[str] = None,
published_after: Optional[str] = None,
published_before: Optional[str] = None,
q: Optional[str] = None,
region_code: Optional[str] = None,
relevance_language: Optional[str] = None,
safe_search: Optional[str] = None,
topic_id: Optional[str] = None,
search_type: Optional[Union[str, list, tuple, set]] = None,
video_caption: Optional[str] = None,
video_category_id: Optional[str] = None,
video_definition: Optional[str] = None,
video_dimension: Optional[str] = None,
video_duration: Optional[str] = None,
video_embeddable: Optional[str] = None,
video_license: Optional[str] = None,
video_paid_product_placement: Optional[str] = None,
video_syndicated: Optional[str] = None,
video_type: Optional[str] = None,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
) -> Union[SearchListResponse, dict]:
"""
Main search api implementation.
You can find all parameters description at https://developers.google.com/youtube/v3/docs/search/list#parameters
Returns:
SearchListResponse or original data
"""
parts = enf_parts(resource="search", value=parts)
if search_type is None:
search_type = "video,channel,playlist"
else:
search_type = enf_comma_separated(field="search_type", value=search_type)
args = {
"part": parts,
"maxResults": min(limit, count),
}
if for_developer:
args["forDeveloper"] = for_developer
if for_mine:
args["forMine"] = for_mine
if related_to_video_id:
args["relatedToVideoId"] = related_to_video_id
if channel_id:
args["channelId"] = channel_id
if channel_type:
args["channelType"] = channel_type
if event_type:
args["eventType"] = event_type
if location:
args["location"] = location
if location_radius:
args["locationRadius"] = location_radius
if order:
args["order"] = order
if published_after:
args["publishedAfter"] = published_after
if published_before:
args["publishedBefore"] = published_before
if q:
args["q"] = q
if region_code:
args["regionCode"] = region_code
if relevance_language:
args["relevanceLanguage"] = relevance_language
if safe_search:
args["safeSearch"] = safe_search
if topic_id:
args["topicId"] = topic_id
if search_type:
args["type"] = search_type
if video_caption:
args["videoCaption"] = video_caption
if video_category_id:
args["videoCategoryId"] = video_category_id
if video_definition:
args["videoDefinition"] = video_definition
if video_dimension:
args["videoDimension"] = video_dimension
if video_duration:
args["videoDuration"] = video_duration
if video_embeddable:
args["videoEmbeddable"] = video_embeddable
if video_license:
args["videoLicense"] = video_license
if video_paid_product_placement:
args["videoPaidProductPlacement"] = video_paid_product_placement
if video_syndicated:
args["videoSyndicated"] = video_syndicated
if video_type:
args["videoType"] = video_type
if page_token:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(resource="search", args=args, count=count)
if return_json:
return res_data
else:
return SearchListResponse.from_dict(res_data)
def search_by_keywords(
self,
*,
q: Optional[str],
parts: Optional[Union[str, list, tuple, set]] = None,
search_type: Optional[Union[str, list, tuple, set]] = None,
count: Optional[int] = 25,
limit: Optional[int] = 25,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
**kwargs: Optional[dict],
) -> Union[SearchListResponse, dict]:
"""
This is simplest usage for search api. You can only passed the keywords to retrieve data from YouTube.
And the result will include videos,playlists and channels.
Note: A call to this method has a quota cost of 100 units.
Args:
q (str):
Your keywords can also use the Boolean NOT (-) and OR (|) operators to exclude videos or
to find videos that are associated with one of several search terms. For example,
to search for videos matching either "boating" or "sailing",
set the q parameter value to boating|sailing. Similarly,
to search for videos matching either "boating" or "sailing" but not "fishing",
set the q parameter value to boating|sailing -fishing.
Note that the pipe character must be URL-escaped when it is sent in your API request.
The URL-escaped value for the pipe character is %7C.
parts ((str,list,tuple,set) optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
search_type ((str,list,tuple,set), optional):
Parameter restricts a search query to only retrieve a particular type of resource.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
The default value is video,channel,playlist
Acceptable values are:
- channel
- playlist
- video
count (int, optional):
The count will retrieve videos data.
Default is 25.
limit (int, optional):
The maximum number of items each request retrieve.
For search, this should not be more than 50.
Default is 25.
page_token (str, optional):
The token of the page of search result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SearchListResponse instance.
kwargs:
If you want use this pass more args. You can use this.
Returns:
SearchListResponse or original data
"""
return self.search(
parts=parts,
q=q,
search_type=search_type,
count=count,
limit=limit,
page_token=page_token,
return_json=return_json,
**kwargs,
)
def search_by_developer(
self,
*,
parts: Optional[Union[str, list, tuple, set]],
q: Optional[str] = None,
count: Optional[int] = 25,
limit: Optional[int] = 25,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
**kwargs,
) -> Union[SearchListResponse, dict]:
"""
Parameter restricts the search to only retrieve videos uploaded via the developer's application or website.
Args:
parts:
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
q:
Your keywords can also use the Boolean NOT (-) and OR (|) operators to exclude videos or
to find videos that are associated with one of several search terms. For example,
to search for videos matching either "boating" or "sailing",
set the q parameter value to boating|sailing. Similarly,
to search for videos matching either "boating" or "sailing" but not "fishing",
set the q parameter value to boating|sailing -fishing.
Note that the pipe character must be URL-escaped when it is sent in your API request.
The URL-escaped value for the pipe character is %7C.
count:
The count will retrieve videos data.
Default is 25.
limit:
The maximum number of items each request retrieve.
For search, this should not be more than 50.
Default is 25.
page_token:
The token of the page of search result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SearchListResponse instance.
kwargs:
If you want use this pass more args. You can use this.
Returns:
SearchListResponse or original data
"""
return self.search(
for_developer=True,
search_type="video",
parts=parts,
q=q,
count=count,
limit=limit,
page_token=page_token,
return_json=return_json,
**kwargs,
)
def search_by_mine(
self,
*,
parts: Optional[Union[str, list, tuple, set]],
q: Optional[str] = None,
count: Optional[int] = 25,
limit: Optional[int] = 25,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
**kwargs,
) -> Union[SearchListResponse, dict]:
"""
Parameter restricts the search to only retrieve videos owned by the authenticated user.
Note:
This methods can not use following parameters:
video_definition, video_dimension, video_duration, video_license,
video_embeddable, video_syndicated, video_type.
Args:
q:
Your keywords can also use the Boolean NOT (-) and OR (|) operators to exclude videos or
to find videos that are associated with one of several search terms. For example,
to search for videos matching either "boating" or "sailing",
set the q parameter value to boating|sailing. Similarly,
to search for videos matching either "boating" or "sailing" but not "fishing",
set the q parameter value to boating|sailing -fishing.
Note that the pipe character must be URL-escaped when it is sent in your API request.
The URL-escaped value for the pipe character is %7C.
parts ((str,list,tuple,set) optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
count (int, optional):
The count will retrieve videos data.
Default is 25.
limit (int, optional):
The maximum number of items each request retrieve.
For search, this should not be more than 50.
Default is 25.
page_token (str, optional):
The token of the page of search result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SearchListResponse instance.
kwargs:
If you want use this pass more args. You can use this.
Returns:
SearchListResponse or original data
"""
return self.search(
for_mine=True,
search_type="video",
parts=parts,
q=q,
count=count,
limit=limit,
page_token=page_token,
return_json=return_json,
**kwargs,
)
def search_by_related_video(
self,
*,
related_to_video_id: str,
parts: Optional[Union[str, list, tuple, set]] = None,
region_code: Optional[str] = None,
relevance_language: Optional[str] = None,
safe_search: Optional[str] = None,
count: Optional[int] = 25,
limit: Optional[int] = 25,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
) -> Union[SearchListResponse, dict]:
"""
Retrieve a list of videos related to that video.
Args:
related_to_video_id:
A YouTube video ID which result associated with.
parts:
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
region_code:
Parameter instructs the API to return search results for videos
that can be viewed in the specified country.
relevance_language:
Parameter instructs the API to return search results that are most relevant to the specified language.
safe_search:
Parameter indicates whether the search results should include restricted content
as well as standard content.
Acceptable values are:
- moderate – YouTube will filter some content from search results and, at the least,
will filter content that is restricted in your locale. Based on their content,
search results could be removed from search results or demoted in search results.
This is the default parameter value.
- none – YouTube will not filter the search result set.
- strict – YouTube will try to exclude all restricted content from the search result set.
Based on their content, search results could be removed from search results or
demoted in search results.
count:
The count will retrieve videos data.
Default is 25.
limit:
The maximum number of items each request retrieve.
For search, this should not be more than 50.
Default is 25.
page_token:
The token of the page of search result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SearchListResponse instance.
Returns:
If you want use this pass more args. You can use this.
"""
return self.search(
parts=parts,
related_to_video_id=related_to_video_id,
search_type="video",
region_code=region_code,
relevance_language=relevance_language,
safe_search=safe_search,
count=count,
limit=limit,
page_token=page_token,
return_json=return_json,
)
def get_subscription_by_id(
self,
*,
subscription_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve subscriptions by given subscription id(s).
Note:
This need authorized access token. or you will get no data.
Args:
subscription_id ((str,list,tuple,set)):
The id for subscription that you want to retrieve data.
You can pass this with single id str, comma-separated id str, or a list,tuple,set of ids.
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SubscriptionListResponse instance.
Returns:
SubscriptionListResponse or original data.
"""
args = {
"id": enf_comma_separated(field="subscription_id", value=subscription_id),
"part": enf_parts(resource="subscriptions", value=parts),
}
resp = self._request(resource="subscriptions", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return SubscriptionListResponse.from_dict(data)
def get_subscription_by_channel(
self,
*,
channel_id: str,
parts: Optional[Union[str, list, tuple, set]] = None,
for_channel_id: Optional[Union[str, list, tuple, set]] = None,
order: Optional[str] = "relevance",
count: Optional[int] = 20,
limit: Optional[int] = 20,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve the specified channel's subscriptions.
Note:
The API returns a 403 (Forbidden) HTTP response code if the specified channel
does not publicly expose its subscriptions and the request is not authorized
by the channel's owner.
Args:
channel_id (str):
The id for channel which you want to get subscriptions.
parts ((str,list,tuple,set) optional):
The resource parts for subscription you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
for_channel_id ((str,list,tuple,set) optional):
The parameter specifies a comma-separated list of channel IDs.
and will then only contain subscriptions matching those channels.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of channel ids.
order (str, optional):
The parameter specifies the method that will be used to sort resources in the API response.
Acceptable values are:
alphabetical – Sort alphabetically.
relevance – Sort by relevance.
unread – Sort by order of activity.
Default is relevance
count (int, optional):
The count will retrieve subscriptions data.
Default is 20.
If provide this with None, will retrieve all subscriptions.
limit (int, optional):
The maximum number of items each request retrieve.
For comment threads, this should not be more than 50.
Default is 20.
page_token(str, optional):
The token of the page of subscriptions result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SubscriptionListResponse instance.
Returns:
SubscriptionListResponse or original data.
"""
if count is None:
limit = 50 # for subscriptions the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"channelId": channel_id,
"part": enf_parts(resource="subscriptions", value=parts),
"order": order,
"maxResults": limit,
}
if for_channel_id is not None:
args["forChannelId"] = enf_comma_separated(
field="for_channel_id", value=for_channel_id
)
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="subscriptions", args=args, count=count
)
if return_json:
return res_data
else:
return SubscriptionListResponse.from_dict(res_data)
def get_subscription_by_me(
self,
*,
mine: Optional[bool] = None,
recent_subscriber: Optional[bool] = None,
subscriber: Optional[bool] = None,
parts: Optional[Union[str, list, tuple, set]] = None,
for_channel_id: Optional[Union[str, list, tuple, set]] = None,
order: Optional[str] = "relevance",
count: Optional[int] = 20,
limit: Optional[int] = 20,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve your subscriptions.
Note:
This can only used in a properly authorized request.
And for me test the parameter `recent_subscriber` and `subscriber` maybe not working.
Use the `mine` first.
Args:
mine (bool, optional):
Set this parameter's value to True to retrieve a feed of the authenticated user's subscriptions.
recent_subscriber (bool, optional):
Set this parameter's value to true to retrieve a feed of the subscribers of the authenticated user
in reverse chronological order (newest first).
And this can only get most recent 1000 subscribers.
subscriber (bool, optional):
Set this parameter's value to true to retrieve a feed of the subscribers of
the authenticated user in no particular order.
parts ((str,list,tuple,set) optional):
The resource parts for subscription you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
for_channel_id ((str,list,tuple,set) optional):
The parameter specifies a comma-separated list of channel IDs.
and will then only contain subscriptions matching those channels.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of channel ids.
order (str, optional):
The parameter specifies the method that will be used to sort resources in the API response.
Acceptable values are:
alphabetical – Sort alphabetically.
relevance – Sort by relevance.
unread – Sort by order of activity.
Default is relevance
count (int, optional):
The count will retrieve subscriptions data.
Default is 20.
If provide this with None, will retrieve all subscriptions.
limit (int, optional):
The maximum number of items each request retrieve.
For subscriptions, this should not be more than 50.
Default is 20.
page_token(str, optional):
The token of the page of subscriptions result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.SubscriptionListResponse instance.
Returns:
SubscriptionListResponse or original data.
"""
if count is None:
limit = 50 # for subscriptions the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"part": enf_parts(resource="subscriptions", value=parts),
"order": order,
"maxResults": limit,
}
if mine is not None:
args["mine"] = mine
elif recent_subscriber is not None:
args["myRecentSubscribers"] = recent_subscriber
elif subscriber is not None:
args["mySubscribers"] = subscriber
else:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"Must specify at least one of mine,recent_subscriber,subscriber.",
)
)
if for_channel_id is not None:
args["forChannelId"] = enf_comma_separated(
field="for_channel_id", value=for_channel_id
)
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(
resource="subscriptions", args=args, count=count
)
if return_json:
return res_data
else:
return SubscriptionListResponse.from_dict(res_data)
def get_video_abuse_report_reason(
self,
*,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
return_json: Optional[bool] = False,
) -> Union[VideoAbuseReportReasonListResponse, dict]:
"""
Retrieve a list of reasons that can be used to report abusive videos.
Notes:
This requires your authorization.
Args:
parts:
The resource parts for abuse reason you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl:
If provide this. Will return report reason's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
return_json:
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.VideoAbuseReportReasonListResponse instance.
Returns:
VideoAbuseReportReasonListResponse or original data.
"""
args = {
"part": enf_parts(resource="videoAbuseReportReasons", value=parts),
"hl": hl,
}
resp = self._request(resource="videoAbuseReportReasons", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return VideoAbuseReportReasonListResponse.from_dict(data)
def get_video_categories(
self,
*,
category_id: Optional[Union[str, list, tuple, set]] = None,
region_code: Optional[str] = None,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
return_json: Optional[bool] = False,
):
"""
Retrieve video categories by category id or region code.
Args:
category_id ((str,list,tuple,set), optional):
The id for video category thread that you want to retrieve data.
You can pass this with single id str, comma-separated id str, or a list,tuple,set of ids.
region_code (str, optional):
The region code that you want to retrieve guide categories.
The parameter value is an ISO 3166-1 alpha-2 country code.
Refer: https://www.iso.org/iso-3166-country-codes.html
parts ((str,list,tuple,set) optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl (str, optional):
If provide this. Will return video category's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
Default is en_US.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.VideoCategoryListResponse instance.
Returns:
VideoCategoryListResponse or original data
"""
args = {
"part": enf_parts(resource="videoCategories", value=parts),
"hl": hl,
}
if category_id is not None:
args["id"] = enf_comma_separated(field="category_id", value=category_id)
elif region_code is not None:
args["regionCode"] = region_code
else:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message="Specify at least one of category_id or region_code",
)
)
resp = self._request(resource="videoCategories", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return VideoCategoryListResponse.from_dict(data)
def get_video_by_id(
self,
*,
video_id: Union[str, list, tuple, set],
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
max_height: Optional[int] = None,
max_width: Optional[int] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve video data by given video id.
Args:
video_id ((str,list,tuple,set)):
The id for video that you want to retrieve data.
You can pass this with single id str, comma-separated id str, or a list,tuple,set of ids.
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl (str, optional):
If provide this. Will return video's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
max_height (int, optional):
Specifies the maximum height of the embedded player returned in the player.embedHtml property.
Acceptable values are 72 to 8192, inclusive.
max_width (int, optional):
Specifies the maximum width of the embedded player returned in the player.embedHtml property.
Acceptable values are 72 to 8192, inclusive.
If provide max_height at the same time. This will may be shorter than max_height.
For more https://developers.google.com/youtube/v3/docs/videos/list#parameters.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.VideoListResponse instance.
Returns:
VideoListResponse or original data
"""
args = {
"id": enf_comma_separated(field="video_id", value=video_id),
"part": enf_parts(resource="videos", value=parts),
"hl": hl,
}
if max_height is not None:
args["maxHeight"] = max_height
if max_width is not None:
args["maxWidth"] = max_width
resp = self._request(resource="videos", method="GET", args=args)
data = self._parse_response(resp)
if return_json:
return data
else:
return VideoListResponse.from_dict(data)
def get_videos_by_chart(
self,
*,
chart: str,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
max_height: Optional[int] = None,
max_width: Optional[int] = None,
region_code: Optional[str] = None,
category_id: Optional[str] = "0",
count: Optional[int] = 5,
limit: Optional[int] = 5,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve a list of YouTube's most popular videos.
Args:
chart (str):
The chart string for you want to retrieve data.
Acceptable values are: mostPopular
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl (str, optional):
If provide this. Will return playlist's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
max_height (int, optional):
Specifies the maximum height of the embedded player returned in the player.embedHtml property.
Acceptable values are 72 to 8192, inclusive.
max_width (int, optional):
Specifies the maximum width of the embedded player returned in the player.embedHtml property.
Acceptable values are 72 to 8192, inclusive.
If provide max_height at the same time. This will may be shorter than max_height.
For more https://developers.google.com/youtube/v3/docs/videos/list#parameters.
region_code (str, optional):
This parameter instructs the API to select a video chart available in the specified region.
Value is an ISO 3166-1 alpha-2 country code.
category_id (str, optional):
The id for video category that you want to filter.
Default is 0.
count (int, optional):
The count will retrieve videos data.
Default is 5.
If provide this with None, will retrieve all videos.
limit (int, optional):
The maximum number of items each request retrieve.
For videos, this should not be more than 50.
Default is 5.
page_token(str, optional):
The token of the page of videos result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.PlaylistListResponse instance.
Returns:
VideoListResponse or original data
"""
if count is None:
limit = 50 # for videos the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"chart": chart,
"part": enf_parts(resource="videos", value=parts),
"hl": hl,
"maxResults": limit,
"videoCategoryId": category_id,
}
if max_height is not None:
args["maxHeight"] = max_height
if max_width is not None:
args["maxWidth"] = max_width
if region_code:
args["regionCode"] = region_code
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(resource="videos", args=args, count=count)
if return_json:
return res_data
else:
return VideoListResponse.from_dict(res_data)
def get_videos_by_myrating(
self,
*,
rating: str,
parts: Optional[Union[str, list, tuple, set]] = None,
hl: Optional[str] = "en_US",
max_height: Optional[int] = None,
max_width: Optional[int] = None,
count: Optional[int] = 5,
limit: Optional[int] = 5,
page_token: Optional[str] = None,
return_json: Optional[bool] = False,
):
"""
Retrieve video data by my ration.
Args:
rating (str):
The rating string for you to retrieve data.
Acceptable values are: dislike, like
parts ((str,list,tuple,set), optional):
The resource parts for you want to retrieve.
If not provide, use default public parts.
You can pass this with single part str, comma-separated parts str or a list,tuple,set of parts.
hl (str, optional):
If provide this. Will return video's language localized info.
This value need https://developers.google.com/youtube/v3/docs/i18nLanguages.
max_height (int, optional):
Specifies the maximum height of the embedded player returned in the player.embedHtml property.
Acceptable values are 72 to 8192, inclusive.
max_width (int, optional):
Specifies the maximum width of the embedded player returned in the player.embedHtml property.
Acceptable values are 72 to 8192, inclusive.
If provide max_height at the same time. This will may be shorter than max_height.
For more https://developers.google.com/youtube/v3/docs/videos/list#parameters.
count (int, optional):
The count will retrieve videos data.
Default is 5.
If provide this with None, will retrieve all videos.
limit (int, optional):
The maximum number of items each request retrieve.
For videos, this should not be more than 50.
Default is 5.
page_token(str, optional):
The token of the page of videos result to retrieve.
You can use this retrieve point result page directly.
And you should know about the the result set for YouTube.
return_json(bool, optional):
The return data type. If you set True JSON data will be returned.
False will return a pyyoutube.VideoListResponse instance.
Returns:
VideoListResponse or original data
"""
if self._access_token is None:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.NEED_AUTHORIZATION,
message="This method can only used with authorization",
)
)
if count is None:
limit = 50 # for videos the max limit for per request is 50
else:
limit = min(count, limit)
args = {
"myRating": rating,
"part": enf_parts(resource="videos", value=parts),
"hl": hl,
"maxResults": limit,
}
if max_height is not None:
args["maxHeight"] = max_height
if max_width is not None:
args["maxWidth"] = max_width
if page_token is not None:
args["pageToken"] = page_token
res_data = self.paged_by_page_token(resource="videos", args=args, count=count)
if return_json:
return res_data
else:
return VideoListResponse.from_dict(res_data)
================================================
FILE: pyyoutube/client.py
================================================
"""
New Client for YouTube API
"""
import inspect
import json
from typing import List, Optional, Tuple, Union
import requests
from requests import Response
from requests.sessions import merge_setting
from requests.structures import CaseInsensitiveDict
from requests_oauthlib.oauth2_session import OAuth2Session
import pyyoutube.resources as resources
from pyyoutube.models.base import BaseModel
from pyyoutube.error import ErrorCode, ErrorMessage, PyYouTubeException
from pyyoutube.models import (
AccessToken,
)
from pyyoutube.resources.base_resource import Resource
def _is_resource_endpoint(obj):
return isinstance(obj, Resource)
class Client:
"""Client for YouTube resource"""
BASE_URL = "https://www.googleapis.com/youtube/v3/"
BASE_UPLOAD_URL = "https://www.googleapis.com/upload/youtube/v3/"
AUTHORIZATION_URL = "https://accounts.google.com/o/oauth2/v2/auth"
EXCHANGE_ACCESS_TOKEN_URL = "https://oauth2.googleapis.com/token"
REVOKE_TOKEN_URL = "https://oauth2.googleapis.com/revoke"
HUB_URL = "https://pubsubhubbub.appspot.com/subscribe"
DEFAULT_REDIRECT_URI = "https://localhost/"
DEFAULT_SCOPE = [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/userinfo.profile",
]
DEFAULT_STATE = "Python-YouTube"
activities = resources.ActivitiesResource()
captions = resources.CaptionsResource()
channels = resources.ChannelsResource()
channelBanners = resources.ChannelBannersResource()
channelSections = resources.ChannelSectionsResource()
comments = resources.CommentsResource()
commentThreads = resources.CommentThreadsResource()
i18nLanguages = resources.I18nLanguagesResource()
i18nRegions = resources.I18nRegionsResource()
members = resources.MembersResource()
membershipsLevels = resources.MembershipLevelsResource()
playlistItems = resources.PlaylistItemsResource()
playlists = resources.PlaylistsResource()
search = resources.SearchResource()
subscriptions = resources.SubscriptionsResource()
thumbnails = resources.ThumbnailsResource()
videoAbuseReportReasons = resources.VideoAbuseReportReasonsResource()
videoCategories = resources.VideoCategoriesResource()
videos = resources.VideosResource()
watermarks = resources.WatermarksResource()
def __new__(cls, *args, **kwargs):
self = super().__new__(cls)
sub_resources = inspect.getmembers(self, _is_resource_endpoint)
for name, resource in sub_resources:
resource_cls = type(resource)
resource = resource_cls(self)
setattr(self, name, resource)
return self
def __init__(
self,
client_id: Optional[str] = None,
client_secret: Optional[str] = None,
access_token: Optional[str] = None,
refresh_token: Optional[str] = None,
api_key: Optional[str] = None,
client_secret_path: Optional[str] = None,
timeout: Optional[int] = None,
proxies: Optional[dict] = None,
headers: Optional[dict] = None,
) -> None:
"""Class initial
Args:
client_id:
ID for your app.
client_secret:
Secret for your app.
access_token:
Access token for user authorized with your app.
refresh_token:
Refresh Token for user.
api_key:
API key for your app which generated from api console.
client_secret_path:
path to the client_secret.json file provided by google console
timeout:
Timeout for every request.
proxies:
Proxies for every request.
headers:
Headers for every request.
Raises:
PyYouTubeException: Missing either credentials.
"""
self.client_id = client_id
self.client_secret = client_secret
self.access_token = access_token
self.refresh_token = refresh_token
self.api_key = api_key
self.timeout = timeout
self.proxies = proxies
self.headers = headers
self.session = requests.Session()
self.merge_headers()
if not self._has_client_data() and client_secret_path is not None:
# try to use client_secret file
self._from_client_secrets_file(client_secret_path)
# Auth settings
if not (self._has_auth_credentials() or self._has_client_data()):
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message="Must specify either client key info or api key.",
)
)
def _from_client_secrets_file(self, client_secret_path: str):
"""Set credentials from client_sectet file
Args:
client_secret_path:
path to the client_secret.json file, provided by google console
Raises:
PyYouTubeException: missing required key, client_secret file not in 'web' format.
"""
with open(client_secret_path, "r") as f:
secrets_data = json.load(f)
credentials = None
for secrets_type in ["web", "installed"]:
if secrets_type in secrets_data:
credentials = secrets_data[secrets_type]
if not credentials:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.INVALID_PARAMS,
message="Only 'web' and 'installed' type client_secret files are supported.",
)
)
# check for reqiered fields
for field in ["client_secret", "client_id"]:
if field not in credentials:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message=f"missing required field '{field}'.",
)
)
self.client_id = credentials["client_id"]
self.client_secret = credentials["client_secret"]
# Set default redirect to first defined in client_secrets file if any
if "redirect_uris" in credentials and len(credentials["redirect_uris"]) > 0:
self.DEFAULT_REDIRECT_URI = credentials["redirect_uris"][0]
def _has_auth_credentials(self) -> bool:
return self.api_key or self.access_token
def _has_client_data(self) -> bool:
return self.client_id and self.client_secret
def merge_headers(self):
"""Merge custom headers to session."""
if self.headers:
self.session.headers = merge_setting(
request_setting=self.session.headers,
session_setting=self.headers,
dict_class=CaseInsensitiveDict,
)
@staticmethod
def parse_response(response: Response) -> dict:
"""Response parser
Args:
response:
Response from the Response.
Returns:
Response dict data.
Raises:
PyYouTubeException: If response has errors.
"""
data = response.json()
if "error" in data:
raise PyYouTubeException(response)
return data
def request(
self,
path: str,
method: str = "GET",
params: Optional[dict] = None,
data: Optional[dict] = None,
json: Optional[dict] = None,
enforce_auth: bool = True,
is_upload: bool = False,
**kwargs,
):
"""Send request to YouTube.
Args:
path:
Resource or url for YouTube data. such as channels,videos and so on.
method:
Method for the request.
params:
Object to send in the query string of the request.
data:
Object to send in the body of the request.
json:
Object json to send in the body of the request.
enforce_auth:
Whether to use user credentials.
is_upload:
Whether it is an upload job.
kwargs:
Additional parameters for request.
Returns:
Response for request.
Raises:
PyYouTubeException: Missing credentials when need credentials.
Request http error.
"""
if not path.startswith("http"):
base_url = self.BASE_UPLOAD_URL if is_upload else self.BASE_URL
path = base_url + path
# Add credentials to request
if enforce_auth:
if self.api_key is None and self.access_token is None:
raise PyYouTubeException(
ErrorMessage(
status_code=ErrorCode.MISSING_PARAMS,
message="You must provide your credentials.",
)
)
else:
self.add_token_to_headers()
params = self.add_api_key_to_params(params=params)
# If json is dataclass convert to dict
if isinstance(json, BaseModel):
json = json.to_dict_ignore_none()
try:
response = self.session.request(
method=method,
url=path,
params=params,
data=data,
json=json,
proxies=self.proxies,
timeout=self.timeout,
**kwargs,
)
except requests.HTTPError as e:
raise PyYouTubeException(
ErrorMessage(status_code=ErrorCode.HTTP_ERROR, message=e.args[0])
)
else:
return response
def add_token_to_headers(self):
if self.access_token:
self.session.headers.update(
{"Authorization": f"Bearer {self.access_token}"}
)
def add_api_key_to_params(self, params: Optional[dict] = None):
if not self.api_key:
return params
if params is None:
params = {"key": self.api_key}
else:
params["key"] = self.api_key
return params
def _get_oauth_session(
self,
redirect_uri: Optional[str] = None,
scope: Optional[List[str]] = None,
state: Optional[str] = None,
**kwargs,
) -> OAuth2Session:
"""Build request session for authorization
Args:
redirect_uri:
Determines how Google's authorization server sends a response to your app.
If not provide will use default https://localhost/
scope:
Permission scope for authorization.
see more: https://developers.google.com/identity/protocols/oauth2/scopes#youtube
state:
State sting for authorization.
**kwargs:
Additional parameters for session.
Returns:
OAuth2.0 Session
"""
redirect_uri = (
redirect_uri if redirect_uri is not None else self.DEFAULT_REDIRECT_URI
)
scope = scope if scope is not None else self.DEFAULT_SCOPE
state = state if state is not None else self.DEFAULT_STATE
return OAuth2Session(
client_id=self.client_id,
scope=scope,
redirect_uri=redirect_uri,
state=state,
**kwargs,
)
def get_authorize_url(
self,
redirect_uri: Optional[str] = None,
scope: Optional[List[str]] = None,
access_type: str = "offline",
state: Optional[str] = None,
include_granted_scopes: Optional[bool] = None,
login_hint: Optional[str] = None,
prompt: Optional[str] = None,
**kwargs,
) -> Tuple[str, str]:
"""Get authorize url for user.
Args:
redirect_uri:
Determines how Google's authorization server sends a response to your app.
If not provide will use default https://localhost/
scope:
The scope you want user to grant permission.
access_type:
Indicates whether your application can refresh access tokens when the user
is not present at the browser.
Valid parameter are `online` and `offline`.
state:
State string between your authorization request and the authorization server's response.
include_granted_scopes:
Enables applications to use incremental authorization to request
access to additional scopes in context.
Set true to enable.
login_hint:
Set the parameter value to an email address or sub identifier, which is
equivalent to the user's Google ID.
prompt:
A space-delimited, case-sensitive list of prompts to present the user.
Possible values are:
- none:
Do not display any authentication or consent screens.
Must not be specified with other values.
- consent:
Prompt the user for consent.
- select_account:
Prompt the user to select an account.
**kwargs:
Additional parameters for authorize session.
Returns:
A tuple of (url, state)
url: Authorize url for user.
state: State string for authorization.
References:
https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps
"""
session = self._get_oauth_session(
redirect_uri=redirect_uri,
scope=scope,
state=state,
**kwargs,
)
authorize_url, state = session.authorization_url(
url=self.AUTHORIZATION_URL,
access_type=access_type,
include_granted_scopes=include_granted_scopes,
login_hint=login_hint,
prompt=prompt,
)
return authorize_url, state
def generate_access_token(
self,
authorization_response: Optional[str] = None,
code: Optional[str] = None,
redirect_uri: Optional[str] = None,
scope: Optional[List[str]] = None,
state: Optional[str] = None,
return_json: bool = False,
**kwargs,
) -> Union[dict, AccessToken]:
"""Exchange the authorization code or authorization response for an access token.
Args:
authorization_response:
Response url for YouTune redirected to.
code:
Authorization code from authorization_response.
redirect_uri:
Determines how Google's authorization server sends a response to your app.
If not provide will use default https://localhost/
scope:
The scope you want user to grant permission.
state:
State string between your authorization request and the authorization server's response.
return_json:
Type for returned data. If you set True JSON data will be returned.
**kwargs:
Additional parameters for authorize session.
Returns:
Access token data.
"""
session = self._get_oauth_session(
redirect_uri=redirect_uri,
scope=scope,
state=state,
**kwargs,
)
token = session.fetch_token(
token_url=self.EXCHANGE_ACCESS_TOKEN_URL,
client_secret=self.client_secret,
authorization_response=authorization_response,
code=code,
proxies=self.proxies,
)
self.access_token = token["access_token"]
self.refresh_token = token.get("refresh_token")
return token if return_json else AccessToken.from_dict(token)
def refresh_access_token(
self, refresh_token: str, return_json: bool = False, **kwargs
) -> Union[dict, AccessToken]:
"""Refresh new access token.
Args:
refresh_token:
The refresh token returned from the authorization code exchange.
return_json:
Type for returned data. If you set True JSON data will be returned.
**kwargs:
Additional parameters for request.
Returns:
Access token data.
"""
response = self.request(
method="POST",
path=self.EXCHANGE_ACCESS_TOKEN_URL,
data={
"client_id": self.client_id,
"client_secret": self.client_secret,
"refresh_token": refresh_token,
"grant_type": "refresh_token",
},
enforce_auth=False,
**kwargs,
)
data = self.parse_response(response)
return data if return_json else AccessToken.from_dict(data)
def revoke_access_token(
self,
token: str,
) -> bool:
"""Revoke token.
Notes:
If the token is an access token which has a corresponding refresh token,
the refresh token will also be revoked.
Args:
token:
Can be an access token or a refresh token.
Returns:
Revoked status
Raises:
PyYouTubeException: When occur errors.
"""
response = self.request(
method="POST",
path=self.REVOKE_TOKEN_URL,
params={"token": token},
enforce_auth=False,
)
if response.ok:
return True
self.parse_response(response)
def subscribe_push_notification(
self,
channel_id: str,
callback_url: str,
mode: str = "subscribe",
lease_seconds: Optional[int] = None,
secret: Optional[str] = None,
verify: str = "async",
) -> bool:
"""Subscribe or unsubscribe to a YouTube channel's push notifications via PubSubHubbub.
When a subscribed channel publishes a new video or updates an existing one,
Google will send a notification to the callback_url.
Args:
channel_id:
The YouTube channel ID to subscribe to.
callback_url:
The URL that will receive push notifications from the hub.
Must be publicly accessible.
mode:
Either "subscribe" or "unsubscribe".
lease_seconds:
How long (in seconds) the subscription should remain active.
If omitted, the hub uses its own default (typically ~432000, i.e. 5 days).
secret:
A secret string used to compute an HMAC-SHA1 signature on each notification,
allowing you to verify the payload came from the hub.
verify:
Verification mode. Either "async" (default) or "sync".
Returns:
True if the hub accepted the request (HTTP 202 Accepted).
Raises:
PyYouTubeException: If the hub returns an error response.
References:
https://developers.google.com/youtube/v3/guides/push_notifications
https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html
"""
topic_url = (
f"https://www.youtube.com/xml/feeds/videos.xml?channel_id={channel_id}"
)
data = {
"hub.callback": callback_url,
"hub.mode": mode,
"hub.topic": topic_url,
"hub.verify": verify,
}
if lease_seconds is not None:
data["hub.lease_seconds"] = str(lease_seconds)
if secret is not None:
data["hub.secret"] = secret
response = self.request(
method="POST",
path=self.HUB_URL,
data=data,
enforce_auth=False,
)
# Hub returns 202 Accepted on success (async) or 204 No Content (sync)
if response.status_code in (202, 204):
return True
self.parse_response(response)
================================================
FILE: pyyoutube/error.py
================================================
from dataclasses import dataclass
from typing import Optional, Union
from requests import Response
__all__ = ["ErrorCode", "ErrorMessage", "PyYouTubeException"]
class ErrorCode:
HTTP_ERROR = 10000
MISSING_PARAMS = 10001
INVALID_PARAMS = 10002
NEED_AUTHORIZATION = 10003
AUTHORIZE_URL_FIRST = 10004
@dataclass
class ErrorMessage:
status_code: Optional[int] = None
message: Optional[str] = None
class PyYouTubeException(Exception):
"""
This is a return demo:
{'error': {'errors': [{'domain': 'youtube.parameter',
'reason': 'missingRequiredParameter',
'message': 'No filter selected. Expected one of: forUsername, managedByMe, categoryId, mine, mySubscribers, id, idParam',
'locationType': 'parameter',
'location': ''}],
'code': 400,
'message': 'No filter selected. Expected one of: forUsername, managedByMe, categoryId, mine, mySubscribers, id, idParam'}}
"""
def __init__(self, response: Optional[Union[ErrorMessage, Response]]):
self.status_code: Optional[int] = None
self.error_type: Optional[str] = None
self.message: Optional[str] = None
self.response: Optional[Union[ErrorMessage, Response]] = response
self.error_handler()
def error_handler(self):
"""
Error has two big type(but not the error type.): This module's error, Api return error.
So This will change two error to one format
"""
if isinstance(self.response, ErrorMessage):
self.status_code = self.response.status_code
self.message = self.response.message
self.error_type = "PyYouTubeException"
elif isinstance(self.response, Response):
res_data = self.response.json()
if "error" in res_data:
error = res_data["error"]
if isinstance(error, dict):
self.status_code = res_data["error"]["code"]
self.message = res_data["error"]["message"]
else:
self.status_code = self.response.status_code
self.message = error
self.error_type = "YouTubeException"
def __repr__(self):
return (
f"{self.error_type}(status_code={self.status_code},message={self.message})"
)
def __str__(self):
return self.__repr__()
================================================
FILE: pyyoutube/media.py
================================================
"""
Media object to upload.
"""
import mimetypes
import os
from typing import IO, Optional, Tuple
from requests import Response
from pyyoutube.error import PyYouTubeException, ErrorMessage, ErrorCode
DEFAULT_CHUNK_SIZE = 20 * 1024 * 1024
class Media:
def __init__(
self,
fd: Optional[IO] = None,
mimetype: Optional[str] = None,
filename: Optional[str] = None,
chunk_size: int = DEFAULT_CHUNK_SIZE,
) -> None:
"""Media representing a file to upload with metadata.
Args:
fd:
The source of the bytes to upload.
mimetype:
Mime-type of the file.
filename:
Name of the file.
At least one of the `fd` or `filename`.
chunk_size:
File will be uploaded in chunks of this many bytes. Only
gitextract_o3azcgt3/
├── .bumpversion.cfg
├── .coveragerc
├── .github/
│ ├── hack/
│ │ ├── changelog.sh
│ │ └── version.sh
│ └── workflows/
│ ├── docs.yaml
│ ├── release.yaml
│ └── test.yaml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.rst
├── docs/
│ ├── docs/
│ │ ├── authorization.md
│ │ ├── getting_started.md
│ │ ├── index.md
│ │ ├── installation.md
│ │ ├── introduce-new-structure.md
│ │ └── usage/
│ │ ├── work-with-api.md
│ │ └── work-with-client.md
│ └── mkdocs.yml
├── examples/
│ ├── README.md
│ ├── __init__.py
│ ├── apis/
│ │ ├── __init__.py
│ │ ├── channel_videos.py
│ │ ├── get_all_videos_id_with_channel_by_search.py
│ │ ├── get_subscription_with_oauth.py
│ │ └── oauth_flow.py
│ └── clients/
│ ├── __init__.py
│ ├── channel_info.py
│ ├── oauth_flow.py
│ ├── oauth_refreshing.py
│ └── upload_video.py
├── pyproject.toml
├── pytest.ini
├── pyyoutube/
│ ├── __init__.py
│ ├── __version__.py
│ ├── api.py
│ ├── client.py
│ ├── error.py
│ ├── media.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── activity.py
│ │ ├── auth.py
│ │ ├── base.py
│ │ ├── caption.py
│ │ ├── category.py
│ │ ├── channel.py
│ │ ├── channel_banner.py
│ │ ├── channel_section.py
│ │ ├── comment.py
│ │ ├── comment_thread.py
│ │ ├── common.py
│ │ ├── i18n.py
│ │ ├── member.py
│ │ ├── memberships_level.py
│ │ ├── mixins.py
│ │ ├── playlist.py
│ │ ├── playlist_item.py
│ │ ├── search_result.py
│ │ ├── subscription.py
│ │ ├── video.py
│ │ ├── video_abuse_report_reason.py
│ │ └── watermark.py
│ ├── resources/
│ │ ├── __init__.py
│ │ ├── activities.py
│ │ ├── base_resource.py
│ │ ├── captions.py
│ │ ├── channel_banners.py
│ │ ├── channel_sections.py
│ │ ├── channels.py
│ │ ├── comment_threads.py
│ │ ├── comments.py
│ │ ├── i18n_languages.py
│ │ ├── i18n_regions.py
│ │ ├── members.py
│ │ ├── membership_levels.py
│ │ ├── playlist_items.py
│ │ ├── playlists.py
│ │ ├── search.py
│ │ ├── subscriptions.py
│ │ ├── thumbnails.py
│ │ ├── video_abuse_report_reasons.py
│ │ ├── video_categories.py
│ │ ├── videos.py
│ │ └── watermarks.py
│ ├── utils/
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ └── params_checker.py
│ └── youtube_utils.py
├── testdata/
│ ├── apidata/
│ │ ├── abuse_reasons/
│ │ │ └── abuse_reason.json
│ │ ├── access_token.json
│ │ ├── activities/
│ │ │ ├── activities_by_channel_p1.json
│ │ │ ├── activities_by_channel_p2.json
│ │ │ ├── activities_by_mine_p1.json
│ │ │ └── activities_by_mine_p2.json
│ │ ├── captions/
│ │ │ ├── captions_by_video.json
│ │ │ ├── captions_filter_by_id.json
│ │ │ ├── insert_response.json
│ │ │ └── update_response.json
│ │ ├── categories/
│ │ │ ├── guide_categories_by_region.json
│ │ │ ├── guide_category_multi.json
│ │ │ ├── guide_category_single.json
│ │ │ ├── video_category_by_region.json
│ │ │ ├── video_category_multi.json
│ │ │ └── video_category_single.json
│ │ ├── channel_banners/
│ │ │ └── insert_response.json
│ │ ├── channel_info_multi.json
│ │ ├── channel_info_single.json
│ │ ├── channel_sections/
│ │ │ ├── channel_sections_by_channel.json
│ │ │ ├── channel_sections_by_id.json
│ │ │ ├── channel_sections_by_ids.json
│ │ │ └── insert_resp.json
│ │ ├── channels/
│ │ │ ├── info.json
│ │ │ ├── info_multiple.json
│ │ │ └── update_resp.json
│ │ ├── client_secrets/
│ │ │ ├── client_secret_installed_bad.json
│ │ │ ├── client_secret_installed_good.json
│ │ │ ├── client_secret_unsupported.json
│ │ │ └── client_secret_web.json
│ │ ├── comment_threads/
│ │ │ ├── comment_thread_single.json
│ │ │ ├── comment_threads_all_to_me.json
│ │ │ ├── comment_threads_by_channel.json
│ │ │ ├── comment_threads_by_video_paged_1.json
│ │ │ ├── comment_threads_by_video_paged_2.json
│ │ │ ├── comment_threads_multi.json
│ │ │ ├── comment_threads_with_search.json
│ │ │ └── insert_response.json
│ │ ├── comments/
│ │ │ ├── comments_by_parent_paged_1.json
│ │ │ ├── comments_by_parent_paged_2.json
│ │ │ ├── comments_multi.json
│ │ │ ├── comments_single.json
│ │ │ └── insert_response.json
│ │ ├── error_permission_resp.json
│ │ ├── i18ns/
│ │ │ ├── language_res.json
│ │ │ └── regions_res.json
│ │ ├── members/
│ │ │ ├── members_data.json
│ │ │ └── membership_levels.json
│ │ ├── playlist_items/
│ │ │ ├── insert_response.json
│ │ │ ├── playlist_items_filter_video.json
│ │ │ ├── playlist_items_multi.json
│ │ │ ├── playlist_items_paged_1.json
│ │ │ ├── playlist_items_paged_2.json
│ │ │ └── playlist_items_single.json
│ │ ├── playlists/
│ │ │ ├── insert_response.json
│ │ │ ├── playlists_mine.json
│ │ │ ├── playlists_multi.json
│ │ │ ├── playlists_paged_1.json
│ │ │ ├── playlists_paged_2.json
│ │ │ └── playlists_single.json
│ │ ├── search/
│ │ │ ├── search_by_developer.json
│ │ │ ├── search_by_event.json
│ │ │ ├── search_by_keywords_p1.json
│ │ │ ├── search_by_keywords_p2.json
│ │ │ ├── search_by_location.json
│ │ │ ├── search_by_mine.json
│ │ │ ├── search_by_related_video.json
│ │ │ ├── search_channels.json
│ │ │ └── search_videos_by_channel.json
│ │ ├── subscriptions/
│ │ │ ├── insert_response.json
│ │ │ ├── subscription_zero.json
│ │ │ ├── subscriptions_by_channel_p1.json
│ │ │ ├── subscriptions_by_channel_p2.json
│ │ │ ├── subscriptions_by_channel_with_filter.json
│ │ │ ├── subscriptions_by_id.json
│ │ │ ├── subscriptions_by_mine_filter.json
│ │ │ ├── subscriptions_by_mine_p1.json
│ │ │ └── subscriptions_by_mine_p2.json
│ │ ├── user_profile.json
│ │ └── videos/
│ │ ├── get_rating_response.json
│ │ ├── insert_response.json
│ │ ├── videos_chart_paged_1.json
│ │ ├── videos_chart_paged_2.json
│ │ ├── videos_info_multi.json
│ │ ├── videos_info_single.json
│ │ ├── videos_myrating_paged_1.json
│ │ └── videos_myrating_paged_2.json
│ ├── error_response.json
│ ├── error_response_simple.json
│ └── modeldata/
│ ├── abuse_report_reason/
│ │ ├── abuse_reason.json
│ │ └── abuse_reason_res.json
│ ├── activities/
│ │ ├── activity.json
│ │ ├── activity_contentDetails.json
│ │ ├── activity_response.json
│ │ └── activity_snippet.json
│ ├── captions/
│ │ ├── caption.json
│ │ ├── caption_response.json
│ │ └── caption_snippet.json
│ ├── categories/
│ │ ├── guide_category_info.json
│ │ ├── guide_category_response.json
│ │ ├── video_category_info.json
│ │ └── video_category_response.json
│ ├── channel_sections/
│ │ ├── channel_section_info.json
│ │ └── channel_section_response.json
│ ├── channels/
│ │ ├── channel_api_response.json
│ │ ├── channel_branding_settings.json
│ │ ├── channel_content_details.json
│ │ ├── channel_info.json
│ │ ├── channel_snippet.json
│ │ ├── channel_statistics.json
│ │ ├── channel_status.json
│ │ └── channel_topic_details.json
│ ├── comments/
│ │ ├── comment_api_response.json
│ │ ├── comment_info.json
│ │ ├── comment_snippet.json
│ │ ├── comment_thread_api_response.json
│ │ ├── comment_thread_info.json
│ │ ├── comment_thread_replies.json
│ │ └── comment_thread_snippet.json
│ ├── common/
│ │ ├── thumbnail_info.json
│ │ └── thumbnails_info.json
│ ├── i18ns/
│ │ ├── language_info.json
│ │ ├── language_res.json
│ │ ├── region_info.json
│ │ └── region_res.json
│ ├── members/
│ │ ├── member_info.json
│ │ └── membership_level.json
│ ├── playlist_items/
│ │ ├── playlist_item_api_response.json
│ │ ├── playlist_item_content_details.json
│ │ ├── playlist_item_info.json
│ │ ├── playlist_item_snippet.json
│ │ └── playlist_item_status.json
│ ├── playlists/
│ │ ├── playlist_api_response.json
│ │ ├── playlist_content_details.json
│ │ ├── playlist_info.json
│ │ ├── playlist_snippet.json
│ │ └── playlist_status.json
│ ├── search_result/
│ │ ├── search_result.json
│ │ ├── search_result_api_response.json
│ │ ├── search_result_id.json
│ │ └── search_result_snippet.json
│ ├── subscriptions/
│ │ ├── contentDetails.json
│ │ ├── resp.json
│ │ ├── snippet.json
│ │ ├── subscriberSnippet.json
│ │ └── subscription.json
│ ├── users/
│ │ ├── access_token.json
│ │ └── user_profile.json
│ └── videos/
│ ├── video_api_response.json
│ ├── video_category_info.json
│ ├── video_content_details.json
│ ├── video_info.json
│ ├── video_paid_product_placement_details.json
│ ├── video_recording_details.json
│ ├── video_snippet.json
│ ├── video_statistics.json
│ ├── video_status.json
│ └── video_topic_details.json
└── tests/
├── __init__.py
├── apis/
│ ├── __init__.py
│ ├── test_activities.py
│ ├── test_auth.py
│ ├── test_captions.py
│ ├── test_categories.py
│ ├── test_channel_sections.py
│ ├── test_channels.py
│ ├── test_comment_threads.py
│ ├── test_comments.py
│ ├── test_i18ns.py
│ ├── test_members.py
│ ├── test_playlist_items.py
│ ├── test_playlists.py
│ ├── test_search.py
│ ├── test_subscriptions.py
│ ├── test_video_abuse_reason.py
│ └── test_videos.py
├── clients/
│ ├── __init__.py
│ ├── base.py
│ ├── test_activities.py
│ ├── test_captions.py
│ ├── test_channel_banners.py
│ ├── test_channel_sections.py
│ ├── test_channels.py
│ ├── test_client.py
│ ├── test_comment_threads.py
│ ├── test_comments.py
│ ├── test_i18n.py
│ ├── test_media.py
│ ├── test_members.py
│ ├── test_membership_levels.py
│ ├── test_playlist_items.py
│ ├── test_playlists.py
│ ├── test_search.py
│ ├── test_subscriptions.py
│ ├── test_thumbnails.py
│ ├── test_video_abuse_report_reasons.py
│ ├── test_video_categories.py
│ ├── test_videos.py
│ └── test_watermarks.py
├── conftest.py
├── models/
│ ├── __init__.py
│ ├── test_abuse_reason.py
│ ├── test_activities.py
│ ├── test_auth_models.py
│ ├── test_captions.py
│ ├── test_category.py
│ ├── test_channel.py
│ ├── test_channel_sections.py
│ ├── test_comments.py
│ ├── test_i18n_models.py
│ ├── test_members.py
│ ├── test_playlist.py
│ ├── test_playlist_item.py
│ ├── test_search_result.py
│ ├── test_subscriptions.py
│ └── test_videos.py
├── test_error_handling.py
├── test_youtube_utils.py
└── utils/
├── __init__.py
└── test_params_checker.py
SYMBOL INDEX (541 symbols across 114 files)
FILE: examples/apis/channel_videos.py
function get_videos (line 14) | def get_videos(channel_id):
function processor (line 32) | def processor():
FILE: examples/apis/get_all_videos_id_with_channel_by_search.py
function get_all_videos_id_by_channel (line 12) | def get_all_videos_id_by_channel(channel_id, limit=50, count=50):
FILE: examples/apis/get_subscription_with_oauth.py
function get_subscriptions (line 12) | def get_subscriptions():
FILE: examples/apis/oauth_flow.py
function do_authorize (line 16) | def do_authorize():
FILE: examples/clients/channel_info.py
function get_channel_info (line 10) | def get_channel_info():
FILE: examples/clients/oauth_flow.py
function do_authorize (line 18) | def do_authorize():
FILE: examples/clients/oauth_refreshing.py
function do_refresh (line 23) | def do_refresh():
FILE: examples/clients/upload_video.py
function upload_video (line 16) | def upload_video():
FILE: pyyoutube/api.py
class Api (line 37) | class Api(object):
method __init__ (line 101) | def __init__(
method _get_oauth_session (line 157) | def _get_oauth_session(
method get_authorization_url (line 193) | def get_authorization_url(
method generate_access_token (line 229) | def generate_access_token(
method refresh_token (line 276) | def refresh_token(
method _parse_response (line 315) | def _parse_response(response: Response) -> dict:
method _parse_data (line 331) | def _parse_data(data: Optional[dict]) -> Union[dict, list]:
method _request (line 344) | def _request(
method get_profile (line 413) | def get_profile(
method paged_by_page_token (line 455) | def paged_by_page_token(
method get_activities_by_channel (line 513) | def get_activities_by_channel(
method get_activities_by_me (line 595) | def get_activities_by_me(
method get_captions_by_video (line 677) | def get_captions_by_video(
method get_channel_info (line 724) | def get_channel_info(
method get_channel_sections_by_id (line 801) | def get_channel_sections_by_id(
method get_channel_sections_by_channel (line 839) | def get_channel_sections_by_channel(
method get_comment_by_id (line 884) | def get_comment_by_id(
method get_comments (line 929) | def get_comments(
method get_comment_thread_by_id (line 996) | def get_comment_thread_by_id(
method get_comment_threads (line 1040) | def get_comment_threads(
method get_i18n_languages (line 1155) | def get_i18n_languages(
method get_i18n_regions (line 1191) | def get_i18n_regions(
method get_members (line 1226) | def get_members(
method get_membership_levels (line 1316) | def get_membership_levels(
method get_playlist_item_by_id (line 1353) | def get_playlist_item_by_id(
method get_playlist_items (line 1392) | def get_playlist_items(
method get_playlist_by_id (line 1458) | def get_playlist_by_id(
method get_playlists (line 1500) | def get_playlists(
method search (line 1581) | def search(
method search_by_keywords (line 1698) | def search_by_keywords(
method search_by_developer (line 1769) | def search_by_developer(
method search_by_mine (line 1829) | def search_by_mine(
method search_by_related_video (line 1893) | def search_by_related_video(
method get_subscription_by_id (line 1964) | def get_subscription_by_id(
method get_subscription_by_channel (line 2005) | def get_subscription_by_channel(
method get_subscription_by_me (line 2090) | def get_subscription_by_me(
method get_video_abuse_report_reason (line 2198) | def get_video_abuse_report_reason(
method get_video_categories (line 2239) | def get_video_categories(
method get_video_by_id (line 2298) | def get_video_by_id(
method get_videos_by_chart (line 2356) | def get_videos_by_chart(
method get_videos_by_myrating (line 2447) | def get_videos_by_myrating(
FILE: pyyoutube/client.py
function _is_resource_endpoint (line 24) | def _is_resource_endpoint(obj):
class Client (line 28) | class Client:
method __new__ (line 66) | def __new__(cls, *args, **kwargs):
method __init__ (line 76) | def __init__(
method _from_client_secrets_file (line 138) | def _from_client_secrets_file(self, client_secret_path: str):
method _has_auth_credentials (line 182) | def _has_auth_credentials(self) -> bool:
method _has_client_data (line 185) | def _has_client_data(self) -> bool:
method merge_headers (line 188) | def merge_headers(self):
method parse_response (line 198) | def parse_response(response: Response) -> dict:
method request (line 216) | def request(
method add_token_to_headers (line 293) | def add_token_to_headers(self):
method add_api_key_to_params (line 299) | def add_api_key_to_params(self, params: Optional[dict] = None):
method _get_oauth_session (line 308) | def _get_oauth_session(
method get_authorize_url (line 346) | def get_authorize_url(
method generate_access_token (line 415) | def generate_access_token(
method refresh_access_token (line 464) | def refresh_access_token(
method revoke_access_token (line 495) | def revoke_access_token(
method subscribe_push_notification (line 525) | def subscribe_push_notification(
FILE: pyyoutube/error.py
class ErrorCode (line 9) | class ErrorCode:
class ErrorMessage (line 18) | class ErrorMessage:
class PyYouTubeException (line 23) | class PyYouTubeException(Exception):
method __init__ (line 35) | def __init__(self, response: Optional[Union[ErrorMessage, Response]]):
method error_handler (line 42) | def error_handler(self):
method __repr__ (line 63) | def __repr__(self):
method __str__ (line 68) | def __str__(self):
FILE: pyyoutube/media.py
class Media (line 16) | class Media:
method __init__ (line 17) | def __init__(
method get_bytes (line 63) | def get_bytes(self, begin: int, length: int) -> bytes:
class MediaUploadProgress (line 80) | class MediaUploadProgress:
method __init__ (line 81) | def __init__(self, progressed_seize: int, total_size: int):
method progress (line 91) | def progress(self) -> float:
method __repr__ (line 103) | def __repr__(self) -> str:
class MediaUpload (line 107) | class MediaUpload:
method __init__ (line 108) | def __init__(
method next_chunk (line 142) | def next_chunk(self) -> Tuple[Optional[MediaUploadProgress], Optional[...
method process_response (line 196) | def process_response(
FILE: pyyoutube/models/activity.py
class ActivityContentDetailsUpload (line 14) | class ActivityContentDetailsUpload(BaseModel):
class ActivityContentDetailsLike (line 25) | class ActivityContentDetailsLike(BaseModel):
class ActivityContentDetailsFavorite (line 36) | class ActivityContentDetailsFavorite(BaseModel):
class ActivityContentDetailsComment (line 47) | class ActivityContentDetailsComment(BaseModel):
class ActivityContentDetailsSubscription (line 58) | class ActivityContentDetailsSubscription(BaseModel):
class ActivityContentDetailsPlaylistItem (line 69) | class ActivityContentDetailsPlaylistItem(BaseModel):
class ActivityContentDetailsRecommendation (line 82) | class ActivityContentDetailsRecommendation(BaseModel):
class ActivityContentDetailsBulletin (line 95) | class ActivityContentDetailsBulletin(BaseModel):
class ActivityContentDetailsSocial (line 106) | class ActivityContentDetailsSocial(BaseModel):
class ActivityContentDetailsChannelItem (line 121) | class ActivityContentDetailsChannelItem(BaseModel):
class ActivitySnippet (line 132) | class ActivitySnippet(BaseModel, DatetimeTimeMixin):
class ActivityContentDetails (line 150) | class ActivityContentDetails(BaseModel):
class Activity (line 178) | class Activity(BaseResource):
class ActivityListResponse (line 190) | class ActivityListResponse(BaseApiResponse):
FILE: pyyoutube/models/auth.py
class AccessToken (line 8) | class AccessToken(BaseModel):
class UserProfile (line 23) | class UserProfile(BaseModel):
FILE: pyyoutube/models/base.py
class BaseModel (line 11) | class BaseModel(DataClassJsonMixin):
method from_dict (line 15) | def from_dict(cls: Type[A], kvs: Json, *, infer_missing=False) -> A:
method to_dict_ignore_none (line 20) | def to_dict_ignore_none(self):
FILE: pyyoutube/models/caption.py
class CaptionSnippet (line 14) | class CaptionSnippet(BaseModel, DatetimeTimeMixin):
class Caption (line 37) | class Caption(BaseResource):
class CaptionListResponse (line 48) | class CaptionListResponse(BaseApiResponse):
FILE: pyyoutube/models/category.py
class CategorySnippet (line 14) | class CategorySnippet(BaseModel):
class VideoCategorySnippet (line 24) | class VideoCategorySnippet(CategorySnippet):
class VideoCategory (line 35) | class VideoCategory(BaseResource):
class VideoCategoryListResponse (line 46) | class VideoCategoryListResponse(BaseApiResponse):
FILE: pyyoutube/models/channel.py
class RelatedPlaylists (line 22) | class RelatedPlaylists(BaseModel):
class ChannelBrandingSettingChannel (line 34) | class ChannelBrandingSettingChannel(BaseModel):
class ChannelBrandingSettingImage (line 54) | class ChannelBrandingSettingImage(BaseModel):
class ChannelSnippet (line 65) | class ChannelSnippet(BaseModel, DatetimeTimeMixin):
class ChannelContentDetails (line 83) | class ChannelContentDetails(BaseModel):
class ChannelStatistics (line 94) | class ChannelStatistics(BaseModel):
class ChannelTopicDetails (line 108) | class ChannelTopicDetails(BaseTopicDetails):
class ChannelStatus (line 123) | class ChannelStatus(BaseModel):
class ChannelBrandingSetting (line 138) | class ChannelBrandingSetting(BaseModel):
class ChannelAuditDetails (line 150) | class ChannelAuditDetails(BaseModel):
class ChannelContentOwnerDetails (line 163) | class ChannelContentOwnerDetails(BaseModel):
class Channel (line 174) | class Channel(BaseResource):
class ChannelListResponse (line 195) | class ChannelListResponse(BaseApiResponse):
FILE: pyyoutube/models/channel_banner.py
class ChannelBanner (line 14) | class ChannelBanner(BaseModel):
FILE: pyyoutube/models/channel_section.py
class ChannelSectionSnippet (line 13) | class ChannelSectionSnippet(BaseModel):
class ChannelSectionContentDetails (line 27) | class ChannelSectionContentDetails(BaseModel):
class ChannelSection (line 39) | class ChannelSection(BaseResource):
class ChannelSectionResponse (line 53) | class ChannelSectionResponse(BaseApiResponse):
class ChannelSectionListResponse (line 64) | class ChannelSectionListResponse(ChannelSectionResponse): ...
FILE: pyyoutube/models/comment.py
class CommentSnippetAuthorChannelId (line 14) | class CommentSnippetAuthorChannelId(BaseModel):
class CommentSnippet (line 25) | class CommentSnippet(BaseModel, DatetimeTimeMixin):
class Comment (line 53) | class Comment(BaseResource):
class CommentListResponse (line 64) | class CommentListResponse(BaseApiResponse):
FILE: pyyoutube/models/comment_thread.py
class CommentThreadSnippet (line 14) | class CommentThreadSnippet(BaseModel):
class CommentThreadReplies (line 29) | class CommentThreadReplies(BaseModel):
class CommentThread (line 40) | class CommentThread(BaseResource):
class CommentThreadListResponse (line 52) | class CommentThreadListResponse(BaseApiResponse):
FILE: pyyoutube/models/common.py
class Thumbnail (line 12) | class Thumbnail(BaseModel):
class Thumbnails (line 25) | class Thumbnails(BaseModel):
class Topic (line 40) | class Topic(BaseModel):
class BaseTopicDetails (line 56) | class BaseTopicDetails(BaseModel):
method get_full_topics (line 63) | def get_full_topics(self):
class Localized (line 81) | class Localized(BaseModel):
class PageInfo (line 95) | class PageInfo(BaseModel):
class BaseApiResponse (line 109) | class BaseApiResponse(BaseModel):
class BaseResource (line 127) | class BaseResource(BaseModel):
class ResourceId (line 140) | class ResourceId(BaseModel):
class Player (line 156) | class Player(BaseModel):
FILE: pyyoutube/models/i18n.py
class I18nRegionSnippet (line 13) | class I18nRegionSnippet(BaseModel):
class I18nRegion (line 25) | class I18nRegion(BaseResource):
class I18nRegionListResponse (line 36) | class I18nRegionListResponse(BaseApiResponse):
class I18nLanguageSnippet (line 47) | class I18nLanguageSnippet(BaseModel):
class I18nLanguage (line 59) | class I18nLanguage(BaseResource):
class I18nLanguageListResponse (line 70) | class I18nLanguageListResponse(BaseApiResponse):
FILE: pyyoutube/models/member.py
class MemberSnippetMemberDetails (line 14) | class MemberSnippetMemberDetails(BaseModel):
class MemberSnippetMembershipsDuration (line 28) | class MemberSnippetMembershipsDuration(BaseModel, DatetimeTimeMixin):
class MemberSnippetMembershipsDurationAtLevel (line 34) | class MemberSnippetMembershipsDurationAtLevel(BaseModel):
class MemberSnippetMembershipsDetails (line 41) | class MemberSnippetMembershipsDetails(BaseModel):
class MemberSnippet (line 60) | class MemberSnippet(BaseModel):
class Member (line 77) | class Member(BaseModel):
class MemberListResponse (line 90) | class MemberListResponse(BaseApiResponse):
FILE: pyyoutube/models/memberships_level.py
class MembershipLevelSnippetLevelDetails (line 13) | class MembershipLevelSnippetLevelDetails(BaseModel):
class MembershipsLevelSnippet (line 18) | class MembershipsLevelSnippet(BaseModel):
class MembershipsLevel (line 32) | class MembershipsLevel(BaseResource):
class MembershipsLevelListResponse (line 43) | class MembershipsLevelListResponse(BaseApiResponse):
FILE: pyyoutube/models/mixins.py
class DatetimeTimeMixin (line 14) | class DatetimeTimeMixin:
method string_to_datetime (line 16) | def string_to_datetime(dt_str: Optional[str]) -> Optional[datetime.dat...
FILE: pyyoutube/models/playlist.py
class PlaylistContentDetails (line 14) | class PlaylistContentDetails(BaseModel):
class PlaylistSnippet (line 25) | class PlaylistSnippet(BaseModel, DatetimeTimeMixin):
class PlaylistStatus (line 43) | class PlaylistStatus(BaseModel):
class Playlist (line 54) | class Playlist(BaseResource):
class PlaylistListResponse (line 68) | class PlaylistListResponse(BaseApiResponse):
FILE: pyyoutube/models/playlist_item.py
class PlaylistItemContentDetails (line 14) | class PlaylistItemContentDetails(BaseModel, DatetimeTimeMixin):
class PlaylistItemSnippet (line 29) | class PlaylistItemSnippet(BaseModel, DatetimeTimeMixin):
class PlaylistItemStatus (line 50) | class PlaylistItemStatus(BaseModel):
class PlaylistItem (line 61) | class PlaylistItem(BaseResource):
class PlaylistItemListResponse (line 76) | class PlaylistItemListResponse(BaseApiResponse):
FILE: pyyoutube/models/search_result.py
class SearchResultSnippet (line 14) | class SearchResultSnippet(BaseModel, DatetimeTimeMixin):
class SearchResultId (line 31) | class SearchResultId(BaseModel):
class SearchResult (line 45) | class SearchResult(BaseResource):
class SearchListResponse (line 57) | class SearchListResponse(BaseApiResponse):
FILE: pyyoutube/models/subscription.py
class SubscriptionSnippet (line 14) | class SubscriptionSnippet(BaseModel, DatetimeTimeMixin):
class SubscriptionContentDetails (line 31) | class SubscriptionContentDetails(BaseModel):
class SubscriptionSubscriberSnippet (line 44) | class SubscriptionSubscriberSnippet(BaseModel):
class Subscription (line 58) | class Subscription(BaseResource):
class SubscriptionListResponse (line 75) | class SubscriptionListResponse(BaseApiResponse):
FILE: pyyoutube/models/video.py
class RegionRestriction (line 25) | class RegionRestriction(BaseModel):
class ContentRating (line 37) | class ContentRating(BaseModel):
class VideoContentDetails (line 113) | class VideoContentDetails(BaseModel):
method get_video_seconds_duration (line 130) | def get_video_seconds_duration(self):
class VideoTopicDetails (line 144) | class VideoTopicDetails(BaseTopicDetails):
method __post_init__ (line 158) | def __post_init__(self):
class VideoSnippet (line 169) | class VideoSnippet(BaseModel, DatetimeTimeMixin):
class VideoStatistics (line 191) | class VideoStatistics(BaseModel):
class VideoStatus (line 205) | class VideoStatus(BaseModel, DatetimeTimeMixin):
class VideoRecordingDetails (line 225) | class VideoRecordingDetails(BaseModel, DatetimeTimeMixin):
class VideoLiveStreamingDetails (line 236) | class VideoLiveStreamingDetails(BaseModel, DatetimeTimeMixin):
class PaidProductPlacementDetail (line 252) | class PaidProductPlacementDetail(BaseModel):
class Video (line 257) | class Video(BaseResource):
class VideoListResponse (line 280) | class VideoListResponse(BaseApiResponse):
class VideoReportAbuse (line 291) | class VideoReportAbuse(BaseModel):
class VideoRatingItem (line 304) | class VideoRatingItem(BaseModel):
class VideoGetRatingResponse (line 314) | class VideoGetRatingResponse(BaseApiResponse):
FILE: pyyoutube/models/video_abuse_report_reason.py
class SecondaryReason (line 13) | class SecondaryReason(BaseModel):
class VideoAbuseReportReasonSnippet (line 25) | class VideoAbuseReportReasonSnippet(BaseModel):
class VideoAbuseReportReason (line 37) | class VideoAbuseReportReason(BaseResource):
class VideoAbuseReportReasonListResponse (line 48) | class VideoAbuseReportReasonListResponse(BaseApiResponse):
FILE: pyyoutube/models/watermark.py
class WatermarkTiming (line 12) | class WatermarkTiming(BaseModel):
class WatermarkPosition (line 19) | class WatermarkPosition(BaseModel):
class Watermark (line 25) | class Watermark(BaseModel):
FILE: pyyoutube/resources/activities.py
class ActivitiesResource (line 13) | class ActivitiesResource(Resource):
method list (line 20) | def list(
FILE: pyyoutube/resources/base_resource.py
class Resource (line 11) | class Resource:
method __init__ (line 14) | def __init__(self, client: Optional["Client"] = None):
method access_token (line 18) | def access_token(self):
method api_key (line 22) | def api_key(self):
FILE: pyyoutube/resources/captions.py
class CaptionsResource (line 15) | class CaptionsResource(Resource):
method list (line 21) | def list(
method insert (line 65) | def insert(
method update (line 118) | def update(
method download (line 183) | def download(
method delete (line 229) | def delete(
FILE: pyyoutube/resources/channel_banners.py
class ChannelBannersResource (line 11) | class ChannelBannersResource(Resource):
method insert (line 18) | def insert(
FILE: pyyoutube/resources/channel_sections.py
class ChannelSectionsResource (line 13) | class ChannelSectionsResource(Resource):
method list (line 19) | def list(
method insert (line 89) | def insert(
method update (line 151) | def update(
method delete (line 202) | def delete(
FILE: pyyoutube/resources/channels.py
class ChannelsResource (line 13) | class ChannelsResource(Resource):
method list (line 19) | def list(
method update (line 122) | def update(
FILE: pyyoutube/resources/comment_threads.py
class CommentThreadsResource (line 13) | class CommentThreadsResource(Resource):
method list (line 20) | def list(
method insert (line 118) | def insert(
FILE: pyyoutube/resources/comments.py
class CommentsResource (line 13) | class CommentsResource(Resource):
method list (line 19) | def list(
method insert (line 87) | def insert(
method update (line 124) | def update(
method mark_as_spam (line 158) | def mark_as_spam(
method set_moderation_status (line 188) | def set_moderation_status(
method delete (line 232) | def delete(
FILE: pyyoutube/resources/i18n_languages.py
class I18nLanguagesResource (line 12) | class I18nLanguagesResource(Resource):
method list (line 19) | def list(
FILE: pyyoutube/resources/i18n_regions.py
class I18nRegionsResource (line 12) | class I18nRegionsResource(Resource):
method list (line 19) | def list(
FILE: pyyoutube/resources/members.py
class MembersResource (line 12) | class MembersResource(Resource):
method list (line 18) | def list(
FILE: pyyoutube/resources/membership_levels.py
class MembershipLevelsResource (line 12) | class MembershipLevelsResource(Resource):
method list (line 18) | def list(
FILE: pyyoutube/resources/playlist_items.py
class PlaylistItemsResource (line 13) | class PlaylistItemsResource(Resource):
method list (line 21) | def list(
method insert (line 98) | def insert(
method update (line 145) | def update(
method delete (line 192) | def delete(
FILE: pyyoutube/resources/playlists.py
class PlaylistsResource (line 13) | class PlaylistsResource(Resource):
method list (line 19) | def list(
method insert (line 112) | def insert(
method update (line 166) | def update(
method delete (line 211) | def delete(
FILE: pyyoutube/resources/search.py
class SearchResource (line 12) | class SearchResource(Resource):
method list (line 19) | def list(
FILE: pyyoutube/resources/subscriptions.py
class SubscriptionsResource (line 13) | class SubscriptionsResource(Resource):
method list (line 19) | def list(
method insert (line 135) | def insert(
method delete (line 174) | def delete(
FILE: pyyoutube/resources/thumbnails.py
class ThumbnailsResource (line 11) | class ThumbnailsResource(Resource):
method set (line 17) | def set(
FILE: pyyoutube/resources/video_abuse_report_reasons.py
class VideoAbuseReportReasonsResource (line 12) | class VideoAbuseReportReasonsResource(Resource):
method list (line 19) | def list(
FILE: pyyoutube/resources/video_categories.py
class VideoCategoriesResource (line 13) | class VideoCategoriesResource(Resource):
method list (line 19) | def list(
FILE: pyyoutube/resources/videos.py
class VideosResource (line 19) | class VideosResource(Resource):
method list (line 25) | def list(
method insert (line 125) | def insert(
method update (line 219) | def update(
method rate (line 268) | def rate(
method get_rating (line 306) | def get_rating(
method report_abuse (line 345) | def report_abuse(
method delete (line 385) | def delete(
FILE: pyyoutube/resources/watermarks.py
class WatermarksResource (line 12) | class WatermarksResource(Resource):
method set (line 13) | def set(
method unset (line 60) | def unset(
FILE: pyyoutube/utils/params_checker.py
function enf_comma_separated (line 15) | def enf_comma_separated(
function enf_parts (line 57) | def enf_parts(resource: str, value: Optional[Union[str, list, tuple, set...
FILE: pyyoutube/youtube_utils.py
function get_video_duration (line 11) | def get_video_duration(duration: str) -> int:
FILE: tests/apis/test_activities.py
class ApiActivitiesTest (line 9) | class ApiActivitiesTest(unittest.TestCase):
method setUp (line 23) | def setUp(self) -> None:
method testGetChannelActivities (line 27) | def testGetChannelActivities(self) -> None:
method testGetMineActivities (line 60) | def testGetMineActivities(self) -> None:
FILE: tests/apis/test_auth.py
class TestOAuthApi (line 10) | class TestOAuthApi(unittest.TestCase):
method setUp (line 18) | def setUp(self) -> None:
method testInitApi (line 21) | def testInitApi(self) -> None:
method testOAuth (line 25) | def testOAuth(self) -> None:
method testGetProfile (line 59) | def testGetProfile(self) -> None:
FILE: tests/apis/test_captions.py
class ApiCaptionsTest (line 9) | class ApiCaptionsTest(unittest.TestCase):
method setUp (line 18) | def setUp(self) -> None:
method testGetCaptionByVideo (line 21) | def testGetCaptionByVideo(self) -> None:
FILE: tests/apis/test_categories.py
class ApiVideoCategoryTest (line 9) | class ApiVideoCategoryTest(unittest.TestCase):
method setUp (line 20) | def setUp(self) -> None:
method testGetVideoCategories (line 23) | def testGetVideoCategories(self) -> None:
FILE: tests/apis/test_channel_sections.py
class ApiChannelSectionTest (line 8) | class ApiChannelSectionTest(unittest.TestCase):
method setUp (line 19) | def setUp(self) -> None:
method testGetChannelSectionsById (line 22) | def testGetChannelSectionsById(self) -> None:
method testGetChannelSectionsByChannel (line 47) | def testGetChannelSectionsByChannel(self) -> None:
FILE: tests/apis/test_channels.py
class ApiChannelTest (line 10) | class ApiChannelTest(unittest.TestCase):
method setUp (line 19) | def setUp(self) -> None:
method testSendRequest (line 22) | def testSendRequest(self) -> None:
method testParseResponse (line 38) | def testParseResponse(self) -> None:
method testGetChannelInfo (line 50) | def testGetChannelInfo(self) -> None:
FILE: tests/apis/test_comment_threads.py
class ApiCommentThreadTest (line 8) | class ApiCommentThreadTest(unittest.TestCase):
method setUp (line 27) | def setUp(self) -> None:
method testGetCommentThreadById (line 31) | def testGetCommentThreadById(self) -> None:
method testGetCommentThreads (line 66) | def testGetCommentThreads(self) -> None:
FILE: tests/apis/test_comments.py
class ApiCommentTest (line 9) | class ApiCommentTest(unittest.TestCase):
method setUp (line 22) | def setUp(self) -> None:
method testGetCommentById (line 25) | def testGetCommentById(self) -> None:
method testGetCommentsByParentId (line 52) | def testGetCommentsByParentId(self) -> None:
FILE: tests/apis/test_i18ns.py
class ApiI18nTest (line 8) | class ApiI18nTest(unittest.TestCase):
method setUp (line 18) | def setUp(self) -> None:
method testGetI18nRegions (line 21) | def testGetI18nRegions(self) -> None:
method testGetI18nLanguages (line 33) | def testGetI18nLanguages(self) -> None:
FILE: tests/apis/test_members.py
class ApiMembersTest (line 8) | class ApiMembersTest(unittest.TestCase):
method setUp (line 18) | def setUp(self) -> None:
method testGetMembers (line 21) | def testGetMembers(self) -> None:
method testGetMembershipLevels (line 38) | def testGetMembershipLevels(self) -> None:
FILE: tests/apis/test_playlist_items.py
class ApiPlaylistItemTest (line 9) | class ApiPlaylistItemTest(unittest.TestCase):
method setUp (line 24) | def setUp(self) -> None:
method testGetPlaylistItemById (line 27) | def testGetPlaylistItemById(self) -> None:
method testGetPlaylistItems (line 59) | def testGetPlaylistItems(self) -> None:
FILE: tests/apis/test_playlists.py
class ApiPlaylistTest (line 9) | class ApiPlaylistTest(unittest.TestCase):
method setUp (line 24) | def setUp(self) -> None:
method testGetPlaylistById (line 28) | def testGetPlaylistById(self) -> None:
method testGetPlaylists (line 57) | def testGetPlaylists(self) -> None:
FILE: tests/apis/test_search.py
class ApiSearchTest (line 9) | class ApiSearchTest(unittest.TestCase):
method setUp (line 13) | def setUp(self) -> None:
method testSearch (line 16) | def testSearch(self) -> None:
method testSearchByKeywords (line 90) | def testSearchByKeywords(self) -> None:
method testSearchByRelatedToVideoId (line 122) | def testSearchByRelatedToVideoId(self) -> None:
method testSearchByDeveloper (line 142) | def testSearchByDeveloper(self) -> None:
method testSearchByMine (line 170) | def testSearchByMine(self) -> None:
FILE: tests/apis/test_subscriptions.py
class ApiPlaylistTest (line 9) | class ApiPlaylistTest(unittest.TestCase):
method setUp (line 30) | def setUp(self) -> None:
method testGetSubscriptionById (line 34) | def testGetSubscriptionById(self) -> None:
method testGetSubscriptionByChannel (line 69) | def testGetSubscriptionByChannel(self) -> None:
method testGetSubscriptionByMe (line 127) | def testGetSubscriptionByMe(self) -> None:
FILE: tests/apis/test_video_abuse_reason.py
class ApiVideoAbuseReason (line 8) | class ApiVideoAbuseReason(unittest.TestCase):
method setUp (line 15) | def setUp(self) -> None:
method testGetVideoAbuseReportReason (line 18) | def testGetVideoAbuseReportReason(self) -> None:
FILE: tests/apis/test_videos.py
class ApiVideoTest (line 9) | class ApiVideoTest(unittest.TestCase):
method setUp (line 26) | def setUp(self) -> None:
method testGetVideoById (line 30) | def testGetVideoById(self) -> None:
method testGetVideoByChart (line 65) | def testGetVideoByChart(self) -> None:
method testGetVideoByMyRating (line 115) | def testGetVideoByMyRating(self) -> None:
FILE: tests/clients/base.py
class BaseTestCase (line 6) | class BaseTestCase:
method url (line 12) | def url(self):
method load_json (line 15) | def load_json(self, filename, helpers):
FILE: tests/clients/test_activities.py
class TestActivitiesResource (line 8) | class TestActivitiesResource(BaseTestCase):
method test_list (line 11) | def test_list(self, helpers, authed_cli):
FILE: tests/clients/test_captions.py
class TestCaptionsResource (line 16) | class TestCaptionsResource(BaseTestCase):
method test_list (line 19) | def test_list(self, helpers, key_cli):
method test_insert (line 30) | def test_insert(self, helpers, authed_cli):
method test_update (line 50) | def test_update(self, helpers, authed_cli):
method test_download (line 81) | def test_download(self, authed_cli):
method test_delete (line 92) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_channel_banners.py
class TestChannelBanners (line 11) | class TestChannelBanners(BaseTestCase):
method test_insert (line 12) | def test_insert(self, helpers, authed_cli):
FILE: tests/clients/test_channel_sections.py
class TestChannelBannersResource (line 9) | class TestChannelBannersResource(BaseTestCase):
method test_list (line 12) | def test_list(self, helpers, authed_cli, key_cli):
method test_insert (line 51) | def test_insert(self, helpers, authed_cli):
method test_update (line 72) | def test_update(self, helpers, authed_cli):
method test_delete (line 91) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_channels.py
class TestChannelsResource (line 9) | class TestChannelsResource(BaseTestCase):
method test_list (line 13) | def test_list(self, helpers, authed_cli, key_cli):
method test_update (line 67) | def test_update(self, helpers, authed_cli):
FILE: tests/clients/test_client.py
class TestClient (line 14) | class TestClient(BaseTestCase):
method test_initial (line 18) | def test_initial(self):
method test_client_secret_web (line 25) | def test_client_secret_web(self):
method test_client_secret_installed (line 34) | def test_client_secret_installed(self):
method test_client_secret_bad (line 43) | def test_client_secret_bad(self):
method test_request (line 56) | def test_request(self, key_cli):
method test_parse_response (line 70) | def test_parse_response(self, key_cli, helpers):
method test_oauth (line 81) | def test_oauth(self, helpers):
method test_subscribe_push_notification (line 117) | def test_subscribe_push_notification(self):
FILE: tests/clients/test_comment_threads.py
class TestCommentThreadsResource (line 9) | class TestCommentThreadsResource(BaseTestCase):
method test_list (line 12) | def test_list(self, helpers, key_cli):
method test_insert (line 49) | def test_insert(self, helpers, authed_cli):
FILE: tests/clients/test_comments.py
class TestCommentsResource (line 9) | class TestCommentsResource(BaseTestCase):
method test_list (line 12) | def test_list(self, helpers, key_cli):
method test_insert (line 39) | def test_insert(self, helpers, authed_cli):
method test_update (line 58) | def test_update(self, helpers, authed_cli):
method test_mark_as_spam (line 76) | def test_mark_as_spam(self, helpers, authed_cli):
method test_set_moderation_status (line 96) | def test_set_moderation_status(self, helpers, authed_cli):
method test_delete (line 119) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_i18n.py
class TestI18nLanguagesResource (line 6) | class TestI18nLanguagesResource(BaseTestCase):
method test_list (line 9) | def test_list(self, helpers, key_cli):
class TestI18nRegionsResource (line 22) | class TestI18nRegionsResource(BaseTestCase):
method test_list (line 25) | def test_list(self, helpers, key_cli):
FILE: tests/clients/test_media.py
class TestMedia (line 15) | class TestMedia:
method test_initial (line 16) | def test_initial(self, tmp_path):
class TestMediaUploadProgress (line 34) | class TestMediaUploadProgress:
method test_progress (line 35) | def test_progress(self):
class TestMediaUpload (line 44) | class TestMediaUpload:
method test_upload (line 45) | def test_upload(self, helpers, authed_cli):
method test_upload_response (line 86) | def test_upload_response(self, authed_cli, helpers):
FILE: tests/clients/test_members.py
class TestMembersResource (line 6) | class TestMembersResource(BaseTestCase):
method test_list (line 9) | def test_list(self, helpers, authed_cli):
FILE: tests/clients/test_membership_levels.py
class TestMembershipLevelsResource (line 6) | class TestMembershipLevelsResource(BaseTestCase):
method test_list (line 9) | def test_list(self, helpers, authed_cli):
FILE: tests/clients/test_playlist_items.py
class TestPlaylistItemsResource (line 9) | class TestPlaylistItemsResource(BaseTestCase):
method test_list (line 12) | def test_list(self, helpers, key_cli):
method test_insert (line 41) | def test_insert(self, helpers, authed_cli):
method test_update (line 63) | def test_update(self, helpers, authed_cli):
method test_delete (line 85) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_playlists.py
class TestPlaylistsResource (line 9) | class TestPlaylistsResource(BaseTestCase):
method test_list (line 12) | def test_list(self, helpers, authed_cli, key_cli):
method test_insert (line 44) | def test_insert(self, helpers, authed_cli):
method test_update (line 62) | def test_update(self, helpers, authed_cli):
method test_delete (line 80) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_search.py
class TestSearchResource (line 6) | class TestSearchResource(BaseTestCase):
method test_list (line 9) | def test_list(self, helpers, authed_cli, key_cli):
FILE: tests/clients/test_subscriptions.py
class TestSubscriptionsResource (line 9) | class TestSubscriptionsResource(BaseTestCase):
method test_list (line 12) | def test_list(self, helpers, key_cli, authed_cli):
method test_inset (line 58) | def test_inset(self, helpers, authed_cli):
method test_delete (line 77) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_thumbnails.py
class TestThumbnailsResource (line 11) | class TestThumbnailsResource(BaseTestCase):
method test_set (line 14) | def test_set(self, authed_cli):
FILE: tests/clients/test_video_abuse_report_reasons.py
class TestVideoAbuseReportReasonsResource (line 6) | class TestVideoAbuseReportReasonsResource(BaseTestCase):
method test_list (line 9) | def test_list(self, helpers, authed_cli):
FILE: tests/clients/test_video_categories.py
class TestVideoCategoriesResource (line 8) | class TestVideoCategoriesResource(BaseTestCase):
method test_list (line 11) | def test_list(self, helpers, key_cli):
FILE: tests/clients/test_videos.py
class TestVideosResource (line 12) | class TestVideosResource(BaseTestCase):
method test_list (line 15) | def test_list(self, helpers, authed_cli, key_cli):
method test_insert (line 51) | def test_insert(self, helpers, authed_cli):
method test_update (line 67) | def test_update(self, helpers, authed_cli):
method test_rate (line 83) | def test_rate(self, helpers, authed_cli):
method test_get_rating (line 107) | def test_get_rating(self, helpers, authed_cli):
method test_report_abuse (line 122) | def test_report_abuse(self, helpers, authed_cli):
method test_delete (line 142) | def test_delete(self, helpers, authed_cli):
FILE: tests/clients/test_watermarks.py
class TestWatermarksResource (line 16) | class TestWatermarksResource(BaseTestCase):
method test_set (line 19) | def test_set(self, authed_cli):
method test_unset (line 40) | def test_unset(self, helpers, authed_cli):
FILE: tests/conftest.py
class Helpers (line 8) | class Helpers:
method load_json (line 10) | def load_json(filename):
method load_file_binary (line 15) | def load_file_binary(filename):
function helpers (line 21) | def helpers():
function authed_cli (line 26) | def authed_cli():
function key_cli (line 31) | def key_cli():
FILE: tests/models/test_abuse_reason.py
class AbuseReasonModelTest (line 7) | class AbuseReasonModelTest(unittest.TestCase):
method testAbuseReason (line 15) | def testAbuseReason(self) -> None:
method testAbuseReasonResponse (line 22) | def testAbuseReasonResponse(self) -> None:
FILE: tests/models/test_activities.py
class ActivityModelTest (line 7) | class ActivityModelTest(unittest.TestCase):
method testActivityContentDetails (line 19) | def testActivityContentDetails(self) -> None:
method testActivitySnippet (line 24) | def testActivitySnippet(self) -> None:
method testActivity (line 32) | def testActivity(self) -> None:
method testActivityListResponse (line 38) | def testActivityListResponse(self) -> None:
FILE: tests/models/test_auth_models.py
class AuthModelTest (line 6) | class AuthModelTest(unittest.TestCase):
method testAccessToken (line 13) | def testAccessToken(self) -> None:
method testUserProfile (line 18) | def testUserProfile(self) -> None:
FILE: tests/models/test_captions.py
class CaptionModelTest (line 7) | class CaptionModelTest(unittest.TestCase):
method testCaptionSnippet (line 17) | def testCaptionSnippet(self):
method testCaption (line 26) | def testCaption(self):
method testCaptionListResponse (line 32) | def testCaptionListResponse(self):
FILE: tests/models/test_category.py
class CategoryModelTest (line 7) | class CategoryModelTest(unittest.TestCase):
method testVideoCategory (line 19) | def testVideoCategory(self) -> None:
method testVideoCategoryListResponse (line 24) | def testVideoCategoryListResponse(self) -> None:
FILE: tests/models/test_channel.py
class ChannelModelTest (line 6) | class ChannelModelTest(unittest.TestCase):
method testChannelBrandingSettings (line 26) | def testChannelBrandingSettings(self) -> None:
method testChannelContentDetails (line 31) | def testChannelContentDetails(self) -> None:
method testChannelTopicDetails (line 36) | def testChannelTopicDetails(self) -> None:
method testChannelSnippet (line 46) | def testChannelSnippet(self) -> None:
method testChannelStatistics (line 59) | def testChannelStatistics(self) -> None:
method testChannelStatus (line 64) | def testChannelStatus(self) -> None:
method testChannel (line 69) | def testChannel(self) -> None:
method testChannelListResponse (line 74) | def testChannelListResponse(self) -> None:
FILE: tests/models/test_channel_sections.py
class ChannelSectionModelTest (line 7) | class ChannelSectionModelTest(unittest.TestCase):
method testChannelSection (line 15) | def testChannelSection(self) -> None:
method testChannelSectionResponse (line 22) | def testChannelSectionResponse(self) -> None:
FILE: tests/models/test_comments.py
class CommentModelModelTest (line 7) | class CommentModelModelTest(unittest.TestCase):
method testCommentSnippet (line 17) | def testCommentSnippet(self) -> None:
method testComment (line 27) | def testComment(self) -> None:
method testCommentListResponse (line 37) | def testCommentListResponse(self) -> None:
class CommentThreadModelTest (line 44) | class CommentThreadModelTest(unittest.TestCase):
method testCommentThreadSnippet (line 56) | def testCommentThreadSnippet(self) -> None:
method testCommentThreadReplies (line 63) | def testCommentThreadReplies(self) -> None:
method testCommentThread (line 72) | def testCommentThread(self) -> None:
method testCommentThreadListResponse (line 82) | def testCommentThreadListResponse(self) -> None:
FILE: tests/models/test_i18n_models.py
class I18nModelTest (line 8) | class I18nModelTest(unittest.TestCase):
method testI18nRegion (line 20) | def testI18nRegion(self) -> None:
method testI18nRegionResponse (line 26) | def testI18nRegionResponse(self) -> None:
method testI18nLanguage (line 32) | def testI18nLanguage(self) -> None:
method testI18nLanguageResponse (line 38) | def testI18nLanguageResponse(self) -> None:
FILE: tests/models/test_members.py
class MemberModelTest (line 7) | class MemberModelTest(unittest.TestCase):
method testMember (line 13) | def testMember(self) -> None:
class MembershipLevelModelTest (line 21) | class MembershipLevelModelTest(unittest.TestCase):
method testMembershipLevel (line 27) | def testMembershipLevel(self) -> None:
FILE: tests/models/test_playlist.py
class PlaylistModelTest (line 7) | class PlaylistModelTest(unittest.TestCase):
method testPlayListContentDetails (line 21) | def testPlayListContentDetails(self) -> None:
method testPlayListSnippet (line 26) | def testPlayListSnippet(self) -> None:
method testPlayListStatus (line 38) | def testPlayListStatus(self) -> None:
method testPlayList (line 43) | def testPlayList(self) -> None:
method testPlaylistListResponse (line 50) | def testPlaylistListResponse(self) -> None:
FILE: tests/models/test_playlist_item.py
class PlaylistItemModelTest (line 7) | class PlaylistItemModelTest(unittest.TestCase):
method testPlaylistItemContentDetails (line 21) | def testPlaylistItemContentDetails(self) -> None:
method testPlaylistItemSnippet (line 30) | def testPlaylistItemSnippet(self) -> None:
method testPlaylistItemStatus (line 42) | def testPlaylistItemStatus(self) -> None:
method testPlaylistItem (line 47) | def testPlaylistItem(self) -> None:
method testPlaylistItemListResponse (line 59) | def testPlaylistItemListResponse(self) -> None:
FILE: tests/models/test_search_result.py
class SearchResultModelTest (line 7) | class SearchResultModelTest(unittest.TestCase):
method testSearchResultId (line 19) | def testSearchResultId(self):
method testSearchResultSnippet (line 23) | def testSearchResultSnippet(self):
method testSearchResult (line 35) | def testSearchResult(self):
method testSearchListResponse (line 40) | def testSearchListResponse(self):
FILE: tests/models/test_subscriptions.py
class SubscriptionModelTest (line 6) | class SubscriptionModelTest(unittest.TestCase):
method testSubscriptionSnippet (line 20) | def testSubscriptionSnippet(self) -> None:
method testSubscriptionContentDetails (line 30) | def testSubscriptionContentDetails(self) -> None:
method testSubscriptionSubscriberSnippet (line 36) | def testSubscriptionSubscriberSnippet(self) -> None:
method testSubscription (line 45) | def testSubscription(self) -> None:
method testSubscriptionResponse (line 53) | def testSubscriptionResponse(self) -> None:
FILE: tests/models/test_videos.py
class VideoModelTest (line 7) | class VideoModelTest(unittest.TestCase):
method testVideoContentDetails (line 29) | def testVideoContentDetails(self) -> None:
method testVideoTopicDetails (line 44) | def testVideoTopicDetails(self) -> None:
method testVideoSnippet (line 55) | def testVideoSnippet(self) -> None:
method testVideoStatistics (line 77) | def testVideoStatistics(self) -> None:
method testVideoStatus (line 82) | def testVideoStatus(self) -> None:
method testVideo (line 91) | def testVideo(self) -> None:
method testVideoListResponse (line 99) | def testVideoListResponse(self) -> None:
method testVideoRecordingDetails (line 105) | def testVideoRecordingDetails(self) -> None:
method testPaidProductPlacementDetail (line 113) | def testPaidProductPlacementDetail(self) -> None:
FILE: tests/test_error_handling.py
class ErrorTest (line 8) | class ErrorTest(unittest.TestCase):
method testResponseError (line 16) | def testResponseError(self) -> None:
method testResponseErrorSimple (line 30) | def testResponseErrorSimple(self) -> None:
method testErrorMessage (line 38) | def testErrorMessage(self):
FILE: tests/test_youtube_utils.py
class UtilsTest (line 7) | class UtilsTest(unittest.TestCase):
method testDurationConvert (line 8) | def testDurationConvert(self):
FILE: tests/utils/test_params_checker.py
class ParamCheckerTest (line 7) | class ParamCheckerTest(unittest.TestCase):
method testEnfCommaSeparated (line 8) | def testEnfCommaSeparated(self) -> None:
method testEnfParts (line 21) | def testEnfParts(self) -> None:
Condensed preview — 309 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,161K chars).
[
{
"path": ".bumpversion.cfg",
"chars": 143,
"preview": "[bumpversion]\ncurrent_version = 0.9.9\ncommit = True\ntag = True\n\n[bumpversion:file:pyyoutube/__version__.py]\n\n[bumpversio"
},
{
"path": ".coveragerc",
"chars": 37,
"preview": "[run]\nomit = pyyoutube/__version__.py"
},
{
"path": ".github/hack/changelog.sh",
"chars": 624,
"preview": "#!/bin/sh\n\nMARKER_PREFIX=\"## Version\"\nVERSION=$(echo \"$1\" | sed 's/^v//g')\n\nIFS=''\nfound=0\n\nwhile read -r \"line\"; do\n #"
},
{
"path": ".github/hack/version.sh",
"chars": 420,
"preview": "#!/bin/sh\n\nLATEST_TAG_REV=$(git rev-list --tags --max-count=1)\nLATEST_COMMIT_REV=$(git rev-list HEAD --max-count=1)\n\nif "
},
{
"path": ".github/workflows/docs.yaml",
"chars": 438,
"preview": "name: Publish docs via GitHub Pages\non:\n push:\n branches:\n - master\njobs:\n build:\n name: Deploy docs\n ru"
},
{
"path": ".github/workflows/release.yaml",
"chars": 617,
"preview": "name: Publish Pypi\non:\n push:\n tags:\n - 'v*.*.*'\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - use"
},
{
"path": ".github/workflows/test.yaml",
"chars": 1614,
"preview": "name: Test\n\non:\n pull_request:\n push:\n branches:\n - master\n\njobs:\n test:\n runs-on: ubuntu-latest\n strat"
},
{
"path": ".gitignore",
"chars": 1717,
"preview": "# Created by .ignore support plugin (hsz.mobi)\n### Python template\n# Byte-compiled / optimized / DLL files\n__pycache__/\n"
},
{
"path": "CHANGELOG.md",
"chars": 5040,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## Version 0.9.9 (2026-04-17)\n\n### Wh"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2020 sns-sdks\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
},
{
"path": "Makefile",
"chars": 1020,
"preview": "all: help clean lint test\n\n.PHONY: all\n\nhelp:\n\t@echo \" env install all dependencies\"\n\t@echo \" clean remo"
},
{
"path": "README.rst",
"chars": 6380,
"preview": "Python YouTube\n\nA Python wrapper for the YouTube Data API V3.\n\n.. image:: https://github.com/sns-sdks/python-youtube/wor"
},
{
"path": "docs/docs/authorization.md",
"chars": 3591,
"preview": "If you want to get more data for your channel, You need provide the authorization.\n\nThis doc shows how to authorize a cl"
},
{
"path": "docs/docs/getting_started.md",
"chars": 2299,
"preview": "This document is a simple tutorial to show how to use this library to get data from YouTube data API.\n\nYou can get the w"
},
{
"path": "docs/docs/index.md",
"chars": 663,
"preview": "# Welcome to Python-Youtube's documentation!\n\n**A Python wrapper around for YouTube Data API.**\n\nAuthor: IkarosKun <merl"
},
{
"path": "docs/docs/installation.md",
"chars": 1027,
"preview": "This library supports Python 3.6 and newer.\n\n## Dependencies\n\nThese following distributions will be installed automatica"
},
{
"path": "docs/docs/introduce-new-structure.md",
"chars": 764,
"preview": "This doc will show you the new api structure for this library.\n\n## Brief\n\nTo make the package easier to maintain and eas"
},
{
"path": "docs/docs/usage/work-with-api.md",
"chars": 22081,
"preview": "# Work with Api\n\n!!! note \"Tips\"\n\n This is the previous version to operate YouTube DATA API.\n\n We recommend using "
},
{
"path": "docs/docs/usage/work-with-client.md",
"chars": 6038,
"preview": "# Work with Client\n\nWe have refactored the project code to support more methods and improve code usability.\n\nAnd new str"
},
{
"path": "docs/mkdocs.yml",
"chars": 1366,
"preview": "site_name: Python-Youtube Docs\nsite_description: Docs for python-youtube library\nsite_url: https://sns-sdks.github.io/py"
},
{
"path": "examples/README.md",
"chars": 861,
"preview": "# Examples\n\nWe provide two entry points to operate the YouTube DATA API.\n\n- Api `from pyyoutube import Api`: This is an "
},
{
"path": "examples/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "examples/apis/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "examples/apis/channel_videos.py",
"chars": 1140,
"preview": "\"\"\"\nRetrieve some videos info from given channel.\n\nUse pyyoutube.api.get_channel_info to get channel video uploads playl"
},
{
"path": "examples/apis/get_all_videos_id_with_channel_by_search.py",
"chars": 725,
"preview": "\"\"\"\nRetrieve channel's videos by search api.\n\nNote Quota impact: A call to this method has a quota cost of 100 units.\n\"\""
},
{
"path": "examples/apis/get_subscription_with_oauth.py",
"chars": 1045,
"preview": "\"\"\"\nThis demo show how to use this library to do authorization and get your subscription.\n\"\"\"\n\nimport pyyoutube\nimport w"
},
{
"path": "examples/apis/oauth_flow.py",
"chars": 870,
"preview": "\"\"\"\nThis example demonstrates how to perform authorization.\n\"\"\"\n\nfrom pyyoutube import Api\n\nCLIENT_ID = \"xxx\" # Your ap"
},
{
"path": "examples/clients/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "examples/clients/channel_info.py",
"chars": 492,
"preview": "\"\"\"\nThis example demonstrates how to retrieve information for a channel.\n\"\"\"\n\nfrom pyyoutube import Client\n\nAPI_KEY = \"Y"
},
{
"path": "examples/clients/oauth_flow.py",
"chars": 1071,
"preview": "\"\"\"\nThis example demonstrates how to perform authorization.\n\"\"\"\n\nfrom pyyoutube import Client\n\nCLIENT_ID = \"xxx\" # Your"
},
{
"path": "examples/clients/oauth_refreshing.py",
"chars": 2605,
"preview": "\"\"\"\nThis example demonstrates how to automatically (re)generate tokens for continuous OAuth.\nWe store the Access Token i"
},
{
"path": "examples/clients/upload_video.py",
"chars": 1133,
"preview": "\"\"\"\nThis example demonstrates how to upload a video.\n\"\"\"\n\nimport pyyoutube.models as mds\nfrom pyyoutube import Client\nfr"
},
{
"path": "pyproject.toml",
"chars": 1483,
"preview": "[tool.poetry]\nname = \"python-youtube\"\nversion = \"0.9.9\"\ndescription = \"A Python wrapper around for YouTube Data API.\"\nau"
},
{
"path": "pytest.ini",
"chars": 51,
"preview": "[pytest]\naddopts = --cov=pyyoutube --cov-report xml"
},
{
"path": "pyyoutube/__init__.py",
"chars": 167,
"preview": "from .api import Api # noqa\nfrom .client import Client # noqa\nfrom .error import * # noqa\nfrom .models import * # no"
},
{
"path": "pyyoutube/__version__.py",
"chars": 705,
"preview": "# d8888b. db db d888888b db db .d88b. d8b db db db .d88b. db db d888888b db db d8888b. d88888b\n# 88 "
},
{
"path": "pyyoutube/api.py",
"chars": 101794,
"preview": "\"\"\"\nMain Api implementation.\n\"\"\"\n\nfrom typing import Optional, List, Union\n\nimport requests\nfrom requests.auth import HT"
},
{
"path": "pyyoutube/client.py",
"chars": 20421,
"preview": "\"\"\"\nNew Client for YouTube API\n\"\"\"\n\nimport inspect\nimport json\nfrom typing import List, Optional, Tuple, Union\n\nimport r"
},
{
"path": "pyyoutube/error.py",
"chars": 2381,
"preview": "from dataclasses import dataclass\nfrom typing import Optional, Union\n\nfrom requests import Response\n\n__all__ = [\"ErrorCo"
},
{
"path": "pyyoutube/media.py",
"chars": 7026,
"preview": "\"\"\"\nMedia object to upload.\n\"\"\"\n\nimport mimetypes\nimport os\nfrom typing import IO, Optional, Tuple\n\nfrom requests import"
},
{
"path": "pyyoutube/models/__init__.py",
"chars": 697,
"preview": "from .activity import * # noqa\nfrom .auth import AccessToken, UserProfile\nfrom .caption import * # noqa\nfrom .category"
},
{
"path": "pyyoutube/models/activity.py",
"chars": 6262,
"preview": "\"\"\"\nThese are activity related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Optional\n\n"
},
{
"path": "pyyoutube/models/auth.py",
"chars": 1212,
"preview": "from dataclasses import dataclass, field\nfrom typing import List, Optional\n\nfrom .base import BaseModel\n\n\n@dataclass\ncla"
},
{
"path": "pyyoutube/models/base.py",
"chars": 690,
"preview": "from dataclasses import dataclass, asdict\nfrom typing import Type, TypeVar\n\nfrom dataclasses_json import DataClassJsonMi"
},
{
"path": "pyyoutube/models/caption.py",
"chars": 1766,
"preview": "\"\"\"\nThese are caption related models\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Optional\n\nfr"
},
{
"path": "pyyoutube/models/category.py",
"chars": 1292,
"preview": "\"\"\"\nThese are category related models.\nInclude VideoCategory\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing i"
},
{
"path": "pyyoutube/models/channel.py",
"chars": 6761,
"preview": "\"\"\"\nThese are channel related models.\n\nReferences: https://developers.google.com/youtube/v3/docs/channels#properties\n\"\"\""
},
{
"path": "pyyoutube/models/channel_banner.py",
"chars": 588,
"preview": "\"\"\"\nThere are channel banner related models\n\nReferences: https://developers.google.com/youtube/v3/docs/channelBanners#pr"
},
{
"path": "pyyoutube/models/channel_section.py",
"chars": 1776,
"preview": "\"\"\"\nThose are models related to channel sections.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List,"
},
{
"path": "pyyoutube/models/comment.py",
"chars": 2353,
"preview": "\"\"\"\nThese are comment related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Optional\n\nf"
},
{
"path": "pyyoutube/models/comment_thread.py",
"chars": 1734,
"preview": "\"\"\"\nThese are comment threads related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import Optional,"
},
{
"path": "pyyoutube/models/common.py",
"chars": 5082,
"preview": "\"\"\"\nThese are common models for multi resource.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import Optiona"
},
{
"path": "pyyoutube/models/i18n.py",
"chars": 1940,
"preview": "\"\"\"\nThese are i18n language and region related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import "
},
{
"path": "pyyoutube/models/member.py",
"chars": 2954,
"preview": "\"\"\"\nThese are member related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Optional\n\nfr"
},
{
"path": "pyyoutube/models/memberships_level.py",
"chars": 1301,
"preview": "\"\"\"\nThese are membership level related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Op"
},
{
"path": "pyyoutube/models/mixins.py",
"chars": 810,
"preview": "\"\"\"\nThese are some mixin for models\n\"\"\"\n\nimport datetime\nfrom typing import Optional\n\nimport isodate\nfrom isodate.isoerr"
},
{
"path": "pyyoutube/models/playlist.py",
"chars": 2247,
"preview": "\"\"\"\nThese are playlist related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import Optional, List\n\n"
},
{
"path": "pyyoutube/models/playlist_item.py",
"chars": 2747,
"preview": "\"\"\"\nThese are playlistItem related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Option"
},
{
"path": "pyyoutube/models/search_result.py",
"chars": 2007,
"preview": "\"\"\"\nThese are search result related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import Optional, L"
},
{
"path": "pyyoutube/models/subscription.py",
"chars": 2566,
"preview": "\"\"\"\nThese are subscription related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import List, Option"
},
{
"path": "pyyoutube/models/video.py",
"chars": 12874,
"preview": "\"\"\"\nThese are video related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import Optional, List\n\nimp"
},
{
"path": "pyyoutube/models/video_abuse_report_reason.py",
"chars": 1529,
"preview": "\"\"\"\nThese are video abuse report reason related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import"
},
{
"path": "pyyoutube/models/watermark.py",
"chars": 1059,
"preview": "\"\"\"\nThese are watermark related models.\n\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom typing import Optional\n\nfrom "
},
{
"path": "pyyoutube/resources/__init__.py",
"chars": 1089,
"preview": "from .activities import ActivitiesResource # noqa\nfrom .captions import CaptionsResource # noqa\nfrom .channel_banners "
},
{
"path": "pyyoutube/resources/activities.py",
"chars": 3700,
"preview": "\"\"\"\nActivities resource implementation\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTubeExc"
},
{
"path": "pyyoutube/resources/base_resource.py",
"chars": 441,
"preview": "\"\"\"\nBase resource class.\n\"\"\"\n\nfrom typing import Optional, TYPE_CHECKING\n\nif TYPE_CHECKING:\n from pyyoutube import Cl"
},
{
"path": "pyyoutube/resources/captions.py",
"chars": 9978,
"preview": "\"\"\"\nCaptions resource implementation\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom requests import Response\n\nfrom pyyout"
},
{
"path": "pyyoutube/resources/channel_banners.py",
"chars": 2031,
"preview": "\"\"\"\nChannel banners resource implementation.\n\"\"\"\n\nfrom typing import Optional\n\nfrom pyyoutube.resources.base_resource im"
},
{
"path": "pyyoutube/resources/channel_sections.py",
"chars": 11393,
"preview": "\"\"\"\nChannel Section resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouT"
},
{
"path": "pyyoutube/resources/channels.py",
"chars": 8256,
"preview": "\"\"\"\nChannel resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTubeExcep"
},
{
"path": "pyyoutube/resources/comment_threads.py",
"chars": 7015,
"preview": "\"\"\"\nComment threads resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouT"
},
{
"path": "pyyoutube/resources/comments.py",
"chars": 9251,
"preview": "\"\"\"\nComment resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTubeExcep"
},
{
"path": "pyyoutube/resources/i18n_languages.py",
"chars": 1885,
"preview": "\"\"\"\ni18n language resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.resources.base_resour"
},
{
"path": "pyyoutube/resources/i18n_regions.py",
"chars": 1817,
"preview": "\"\"\"\ni18n regions resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.resources.base_resourc"
},
{
"path": "pyyoutube/resources/members.py",
"chars": 3156,
"preview": "\"\"\"\nMembers resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.resources.base_resource imp"
},
{
"path": "pyyoutube/resources/membership_levels.py",
"chars": 1645,
"preview": "\"\"\"\nMembership levels resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.models import Mem"
},
{
"path": "pyyoutube/resources/playlist_items.py",
"chars": 10663,
"preview": "\"\"\"\nPlaylist items resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTu"
},
{
"path": "pyyoutube/resources/playlists.py",
"chars": 12564,
"preview": "\"\"\"\nPlaylist resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTubeExce"
},
{
"path": "pyyoutube/resources/search.py",
"chars": 14310,
"preview": "\"\"\"\nSearch resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.resources.base_resource impo"
},
{
"path": "pyyoutube/resources/subscriptions.py",
"chars": 9105,
"preview": "\"\"\"\nSubscription resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTube"
},
{
"path": "pyyoutube/resources/thumbnails.py",
"chars": 972,
"preview": "\"\"\"\nThumbnails resources implementation.\n\"\"\"\n\nfrom typing import Optional\n\nfrom pyyoutube.resources.base_resource import"
},
{
"path": "pyyoutube/resources/video_abuse_report_reasons.py",
"chars": 1948,
"preview": "\"\"\"\nVideo abuse report reasons resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.resource"
},
{
"path": "pyyoutube/resources/video_categories.py",
"chars": 2929,
"preview": "\"\"\"\nVideo categories resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYou"
},
{
"path": "pyyoutube/resources/videos.py",
"chars": 19011,
"preview": "\"\"\"\nVideos resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import PyYouTubeExcept"
},
{
"path": "pyyoutube/resources/watermarks.py",
"chars": 3934,
"preview": "\"\"\"\nWatermarks resource implementation.\n\"\"\"\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.resources.base_resource "
},
{
"path": "pyyoutube/utils/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "pyyoutube/utils/constants.py",
"chars": 4582,
"preview": "\"\"\"\nsome constants for YouTube\n\"\"\"\n\nACTIVITIES_RESOURCE_PROPERTIES = {\"id\", \"snippet\", \"contentDetails\"}\n\nCAPTIONS_RESOU"
},
{
"path": "pyyoutube/utils/params_checker.py",
"chars": 3237,
"preview": "\"\"\"\nfunction's params checker.\n\"\"\"\n\nimport logging\n\nfrom typing import Optional, Union\n\nfrom pyyoutube.error import Erro"
},
{
"path": "pyyoutube/youtube_utils.py",
"chars": 860,
"preview": "\"\"\"\nThis provide some common utils methods for YouTube resource.\n\"\"\"\n\nimport isodate\nfrom isodate.isoerror import ISO860"
},
{
"path": "testdata/apidata/abuse_reasons/abuse_reason.json",
"chars": 2314,
"preview": "{\n \"kind\": \"youtube#videoAbuseReportReasonListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/YH398HlGf_qbYlJQUZVMR"
},
{
"path": "testdata/apidata/access_token.json",
"chars": 238,
"preview": "{\"access_token\":\"access_token\",\"expires_in\":3599,\"refresh_token\":\"refresh_token\",\"scope\":[\"https://www.googleapis.com/au"
},
{
"path": "testdata/apidata/activities/activities_by_channel_p1.json",
"chars": 19479,
"preview": "{\n \"kind\": \"youtube#activityListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/wgXTq3L7ZY7nAhRFHl3U-cfbtIM\\\"\",\n \""
},
{
"path": "testdata/apidata/activities/activities_by_channel_p2.json",
"chars": 6065,
"preview": "{\n \"kind\": \"youtube#activityListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/ivly37DC9305MS_NVi_P4LZl_5I\\\"\",\n \""
},
{
"path": "testdata/apidata/activities/activities_by_mine_p1.json",
"chars": 1482,
"preview": "{\n \"kind\": \"youtube#activityListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/le7hKns0ey3G45tVxUz8WPZskcQ\\\"\",\n \""
},
{
"path": "testdata/apidata/activities/activities_by_mine_p2.json",
"chars": 1480,
"preview": "{\n \"kind\": \"youtube#activityListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/6yncT242auTddLxSe4dfDTC-4xE\\\"\",\n \""
},
{
"path": "testdata/apidata/captions/captions_by_video.json",
"chars": 1278,
"preview": "{\n \"kind\": \"youtube#captionListResponse\",\n \"etag\": \"\\\"OOFf3Zw2jDbxxHsjJ3l8u1U8dz4/bB4ewYNN7bQHonV-K7efrgBqh8M\\\"\",\n \"i"
},
{
"path": "testdata/apidata/captions/captions_filter_by_id.json",
"chars": 697,
"preview": "{\n \"kind\": \"youtube#captionListResponse\",\n \"etag\": \"\\\"OOFf3Zw2jDbxxHsjJ3l8u1U8dz4/4OU1z5mciyh4emins-W6FGneNdM\\\"\",\n \"i"
},
{
"path": "testdata/apidata/captions/insert_response.json",
"chars": 515,
"preview": "{\n \"kind\": \"youtube#caption\",\n \"etag\": \"R7KYT4aJbHp2wxlTmtFuKJ4pmF8\",\n \"id\": \"AUieDabWmL88_xoRtxyxjTMtmvdoF9dLTW3WxfJ"
},
{
"path": "testdata/apidata/captions/update_response.json",
"chars": 516,
"preview": "{\n \"kind\": \"youtube#caption\",\n \"etag\": \"R7KYT4aJbHp2wxlTmtFuKJ4pmF8\",\n \"id\": \"AUieDabWmL88_xoRtxyxjTMtmvdoF9dLTW3WxfJ"
},
{
"path": "testdata/apidata/categories/guide_categories_by_region.json",
"chars": 3115,
"preview": "{\n \"kind\": \"youtube#guideCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/cKe5cQFDAFacJgSsRH_6x7oGHZU\\\"\""
},
{
"path": "testdata/apidata/categories/guide_category_multi.json",
"chars": 706,
"preview": "{\n \"kind\": \"youtube#guideCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/JvKaPPmX316HuCUpJddmxaDPomo\\\"\""
},
{
"path": "testdata/apidata/categories/guide_category_single.json",
"chars": 418,
"preview": "{\n \"kind\": \"youtube#guideCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/KIJAFi2jsRHVBmAk3XYhyRKynjw\\\"\""
},
{
"path": "testdata/apidata/categories/video_category_by_region.json",
"chars": 9168,
"preview": "{\n \"kind\": \"youtube#videoCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/S730Ilt-Fi-emsQJvJAAShlR6hM\\\"\""
},
{
"path": "testdata/apidata/categories/video_category_multi.json",
"chars": 701,
"preview": "{\n \"kind\": \"youtube#videoCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/QhsRsql8vvkcmFdomppeHDbsV0Q\\\"\""
},
{
"path": "testdata/apidata/categories/video_category_single.json",
"chars": 417,
"preview": "{\n \"kind\": \"youtube#videoCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/0_wT9Ta0iZu7ETYC3E6Xi_B4mtA\\\"\""
},
{
"path": "testdata/apidata/channel_banners/insert_response.json",
"chars": 207,
"preview": "{\n \"kind\": \"youtube#channelBannerResource\",\n \"etag\": \"ezPZq6gkoCbM-5C4P-ved0Irol0\",\n \"url\": \"https://yt3.googleuserco"
},
{
"path": "testdata/apidata/channel_info_multi.json",
"chars": 3150,
"preview": "{\n \"kind\": \"youtube#channelListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/GaZ2FEuxLAqXsTHh13eEnkvWngM\\\"\",\n \"p"
},
{
"path": "testdata/apidata/channel_info_single.json",
"chars": 1838,
"preview": "{\n \"kind\": \"youtube#channelListResponse\",\n \"etag\": \"\\\"8jEFfXBrqiSrcF6Ee7MQuz8XuAM/0lqbdkIcLGXAPiLsJ3FTHo96TDg\\\"\",\n \"p"
},
{
"path": "testdata/apidata/channel_sections/channel_sections_by_channel.json",
"chars": 1272,
"preview": "{\n \"kind\": \"youtube#channelSectionListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/IG4AAhdP913_ibNr3xxa2XjZhAU\\\""
},
{
"path": "testdata/apidata/channel_sections/channel_sections_by_id.json",
"chars": 647,
"preview": "{\n \"kind\": \"youtube#channelSectionListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/Oysqp4SfBtVFI8-0LVzUEHn8LN4\\\""
},
{
"path": "testdata/apidata/channel_sections/channel_sections_by_ids.json",
"chars": 883,
"preview": "{\n \"kind\": \"youtube#channelSectionListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/Nvmls-WhS6tunMyp9v6ZIEFrgRI\\\""
},
{
"path": "testdata/apidata/channel_sections/insert_resp.json",
"chars": 338,
"preview": "{\n \"kind\": \"youtube#channelSection\",\n \"etag\": \"VNVb0NhdJ8VHoZaVCqGVqfaRrVU\",\n \"id\": \"UCa-vrCLQHviTOVnEKDOdetQ.Zx4DA4x"
},
{
"path": "testdata/apidata/channels/info.json",
"chars": 1320,
"preview": "{\"kind\":\"youtube#channelListResponse\",\"etag\":\"DovVRc4nTNzGShQkXoC7R2ab3JQ\",\"pageInfo\":{\"totalResults\":1,\"resultsPerPage\""
},
{
"path": "testdata/apidata/channels/info_multiple.json",
"chars": 4304,
"preview": "{\"kind\":\"youtube#channelListResponse\",\"etag\":\"doLptdWt69-xv1D0XqhnNqKHg9o\",\"pageInfo\":{\"totalResults\":2,\"resultsPerPage\""
},
{
"path": "testdata/apidata/channels/update_resp.json",
"chars": 381,
"preview": "{\"kind\":\"youtube#channel\",\"etag\":\"qlk0Tup07Hsl_Dz8nMefxFRUiEU\",\"id\":\"UCa-vrCLQHviTOVnEKDOdetQ\",\"brandingSettings\":{\"chan"
},
{
"path": "testdata/apidata/client_secrets/client_secret_installed_bad.json",
"chars": 307,
"preview": "{\n \"installed\": {\n \"client_id\": \"client_id\",\n \"project_id\": \"project_id\",\n \"auth_uri\": \"https://"
},
{
"path": "testdata/apidata/client_secrets/client_secret_installed_good.json",
"chars": 349,
"preview": "{\n \"installed\": {\n \"client_id\": \"client_id\",\n \"project_id\": \"project_id\",\n \"auth_uri\": \"https://"
},
{
"path": "testdata/apidata/client_secrets/client_secret_unsupported.json",
"chars": 309,
"preview": "{\n \"unsupported\": {\n \"client_id\": \"client_id\",\n \"project_id\": \"project_id\",\n \"auth_uri\": \"https:"
},
{
"path": "testdata/apidata/client_secrets/client_secret_web.json",
"chars": 432,
"preview": "{\n \"web\": {\n \"client_id\": \"client_id\",\n \"project_id\": \"project_id\",\n \"auth_uri\": \"https://accoun"
},
{
"path": "testdata/apidata/comment_threads/comment_thread_single.json",
"chars": 1775,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/VQfUfBFenzO3S8AzxaX0A2cOK_w\\\"\""
},
{
"path": "testdata/apidata/comment_threads/comment_threads_all_to_me.json",
"chars": 5463,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/9f4Mz6CLxsfPgItKiTYYywpU5pY\\\"\""
},
{
"path": "testdata/apidata/comment_threads/comment_threads_by_channel.json",
"chars": 2769,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/Wm2vP6ZY_91XACR5c9Vk8QFfpWY\\\"\""
},
{
"path": "testdata/apidata/comment_threads/comment_threads_by_video_paged_1.json",
"chars": 7936,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/mj64hlcyhnGGcJWAccdWisvf8MY\\\"\""
},
{
"path": "testdata/apidata/comment_threads/comment_threads_by_video_paged_2.json",
"chars": 7741,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/mj64hlcyhnGGcJWAccdWisvf8MY\\\"\""
},
{
"path": "testdata/apidata/comment_threads/comment_threads_multi.json",
"chars": 2968,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/LKHkXHQZoSC-BXdpnHc829mN3QM\\\"\""
},
{
"path": "testdata/apidata/comment_threads/comment_threads_with_search.json",
"chars": 1471,
"preview": "{\n \"kind\": \"youtube#commentThreadListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/Sb1s9ORYlw5LpXhXM1osg1jeYgM\\\"\""
},
{
"path": "testdata/apidata/comment_threads/insert_response.json",
"chars": 1152,
"preview": "{\n \"kind\": \"youtube#commentThread\",\n \"etag\": \"AMgl2io48I4z6Ulu9kv4C43sVvk\",\n \"id\": \"Ugx_5P8rmn4vKbN6wwt4AaABAg\",\n \"s"
},
{
"path": "testdata/apidata/comments/comments_by_parent_paged_1.json",
"chars": 2214,
"preview": "{\n \"kind\": \"youtube#commentListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/WG_t4MaQHNzJKOhVkfr0prByYcE\\\"\",\n \"n"
},
{
"path": "testdata/apidata/comments/comments_by_parent_paged_2.json",
"chars": 1070,
"preview": "{\n \"kind\": \"youtube#commentListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/pCAXgQBtRsyN8PLsTBsg6O6diuY\\\"\",\n \"p"
},
{
"path": "testdata/apidata/comments/comments_multi.json",
"chars": 1734,
"preview": "{\n \"kind\": \"youtube#commentListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/KGqYDVMiabGgL8yNEbUJOwtrsqY\\\"\",\n \"i"
},
{
"path": "testdata/apidata/comments/comments_single.json",
"chars": 916,
"preview": "{\n \"kind\": \"youtube#commentListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/A2nkVdtP_3bQZi4INK9lZ7XTYXs\\\"\",\n \"i"
},
{
"path": "testdata/apidata/comments/insert_response.json",
"chars": 731,
"preview": "{\n \"kind\": \"youtube#comment\",\n \"etag\": \"lTl2Wjqipb6KqrmPU04DLigrzrg\",\n \"id\": \"Ugy_CAftKrIUCyPr9GR4AaABAg.9iPXEClD9lW9"
},
{
"path": "testdata/apidata/error_permission_resp.json",
"chars": 290,
"preview": "{\n \"error\": {\n \"code\": 403,\n \"message\": \"The caller does not have permission\",\n \"errors\": [\n {\n \"m"
},
{
"path": "testdata/apidata/i18ns/language_res.json",
"chars": 1273,
"preview": "{\n \"kind\": \"youtube#i18nLanguageListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/qgFy24yvs-L_dNjr2d-Rd_Xcfw4\\\"\","
},
{
"path": "testdata/apidata/i18ns/regions_res.json",
"chars": 1006,
"preview": "{\n \"kind\": \"youtube#i18nRegionListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/q85_wZeDyKDzYtt-LhNaozyi_sk\\\"\",\n "
},
{
"path": "testdata/apidata/members/members_data.json",
"chars": 2268,
"preview": "{\n \"kind\": \"youtube#memberListResponse\",\n \"etag\": \"etag\",\n \"pageInfo\": {\n \"totalResults\": 2,\n \"resultsPerPage\":"
},
{
"path": "testdata/apidata/members/membership_levels.json",
"chars": 578,
"preview": "{\n \"kind\": \"youtube#membershipsLevelListResponse\",\n \"etag\": \"etag\",\n \"items\": [\n {\n \"kind\": \"youtube#membersh"
},
{
"path": "testdata/apidata/playlist_items/insert_response.json",
"chars": 1556,
"preview": "{\n \"kind\": \"youtube#playlistItem\",\n \"etag\": \"4Bl2u6s8N1Jkkz1AHN4E-tw4OQQ\",\n \"id\": \"UExCYWlkdDBpbENNYW5HRElLcjhVVkJGWn"
},
{
"path": "testdata/apidata/playlist_items/playlist_items_filter_video.json",
"chars": 1954,
"preview": "{\n \"kind\": \"youtube#playlistItemListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/L2uYevt91mZBw1hKeHio9_Aamz8\\\"\","
},
{
"path": "testdata/apidata/playlist_items/playlist_items_multi.json",
"chars": 3702,
"preview": "{\n \"kind\": \"youtube#playlistItemListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/Rwbdfm4jH2tQT1_c-Jxyy8qh2Xs\\\"\","
},
{
"path": "testdata/apidata/playlist_items/playlist_items_paged_1.json",
"chars": 23243,
"preview": "{\n \"kind\": \"youtube#playlistItemListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/P4kQiZH0w8ID3LMajA0LjTQAypg\\\"\","
},
{
"path": "testdata/apidata/playlist_items/playlist_items_paged_2.json",
"chars": 6931,
"preview": "{\n \"kind\": \"youtube#playlistItemListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/Mdch507Btu8hjUyr5r5qJbQgL1Y\\\"\","
},
{
"path": "testdata/apidata/playlist_items/playlist_items_single.json",
"chars": 1954,
"preview": "{\n \"kind\": \"youtube#playlistItemListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/YVsjtCTnqysTcNJy7jZglowaNYM\\\"\","
},
{
"path": "testdata/apidata/playlists/insert_response.json",
"chars": 811,
"preview": "{\n \"kind\": \"youtube#playlist\",\n \"etag\": \"Gw0SW_V3Hy1XNqjAJB1v1Q0ZmB4\",\n \"id\": \"PLBaidt0ilCMZN8XPVB5iXY6FlSYGeyn2n\",\n "
},
{
"path": "testdata/apidata/playlists/playlists_mine.json",
"chars": 3911,
"preview": "{\n \"kind\": \"youtube#playlistListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/LjFPI2nwRBxtabydhze2pwRj5LI\\\"\",\n \""
},
{
"path": "testdata/apidata/playlists/playlists_multi.json",
"chars": 2949,
"preview": "{\n \"kind\": \"youtube#playlistListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/tANZNtv_p2OkP0sqKDLjKwJQ4Cs\\\"\",\n \""
},
{
"path": "testdata/apidata/playlists/playlists_paged_1.json",
"chars": 17440,
"preview": "{\n \"kind\": \"youtube#playlistListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/jGXEiDmTtAs7vTBHF90lfHs9yOA\\\"\",\n \""
},
{
"path": "testdata/apidata/playlists/playlists_paged_2.json",
"chars": 17594,
"preview": "{\n \"kind\": \"youtube#playlistListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/LjFPI2nwRBxtabydhze2pwRj5LI\\\"\",\n \""
},
{
"path": "testdata/apidata/playlists/playlists_single.json",
"chars": 1558,
"preview": "{\n \"kind\": \"youtube#playlistListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/fk7i9mk8dlQehbz-BVBy0SzccWY\\\"\",\n \""
},
{
"path": "testdata/apidata/search/search_by_developer.json",
"chars": 6271,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/-_uuZ-sNfL41nlDe7y-H3-hTVR8\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_by_event.json",
"chars": 30004,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/SE083xj7h4uR9Tn6xMg-LwbsFw8\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_by_keywords_p1.json",
"chars": 29391,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/HlaDCPqWxO8HJ_tV51UiHmzJdAs\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_by_keywords_p2.json",
"chars": 29321,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/ALJWUURzLUpxp1uFSnJBAmUIj8g\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_by_location.json",
"chars": 6015,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/KRABNKy-TvvaFRB9poJwJHu7_CA\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_by_mine.json",
"chars": 2300,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/2d1a6UDYt8tuTka8VkEd_2F0uNM\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_by_related_video.json",
"chars": 9392,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/Jt-dx_Bx1OzzEBghaPZjhe1AeIc\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_channels.json",
"chars": 6164,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/CvjFcA434WC9odlJ9qApNTqQE3Y\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/search/search_videos_by_channel.json",
"chars": 6281,
"preview": "{\n \"kind\": \"youtube#searchListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/qDAZEJAlbMKs4-6fpruc8u6toc4\\\"\",\n \"ne"
},
{
"path": "testdata/apidata/subscriptions/insert_response.json",
"chars": 2330,
"preview": "{\n \"kind\": \"youtube#subscription\",\n \"etag\": \"BBbHqFIch0N1EhR1bwn0s3MofFg\",\n \"id\": \"POsnRIYsMcp1Cghr_Fsh-6uFZRcIHmTKzz"
},
{
"path": "testdata/apidata/subscriptions/subscription_zero.json",
"chars": 204,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/ewwRz0VbTYpp2EGbOkvZ5M_1mbo\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_channel_p1.json",
"chars": 8057,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/NtDoQfEOjOm9UE8PH2xuzaVGuko\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_channel_p2.json",
"chars": 2717,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/XhiVa6ArhD2_-Lydooagc6Tq7oc\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_channel_with_filter.json",
"chars": 3144,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/Bpz_bu12-YiX5_pAavOTWX7GtDQ\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_id.json",
"chars": 2270,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/USyhytrL1qAH8AxBqW22EUor8kw\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_mine_filter.json",
"chars": 2435,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/VWi2sv7EuSXCkMveaGb_XUpimn4\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_mine_p1.json",
"chars": 9999,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/U5b9PTEHoZSQJmnmfpQ8uv2uVUY\\\"\","
},
{
"path": "testdata/apidata/subscriptions/subscriptions_by_mine_p2.json",
"chars": 6615,
"preview": "{\n \"kind\": \"youtube#subscriptionListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/VTrpIDMd19kB85U38VXsGy5SZ4Q\\\"\","
},
{
"path": "testdata/apidata/user_profile.json",
"chars": 223,
"preview": "{\n \"family_name\": \"liu\",\n \"name\": \"kun liu\",\n \"picture\": \"https://lh3.googleusercontent.com/-uDfN5WzhqmE/AAAAAAAAAAI/"
},
{
"path": "testdata/apidata/videos/get_rating_response.json",
"chars": 173,
"preview": "{\n \"kind\": \"youtube#videoGetRatingResponse\",\n \"etag\": \"jHmA6WPghQxwUKfIGg5LVYotT3Y\",\n \"items\": [\n {\n \"videoId"
},
{
"path": "testdata/apidata/videos/insert_response.json",
"chars": 2903,
"preview": "{\n \"kind\": \"youtube#video\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/dbCtFPFQrd6OMTnWAYrcpZDPai0\\\"\",\n \"id\": \"D-lhorsDl"
},
{
"path": "testdata/apidata/videos/videos_chart_paged_1.json",
"chars": 19986,
"preview": "{\n \"kind\": \"youtube#videoListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/rzuXDNleIBUJYgD8CQtVBDeS0mU\\\"\",\n \"nex"
},
{
"path": "testdata/apidata/videos/videos_chart_paged_2.json",
"chars": 11954,
"preview": "{\n \"kind\": \"youtube#videoListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/Ef75aANPGrASduOASo4wDAnehrs\\\"\",\n \"pre"
},
{
"path": "testdata/apidata/videos/videos_info_multi.json",
"chars": 6576,
"preview": "{\n \"kind\": \"youtube#videoListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/gdL-V0hs_jHqrqpeSTPV4WGOc9Y\\\"\",\n \"pag"
},
{
"path": "testdata/apidata/videos/videos_info_single.json",
"chars": 3393,
"preview": "{\n \"kind\": \"youtube#videoListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/tCG7DWpALbkZUGKS9l2aPYQwYRo\\\"\",\n \"pag"
},
{
"path": "testdata/apidata/videos/videos_myrating_paged_1.json",
"chars": 4031,
"preview": "{\n \"kind\": \"youtube#videoListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/b3UF68xF07lN4fYS18DcwcBg3mE\\\"\",\n \"nex"
},
{
"path": "testdata/apidata/videos/videos_myrating_paged_2.json",
"chars": 2129,
"preview": "{\n \"kind\": \"youtube#videoListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/Uj_O0S-h8FpI0EG1DLVrKzjMiqM\\\"\",\n \"pre"
},
{
"path": "testdata/error_response.json",
"chars": 203,
"preview": "{\n \"error\": {\n \"errors\": [\n {\n \"domain\": \"usageLimits\",\n \"reason\": \"keyInvalid\",\n \"message"
},
{
"path": "testdata/error_response_simple.json",
"chars": 30,
"preview": "{\n \"error\": \"error message\"\n}"
},
{
"path": "testdata/modeldata/abuse_report_reason/abuse_reason.json",
"chars": 449,
"preview": "{\n \"kind\": \"youtube#videoAbuseReportReason\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/_WIvuNJwlISvQNQt_ukh2m0kt2Y\\\"\",\n "
},
{
"path": "testdata/modeldata/abuse_report_reason/abuse_reason_res.json",
"chars": 2314,
"preview": "{\n \"kind\": \"youtube#videoAbuseReportReasonListResponse\",\n \"etag\": \"\\\"SJZWTG6xR0eGuCOh2bX6w3s4F94/YH398HlGf_qbYlJQUZVMR"
},
{
"path": "testdata/modeldata/activities/activity.json",
"chars": 1169,
"preview": "{\n \"kind\": \"youtube#activity\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/Jy79IfTqdSUQSMOkAA9ynak3zOI\\\"\",\n \"id\": \"MTUxNT"
},
{
"path": "testdata/modeldata/activities/activity_contentDetails.json",
"chars": 50,
"preview": "{\n \"upload\": {\n \"videoId\": \"LDXYRzerjzU\"\n }\n}"
},
{
"path": "testdata/modeldata/activities/activity_response.json",
"chars": 2903,
"preview": "{\n \"kind\": \"youtube#activityListResponse\",\n \"etag\": \"\\\"p4VTdlkQv3HQeTEaXgvLePAydmU/JK3LLzdEV9zduJqxK4pgzb55QfI\\\"\",\n \""
},
{
"path": "testdata/modeldata/activities/activity_snippet.json",
"chars": 1308,
"preview": "{\n \"publishedAt\": \"2019-12-30T20:00:02.000Z\",\n \"channelId\": \"UC_x5XG1OV2P6uZZ5FSM9Ttw\",\n \"title\": \"2019 Year in Revie"
},
{
"path": "testdata/modeldata/captions/caption.json",
"chars": 486,
"preview": "{\n \"kind\": \"youtube#caption\",\n \"etag\": \"\\\"OOFf3Zw2jDbxxHsjJ3l8u1U8dz4/X6ucQ8rZVjhog8RtdYb8rQYLErE\\\"\",\n \"id\": \"SwPOvp0"
},
{
"path": "testdata/modeldata/captions/caption_response.json",
"chars": 1278,
"preview": "{\n \"kind\": \"youtube#captionListResponse\",\n \"etag\": \"\\\"OOFf3Zw2jDbxxHsjJ3l8u1U8dz4/bB4ewYNN7bQHonV-K7efrgBqh8M\\\"\",\n \"i"
},
{
"path": "testdata/modeldata/captions/caption_snippet.json",
"chars": 298,
"preview": "{\n \"videoId\": \"oHR3wURdJ94\",\n \"lastUpdated\": \"2020-01-14T09:40:49.981Z\",\n \"trackKind\": \"standard\",\n \"language\": \"en\""
},
{
"path": "testdata/modeldata/categories/guide_category_info.json",
"chars": 240,
"preview": "{\n \"kind\": \"youtube#guideCategory\",\n \"etag\": \"\\\"8jEFfXBrqiSrcF6Ee7MQuz8XuAM/fnL4T7wf3HKS8VCeb2Mui5q9zeM\\\"\",\n \"id\": \"G"
},
{
"path": "testdata/modeldata/categories/guide_category_response.json",
"chars": 418,
"preview": "{\n \"kind\": \"youtube#guideCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/KIJAFi2jsRHVBmAk3XYhyRKynjw\\\"\""
},
{
"path": "testdata/modeldata/categories/video_category_info.json",
"chars": 235,
"preview": "{\n \"kind\": \"youtube#videoCategory\",\n \"etag\": \"\\\"8jEFfXBrqiSrcF6Ee7MQuz8XuAM/9GQMSRjrZdHeb1OEM1XVQ9zbGec\\\"\",\n \"id\": \"1"
},
{
"path": "testdata/modeldata/categories/video_category_response.json",
"chars": 416,
"preview": "{\n \"kind\": \"youtube#videoCategoryListResponse\",\n \"etag\": \"\\\"j6xRRd8dTPVVptg711_CSPADRfg/0_wT9Ta0iZu7ETYC3E6Xi_B4mtA\\\"\""
},
{
"path": "testdata/modeldata/channel_sections/channel_section_info.json",
"chars": 1156,
"preview": "{\n \"kind\": \"youtube#channelSection\",\n \"etag\": \"\\\"Fznwjl6JEQdo1MGvHOGaz_YanRU/5bNXeieMoiNVa4NokOortBf50ZA\\\"\",\n \"id\": \""
},
{
"path": "testdata/modeldata/channel_sections/channel_section_response.json",
"chars": 5372,
"preview": "{\n \"kind\": \"youtube#channelSectionListResponse\",\n \"etag\": \"\\\"Fznwjl6JEQdo1MGvHOGaz_YanRU/PSyTmUO7BRU2cPplSImsWGWgOz8\\\""
},
{
"path": "testdata/modeldata/channels/channel_api_response.json",
"chars": 1837,
"preview": "{\n \"kind\": \"youtube#channelListResponse\",\n \"etag\": \"\\\"8jEFfXBrqiSrcF6Ee7MQuz8XuAM/0lqbdkIcLGXAPiLsJ3FTHo96TDg\\\"\",\n \"p"
},
{
"path": "testdata/modeldata/channels/channel_branding_settings.json",
"chars": 4278,
"preview": "{\n \"channel\": {\n \"title\": \"Google Developers\",\n \"description\": \"The Google Developers channel features talks from"
},
{
"path": "testdata/modeldata/channels/channel_content_details.json",
"chars": 123,
"preview": "{\n \"relatedPlaylists\": {\n \"uploads\": \"UU_x5XG1OV2P6uZZ5FSM9Ttw\",\n \"watchHistory\": \"HL\",\n \"watchLater\": \"WL\"\n "
},
{
"path": "testdata/modeldata/channels/channel_info.json",
"chars": 6518,
"preview": "{\n \"kind\": \"youtube#channel\",\n \"etag\": \"\\\"8jEFfXBrqiSrcF6Ee7MQuz8XuAM/HUbWoTqNN1LPZKmbyCzPgvjVuR4\\\"\",\n \"id\": \"UC_x5XG"
},
{
"path": "testdata/modeldata/channels/channel_snippet.json",
"chars": 1092,
"preview": "{\n \"title\": \"Google Developers\",\n \"description\": \"The Google Developers channel features talks from events, educationa"
},
{
"path": "testdata/modeldata/channels/channel_statistics.json",
"chars": 141,
"preview": "{\n \"viewCount\": 160361638,\n \"commentCount\": \"0\",\n \"subscriberCount\": \"1927873\",\n \"hiddenSubscriberCount\": false,\n \""
},
{
"path": "testdata/modeldata/channels/channel_status.json",
"chars": 100,
"preview": "{\n \"privacyStatus\": \"public\",\n \"isLinked\": true,\n \"longUploadsStatus\": \"longUploadsUnspecified\"\n}"
}
]
// ... and 109 more files (download for full content)
About this extraction
This page contains the full source code of the sns-sdks/python-youtube GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 309 files (1.0 MB), approximately 299.7k tokens, and a symbol index with 541 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.