youtube_up

Installation

pip install youtube-up

Installing certificates

On your first run you may get an error which says Was not able to load https://youtube.com. Have you installed the certificate at {cert_path} ?. If this happens you should follow the instructions at https://docs.mitmproxy.org/stable/concepts-certificates/#installing-the-mitmproxy-ca-certificate-manually to install the certificate at the given path.

Examples

Upload a video

from youtube_up import AllowCommentsEnum, Metadata, PrivacyEnum, YTUploaderSession

uploader = YTUploaderSession.from_cookies_txt("cookies/cookies.txt")
metadata = Metadata(
    title="Video title",
    description="Video description",
    privacy=PrivacyEnum.PUBLIC,
    made_for_kids=False,
    allow_comments_mode=AllowCommentsEnum.HOLD_ALL,
)
uploader.upload("video.webm", metadata)

Note that for Enum-type parameters we can either pass the Enum itself (as shown above), or the Enum value, or the Enum key, as a string. For example, instead of writing

allow_comments_mode=AllowCommentsEnum.HOLD_ALL

we could instead write allow_comments_mode="HOLD_ALL" or allow_comments_mode="APPROVED_COMMENTS"

Upload multiple videos

from youtube_up import Metadata, YTUploaderSession

uploader = YTUploaderSession.from_cookies_txt("cookies/cookies.txt")
metadata_1 = Metadata(
    title="Video 1",
)
metadata_2 = Metadata(
    title="Video 2",
)
uploader.upload("video1.webm", metadata_1)
uploader.upload("video2.webm", metadata_2)

Upload to a new or existing playlist

from youtube_up import Metadata, YTUploaderSession, Playlist

uploader = YTUploaderSession.from_cookies_txt("cookies/cookies.txt")
metadata = Metadata(
    title="Video 1",
    playlists=[
        Playlist(
            "Songs by me",
            description="Playlist that will only be created if "
            "no playlist exists with the title 'Songs by me'",
            create_if_title_doesnt_exist=True,
            create_if_title_exists=False,
        ),
        Playlist(
            "test playlist",
            description="Playlist that video will be added to "
            "only if it exists already. This description does "
            "nothing.",
            create_if_title_doesnt_exist=False,
            create_if_title_exists=False,
        ),
        Playlist(
            "Album",
            description="Playlist will be created even if there"
            " is already a playlist with the name 'Album'"
            create_if_title_doesnt_exist=True,
            create_if_title_exists=True,
        ),
    ],
)
uploader.upload("video.webm", metadata)

CLI

youtube-up comes with a CLI app for uploading videos. For example, if we wanted to create a public video with the title "Video title", we would execute the following command: youtube-up video video.webm --title="Video title" --cookies_file="cookies/cookies.txt" --privacy="PUBLIC"

The app can also take a JSON file as input. For example, the following JSON file would upload one video to a new or existing playlist called "Music" and one video which is set to premiere on December 25th, 2023 at 5 PM (local time).

[
    {
        "file": "song.webm",
        "metadata": {
            "title": "New song",
            "privacy": "PUBLIC",
            "playlists": [
                {
                    "title": "Music"
                }
            ]
        }
    },
    {
        "file": "premiere.webm",
        "metadata": {
            "title": "Special Announcement",
            "scheduled_upload": "2023-12-25T17:00:00",
            "premiere_countdown_duration": "ONE_MIN",
            "premiere_theme": "BRIGHT"
        }
    }
]

If we wanted the video to premiere at 5 PM GMT, would could have written "2023-12-25T17:00:00+00:00" instead. We then run

youtube-up json metadata.json --cookies_file="cookies/cookies.txt"

to upload these videos.

  1# ruff: noqa
  2"""
  3# Installation
  4
  5`pip install youtube-up`
  6
  7## Installing certificates
  8
  9On your first run you may get an error which says `Was not able to load https://youtube.com. Have you installed the certificate at {cert_path} ?`.
 10If this happens you should follow the instructions at https://docs.mitmproxy.org/stable/concepts-certificates/#installing-the-mitmproxy-ca-certificate-manually
 11to install the certificate at the given path.
 12
 13# Examples
 14
 15## Upload a video
 16```python
 17from youtube_up import AllowCommentsEnum, Metadata, PrivacyEnum, YTUploaderSession
 18
 19uploader = YTUploaderSession.from_cookies_txt("cookies/cookies.txt")
 20metadata = Metadata(
 21    title="Video title",
 22    description="Video description",
 23    privacy=PrivacyEnum.PUBLIC,
 24    made_for_kids=False,
 25    allow_comments_mode=AllowCommentsEnum.HOLD_ALL,
 26)
 27uploader.upload("video.webm", metadata)
 28```
 29Note that for Enum-type parameters we can either pass the Enum itself (as shown above),
 30or the Enum value, or the Enum key, as a string. For example, instead of writing
 31
 32`allow_comments_mode=AllowCommentsEnum.HOLD_ALL`
 33
 34we could instead write `allow_comments_mode="HOLD_ALL"`
 35or `allow_comments_mode="APPROVED_COMMENTS"`
 36
 37## Upload multiple videos
 38```python
 39from youtube_up import Metadata, YTUploaderSession
 40
 41uploader = YTUploaderSession.from_cookies_txt("cookies/cookies.txt")
 42metadata_1 = Metadata(
 43    title="Video 1",
 44)
 45metadata_2 = Metadata(
 46    title="Video 2",
 47)
 48uploader.upload("video1.webm", metadata_1)
 49uploader.upload("video2.webm", metadata_2)
 50```
 51
 52## Upload to a new or existing playlist
 53```python
 54from youtube_up import Metadata, YTUploaderSession, Playlist
 55
 56uploader = YTUploaderSession.from_cookies_txt("cookies/cookies.txt")
 57metadata = Metadata(
 58    title="Video 1",
 59    playlists=[
 60        Playlist(
 61            "Songs by me",
 62            description="Playlist that will only be created if "
 63            "no playlist exists with the title 'Songs by me'",
 64            create_if_title_doesnt_exist=True,
 65            create_if_title_exists=False,
 66        ),
 67        Playlist(
 68            "test playlist",
 69            description="Playlist that video will be added to "
 70            "only if it exists already. This description does "
 71            "nothing.",
 72            create_if_title_doesnt_exist=False,
 73            create_if_title_exists=False,
 74        ),
 75        Playlist(
 76            "Album",
 77            description="Playlist will be created even if there"
 78            " is already a playlist with the name 'Album'"
 79            create_if_title_doesnt_exist=True,
 80            create_if_title_exists=True,
 81        ),
 82    ],
 83)
 84uploader.upload("video.webm", metadata)
 85```
 86
 87## CLI
 88youtube-up comes with a CLI app for uploading videos. For example, if we wanted to
 89create a public video with the title "Video title", we would execute the following command:
 90`youtube-up video video.webm --title="Video title" --cookies_file="cookies/cookies.txt" --privacy="PUBLIC"`
 91
 92The app can also take a JSON file as input. For example, the following JSON file would upload
 93one video to a new or existing playlist called "Music" and one video which is set to premiere
 94on December 25th, 2023 at 5 PM (local time).
 95
 96```json
 97[
 98    {
 99        "file": "song.webm",
100        "metadata": {
101            "title": "New song",
102            "privacy": "PUBLIC",
103            "playlists": [
104                {
105                    "title": "Music"
106                }
107            ]
108        }
109    },
110    {
111        "file": "premiere.webm",
112        "metadata": {
113            "title": "Special Announcement",
114            "scheduled_upload": "2023-12-25T17:00:00",
115            "premiere_countdown_duration": "ONE_MIN",
116            "premiere_theme": "BRIGHT"
117        }
118    }
119]
120```
121
122If we wanted the video to premiere at 5 PM GMT, would could have written "2023-12-25T17:00:00+00:00"
123instead. We then run
124
125`youtube-up json metadata.json --cookies_file="cookies/cookies.txt"`
126
127to upload these videos.
128"""
129
130from .metadata import *
131from .metadata import __all__ as m_all
132from .uploader import *
133from .uploader import __all__ as u_all
134
135__all__ = u_all + m_all
class YTUploaderSession:
 53class YTUploaderSession:
 54    """
 55    Class for uploading YouTube videos to a single channel
 56    """
 57
 58    _delegated_session_id_regex = re.compile(r'"DELEGATED_SESSION_ID":"([^"]*)"')
 59    _innertube_api_key_regex = re.compile(r'"INNERTUBE_API_KEY":"([^"]*)"')
 60    _session_index_regex = re.compile(r'"SESSION_INDEX":"([^"]*)"')
 61    _channel_id_regex = re.compile(r"https://studio.youtube.com/channel/([^/]*)/*")
 62    _progress_steps = {
 63        "start": 0,
 64        "get_session_data": 10,
 65        "get_upload_url": 20,
 66        "upload_video": 70,
 67        "get_session_token": 80,
 68        "create_video": 90,
 69        "upload_thumbnail": 95,
 70        "finish": 100,
 71    }
 72    _cookie_whitelist = {
 73        "LOGIN_INFO",
 74        "__Secure-1PSID",
 75        "__Secure-3PSID",
 76        "__Secure-1PAPISID",
 77        "__Secure-3PAPISID",
 78        "__Secure-1PSIDTS",
 79        "__Secure-3PSIDTS",
 80        "SAPISID",
 81    }
 82
 83    _session_token: str
 84    _cookies: FileCookieJar
 85    _session: requests.Session
 86
 87    def __init__(
 88        self,
 89        cookie_jar: FileCookieJar,
 90        webdriver_path: Optional[str] = None,
 91        selenium_timeout: float = 60,
 92    ):
 93        """Create YTUploaderSession from generic FileCookieJar
 94
 95        Args:
 96            cookie_jar (FileCookieJar): FileCookieJar. Must have save(), load(),
 97                and set_cookie(http.cookiejar.Cookie) methods
 98            webdriver_path (str, optional): Optional path to geckodriver or chromedriver
 99                executable
100            selenium_timeout (float, optional): Timeout to wait for grst request.
101                Defaults to 60 seconds
102        """
103        self._session_token = ""
104        self._webdriver_path = webdriver_path
105        self._selenium_timeout = selenium_timeout
106
107        # load cookies and init session
108        self._cookies = cookie_jar
109        self._session = requests.Session()
110        self._reload_cookies()
111        hash = self._generateSAPISIDHASH(self._session.cookies["SAPISID"])
112        self._session.headers = {
113            "Authorization": f"SAPISIDHASH {hash}",
114            "x-origin": "https://studio.youtube.com",
115            "user-agent": (
116                "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) "
117                "Gecko/20100101 Firefox/119.0"
118            ),
119        }
120
121    def _reload_cookies(self):
122        self._cookies.load(ignore_discard=True, ignore_expires=True)
123        self._session.cookies.clear()
124        for cookie in self._cookies:
125            if cookie.name == "SESSION_TOKEN":
126                self._session_token = cookie.value
127            elif cookie.name in self._cookie_whitelist:
128                self._session.cookies.set_cookie(copy.copy(cookie))
129
130    @classmethod
131    def from_cookies_txt(
132        cls,
133        cookies_txt_path: str,
134        webdriver_path: Optional[str] = None,
135        selenium_timeout: float = 60,
136    ):
137        """Create YTUploaderSession from cookies.txt file
138
139        Args:
140            cookies_txt_path (str): Path to Netscape cookies format file
141            webdriver_path (str, optional): Optional path to geckodriver or chromedriver
142                executable
143            selenium_timeout (float, optional): Timeout to wait for grst request.
144                Defaults to 60 seconds
145        """
146        cj = MozillaCookieJar(cookies_txt_path)
147        return cls(cj, webdriver_path, selenium_timeout)
148
149    def upload(
150        self,
151        file_path: str,
152        metadata: Metadata,
153        progress_callback: Callable[[str, float], None] = lambda step, percent: None,
154    ) -> str:
155        """Upload a video
156
157        Args:
158            file_path (str): Path to video file
159            metadata (Metadata): Metadata of video to set when uploaded
160            progress_callback (Callable[[str, float], None], optional): Optional
161                progress callback. Callback receives what step uploader is on and what
162                the total percentage of the upload progress is (defined by
163                YTUploaderSession._progress_steps).
164
165        Returns:
166            str: ID of video uploaded
167        """
168        try:
169            metadata.validate()
170        except ValueError as ex:
171            raise YTUploaderException(f"Validation error: {ex}") from ex
172        progress_callback("start", self._progress_steps["start"])
173        data = self._get_session_data()
174        progress_callback("get_session_data", self._progress_steps["get_session_data"])
175        url = self._get_video_upload_url(data)
176        progress_callback("get_upload_url", self._progress_steps["get_upload_url"])
177        scotty_resource_id = self._upload_file(
178            url, file_path, progress_callback, "get_upload_url", "upload_video"
179        )
180        progress_callback("upload_video", self._progress_steps["upload_video"])
181        encrypted_video_id = self._create_video(scotty_resource_id, metadata, data)
182        if encrypted_video_id is None:
183            # could be bad session token, try to get new one
184            self._get_session_token()
185            progress_callback(
186                "get_session_token", self._progress_steps["get_session_token"]
187            )
188            encrypted_video_id = self._create_video(scotty_resource_id, metadata, data)
189            if encrypted_video_id is None:
190                raise YTUploaderException("Could not create video")
191        data.encrypted_video_id = encrypted_video_id
192        progress_callback("create_video", self._progress_steps["create_video"])
193
194        # set thumbnail
195        if metadata.thumbnail is not None:
196            url = self._get_upload_url_thumbnail(data)
197            data.thumbnail_scotty_id = self._upload_file(
198                url,
199                metadata.thumbnail,
200                progress_callback,
201                "create_video",
202                "upload_thumbnail",
203            )
204            data.thumbnail_format = self._get_thumbnail_format(metadata.thumbnail)
205
206        # playlists
207        if metadata.playlists:
208            playlists = self._get_creator_playlists(data)
209            if metadata.playlist_ids is None:
210                metadata.playlist_ids = []
211            for playlist in metadata.playlists:
212                exists = playlist.title in playlists
213                if (playlist.create_if_title_exists and exists) or (
214                    playlist.create_if_title_doesnt_exist and not exists
215                ):
216                    playlist_id = self._create_playlist(playlist, data)
217                    metadata.playlist_ids.append(playlist_id)
218                elif exists:
219                    metadata.playlist_ids.append(playlists[playlist.title])
220        # captions
221        if metadata.captions_files:
222            for caption_file in metadata.captions_files:
223                if caption_file.language is None:
224                    caption_file.language = metadata.audio_language
225                self._update_captions(caption_file, data)
226
227        self._update_metadata(metadata, data)
228        # save cookies
229        for cookie in self._session.cookies:
230            self._cookies.set_cookie(cookie)
231        self._cookies.save()
232        progress_callback("finish", self._progress_steps["finish"])
233        return data.encrypted_video_id
234
235    def has_valid_cookies(self) -> bool:
236        """Check if cookies are valid
237
238        Returns:
239            bool: True if we are able to log in to YouTube with the given cookies
240        """
241        r = self._session.get("https://youtube.com/upload")
242
243        return "studio.youtube.com/channel" in r.url
244
245    def _get_thumbnail_format(self, filename: str) -> ThumbnailFormatEnum:
246        ext = filename.split(".")[-1]
247        if ext in ("jpg", "jpeg", "jfif", "pjpeg", "pjp"):
248            return ThumbnailFormatEnum.JPG
249        if ext in ("png",):
250            return ThumbnailFormatEnum.PNG
251        raise YTUploaderException(
252            f"Unknown format for thumbnail with extension '{ext}'. "
253            "Only JPEG and PNG allowed"
254        )
255
256    def _get_session_token(self):
257        try:
258            # try firefox
259            options = webdriver.FirefoxOptions()
260            options.add_argument("--headless")
261            if self._webdriver_path:
262                service = FirefoxService(self._webdriver_path)
263                driver = webdriver.Firefox(options=options, service=service)
264            else:
265                driver = webdriver.Firefox(options=options)
266        except Exception:
267            try:
268                # try chrome
269                options = webdriver.ChromeOptions()
270                options.add_argument("--headless=new")
271                if self._webdriver_path:
272                    service = ChromeService(self._webdriver_path)
273                    driver = webdriver.Chrome(options=options, service=service)
274                else:
275                    driver = webdriver.Chrome(options=options)
276            except Exception as ex:
277                raise YTUploaderException(
278                    "Could not launch Firefox or Chrome. Make sure geckodriver or "
279                    "chromedriver is installed"
280                ) from ex
281
282        driver.set_page_load_timeout(self._selenium_timeout)
283
284        try:
285            driver.get("https://youtube.com")
286        except Exception as ex:
287            cert_path = os.path.join(
288                driver.backend.storage.home_dir, "mitmproxy-ca-cert.cer"
289            )
290            raise YTUploaderException(
291                "Was not able to load https://youtube.com. Have you installed the cert"
292                f"ificate at {cert_path} ? See https://docs.mitmproxy.org/stable/conce"
293                "pts-certificates/#installing-the-mitmproxy-ca-certificate-manually"
294            ) from ex
295
296        self._reload_cookies()
297        for cookie in self._cookies:
298            if cookie.name in self._cookie_whitelist:
299                driver.add_cookie(cookie.__dict__)
300
301        driver.get("https://youtube.com/upload")
302
303        if "studio.youtube.com/channel" not in driver.current_url:
304            driver.quit()
305            raise YTUploaderException(
306                "Could not log in to YouTube account. Try getting new cookies"
307            )
308
309        r = driver.wait_for_request(
310            "studio.youtube.com/youtubei/v1/ars/grst", timeout=self._selenium_timeout
311        )
312        response = r.response
313        r_json = json.loads(
314            decode(response.body, response.headers.get("Content-Encoding"))
315        )
316        self._session_token = r_json["sessionToken"]
317        self._cookies.set_cookie(
318            Cookie(
319                None,
320                "SESSION_TOKEN",
321                self._session_token,
322                None,
323                False,
324                "",
325                False,
326                False,
327                "",
328                False,
329                False,
330                None,
331                False,
332                None,
333                None,
334                {},
335            )
336        )
337        self._cookies.save()
338        driver.quit()
339
340    @staticmethod
341    def _generateUUID() -> str:
342        return str(uuid.uuid4()).upper()
343
344    @staticmethod
345    def _generateSAPISIDHASH(SAPISID) -> str:
346        timestamp = math.floor(time.time())
347        msg = f"{timestamp} {SAPISID} {'https://studio.youtube.com'}"
348        hash = sha1(msg.encode("utf-8")).hexdigest()
349        return f"{timestamp}_{hash}"
350
351    def _get_session_data(self) -> YTUploaderVideoData:
352        r = self._session.get("https://youtube.com/upload")
353
354        if "studio.youtube.com/channel" not in r.url:
355            raise YTUploaderException(
356                "Could not log in to YouTube account. Try getting new cookies"
357            )
358
359        channel_id = self._channel_id_regex.match(r.url).group(1)  # type: ignore[union-attr]
360        innertube_api_key = self._innertube_api_key_regex.search(r.text).group(1)  # type: ignore[union-attr]
361        m = self._delegated_session_id_regex.search(r.text)
362        delegated_session_id = m and m.group(1)
363        authuser = self._session_index_regex.search(r.text).group(1)  # type: ignore[union-attr]
364        self._session.headers["X-Goog-AuthUser"] = authuser
365        return YTUploaderVideoData(
366            authuser=authuser,
367            channel_id=channel_id,
368            innertube_api_key=innertube_api_key,
369            delegated_session_id=delegated_session_id,
370        )
371
372    def _get_upload_url(self, api_url: str, authuser: str, data: dict) -> str:
373        params = {"authuser": authuser}
374        headers = {
375            "x-goog-upload-command": "start",
376            "x-goog-upload-protocol": "resumable",
377        }
378        r = self._session.post(
379            api_url,
380            headers=headers,
381            params=params,
382            json=data,
383        )
384        r.raise_for_status()
385        upload_url = r.headers["x-goog-upload-url"]
386        return upload_url
387
388    def _get_video_upload_url(self, data: YTUploaderVideoData) -> str:
389        data.front_end_upload_id = f"innertube_studio:{self._generateUUID()}:0"
390        return self._get_upload_url(
391            "https://upload.youtube.com/upload/studio",
392            data.authuser,
393            {"frontendUploadId": data.front_end_upload_id},
394        )
395
396    def _get_upload_url_thumbnail(self, data: YTUploaderVideoData) -> str:
397        return self._get_upload_url(
398            "https://upload.youtube.com/upload/studiothumbnail", data.authuser, {}
399        )
400
401    def _get_creator_playlists(self, data: YTUploaderVideoData) -> dict[str, str]:
402        playlists = {}
403        page_token = ""
404        while True:
405            params = {"key": data.innertube_api_key, "alt": "json"}
406            json = APIRequestListPlaylists.from_session_data(
407                data.channel_id,
408                self._session_token,
409                data.delegated_session_id,
410                page_token,
411            ).to_dict()
412            r = self._session.post(
413                "https://studio.youtube.com/youtubei/v1/creator/list_creator_playlists",
414                params=params,
415                json=json,
416            )
417            r.raise_for_status()
418            json = r.json()
419            playlists.update(
420                {
421                    playlist["title"]: playlist["playlistId"]
422                    for playlist in json.get("playlists", [])
423                }
424            )
425            if json.get("nextPageToken"):
426                page_token = json["nextPageToken"]
427            else:
428                break
429        return playlists
430
431    def _get_claimed_videos(self, data: YTUploaderVideoData) -> list[dict]:
432        videos = []
433        page_token = ""
434        while True:
435            params = {"alt": "json"}
436            json = APIRequestListVideos.list_claimed(
437                data.channel_id,
438                data.delegated_session_id,
439                page_token,
440            ).to_dict()
441            r = self._session.post(
442                "https://studio.youtube.com/youtubei/v1/creator/list_creator_videos",
443                params=params,
444                json=json,
445            )
446            r.raise_for_status()
447            json = r.json()
448            videos += json.get("videos", [])
449            if json.get("nextPageToken"):
450                page_token = json["nextPageToken"]
451            else:
452                break
453        return videos
454
455    def _get_claim_info(self, data: YTUploaderVideoData, video_id: str):
456        params = {"alt": "json"}
457        data = APIRequestListClaim.from_session_data(
458            data.channel_id,
459            data.delegated_session_id,
460            video_id,
461        ).to_dict()
462        r = self._session.post(
463            "https://studio.youtube.com/youtubei/v1/creator/list_creator_received_claims",
464            params=params,
465            json=data,
466        )
467        r.raise_for_status()
468        json = r.json()
469        return list(zip(json["receivedClaims"], json["contentOwners"]))
470
471    def _dispute_claim(
472        self,
473        data: YTUploaderVideoData,
474        claim_id: str,
475        video_id: str,
476        justification: str,
477        legal_name: str,
478    ):
479        params = {"alt": "json"}
480        data = APIRequestDispute.from_session_data(
481            data.channel_id,
482            self._session_token,
483            data.delegated_session_id,
484            claim_id,
485            video_id,
486            justification,
487            legal_name,
488        ).to_dict()
489        r = self._session.post(
490            "https://studio.youtube.com/youtubei/v1/copyright/submit_claim_dispute",
491            params=params,
492            json=data,
493        )
494        r.raise_for_status()
495        data = r.json()
496
497    def _create_playlist(
498        self,
499        playlist: Playlist,
500        data: YTUploaderVideoData,
501    ) -> str:
502        params = {"key": data.innertube_api_key, "alt": "json"}
503        data = APIRequestCreatePlaylist.from_session_data(
504            data.channel_id, self._session_token, data.delegated_session_id, playlist
505        ).to_dict()
506        r = self._session.post(
507            "https://studio.youtube.com/youtubei/v1/playlist/create",
508            params=params,
509            json=data,
510        )
511        r.raise_for_status()
512        return r.json()["playlistId"]
513
514    def _update_captions(
515        self,
516        caption_file: CaptionsFile,
517        data: YTUploaderVideoData,
518    ):
519        params = {"key": data.innertube_api_key, "alt": "json"}
520        with open(caption_file.path, "rb") as f:
521            captions_b64 = "data:application/octet-stream;base64," + base64.b64encode(
522                f.read()
523            ).decode("utf-8")
524        timestamp = str(time.time_ns())
525        assert caption_file.language is not None
526        assert data.encrypted_video_id is not None
527        data = APIRequestUpdateCaptions.from_session_data(
528            data.channel_id,
529            self._session_token,
530            data.delegated_session_id,
531            data.encrypted_video_id,
532            caption_file.path,
533            captions_b64,
534            caption_file.language,
535            timestamp,
536        ).to_dict()
537        r = self._session.post(
538            "https://studio.youtube.com/youtubei/v1/globalization/update_captions",
539            params=params,
540            json=data,
541        )
542        r.raise_for_status()
543
544    def _upload_file(
545        self,
546        upload_url: str,
547        file_path: str,
548        progress_callback: Callable[[str, float], None],
549        prev_progress_step: str,
550        cur_progress_step: str,
551    ) -> str:
552        headers = {
553            "x-goog-upload-command": "upload, finalize",
554            "x-goog-upload-offset": "0",
555        }
556
557        with open(file_path, "rb") as f:
558            f.seek(0, os.SEEK_END)
559            size = f.tell()
560            f.seek(0)
561            bytes_sent = 0
562
563            def upload_callback(bytes: int):
564                nonlocal bytes_sent
565                bytes_sent += bytes
566                start_prog = self._progress_steps[prev_progress_step]
567                end_prog = self._progress_steps[cur_progress_step]
568                cur_prog = start_prog + (end_prog - start_prog) * (bytes_sent / size)
569                cur_prog = round(cur_prog, 1)
570                progress_callback(cur_progress_step, cur_prog)
571
572            wrapped_file = CallbackIOWrapper(upload_callback, f)
573            r = self._session.post(upload_url, headers=headers, data=wrapped_file)
574
575        r.raise_for_status()
576        return r.json()["scottyResourceId"]
577
578    def _create_video(
579        self, scotty_resource_id: str, metadata: Metadata, data: YTUploaderVideoData
580    ) -> Optional[str]:
581        if self._session_token == "":
582            return None
583        assert data.front_end_upload_id is not None
584        params = {"key": data.innertube_api_key, "alt": "json"}
585        data = APIRequestCreateVideo.from_session_data(
586            data.channel_id,
587            self._session_token,
588            data.delegated_session_id,
589            data.front_end_upload_id,
590            metadata,
591            scotty_resource_id,
592        ).to_dict()
593        r = self._session.post(
594            "https://studio.youtube.com/youtubei/v1/upload/createvideo",
595            params=params,
596            json=data,
597        )
598        r.raise_for_status()
599        return r.json().get("videoId")
600
601    def _update_metadata(self, metadata: Metadata, data: YTUploaderVideoData):
602        assert data.encrypted_video_id is not None
603        params = {"key": data.innertube_api_key, "alt": "json"}
604        data = APIRequestUpdateMetadata.from_session_data(
605            data.channel_id,
606            self._session_token,
607            data.delegated_session_id,
608            data.encrypted_video_id,
609            metadata,
610            data.thumbnail_scotty_id,
611            data.thumbnail_format,
612        ).to_dict()
613        r = self._session.post(
614            "https://studio.youtube.com/youtubei/v1/video_manager/metadata_update",
615            params=params,
616            json=data,
617        )
618        r.raise_for_status()

Class for uploading YouTube videos to a single channel

YTUploaderSession( cookie_jar: http.cookiejar.FileCookieJar, webdriver_path: Optional[str] = None, selenium_timeout: float = 60)
 87    def __init__(
 88        self,
 89        cookie_jar: FileCookieJar,
 90        webdriver_path: Optional[str] = None,
 91        selenium_timeout: float = 60,
 92    ):
 93        """Create YTUploaderSession from generic FileCookieJar
 94
 95        Args:
 96            cookie_jar (FileCookieJar): FileCookieJar. Must have save(), load(),
 97                and set_cookie(http.cookiejar.Cookie) methods
 98            webdriver_path (str, optional): Optional path to geckodriver or chromedriver
 99                executable
100            selenium_timeout (float, optional): Timeout to wait for grst request.
101                Defaults to 60 seconds
102        """
103        self._session_token = ""
104        self._webdriver_path = webdriver_path
105        self._selenium_timeout = selenium_timeout
106
107        # load cookies and init session
108        self._cookies = cookie_jar
109        self._session = requests.Session()
110        self._reload_cookies()
111        hash = self._generateSAPISIDHASH(self._session.cookies["SAPISID"])
112        self._session.headers = {
113            "Authorization": f"SAPISIDHASH {hash}",
114            "x-origin": "https://studio.youtube.com",
115            "user-agent": (
116                "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) "
117                "Gecko/20100101 Firefox/119.0"
118            ),
119        }

Create YTUploaderSession from generic FileCookieJar

Arguments:
  • cookie_jar (FileCookieJar): FileCookieJar. Must have save(), load(), and set_cookie(http.cookiejar.Cookie) methods
  • webdriver_path (str, optional): Optional path to geckodriver or chromedriver executable
  • selenium_timeout (float, optional): Timeout to wait for grst request. Defaults to 60 seconds
@classmethod
def from_cookies_txt( cls, cookies_txt_path: str, webdriver_path: Optional[str] = None, selenium_timeout: float = 60):
130    @classmethod
131    def from_cookies_txt(
132        cls,
133        cookies_txt_path: str,
134        webdriver_path: Optional[str] = None,
135        selenium_timeout: float = 60,
136    ):
137        """Create YTUploaderSession from cookies.txt file
138
139        Args:
140            cookies_txt_path (str): Path to Netscape cookies format file
141            webdriver_path (str, optional): Optional path to geckodriver or chromedriver
142                executable
143            selenium_timeout (float, optional): Timeout to wait for grst request.
144                Defaults to 60 seconds
145        """
146        cj = MozillaCookieJar(cookies_txt_path)
147        return cls(cj, webdriver_path, selenium_timeout)

Create YTUploaderSession from cookies.txt file

Arguments:
  • cookies_txt_path (str): Path to Netscape cookies format file
  • webdriver_path (str, optional): Optional path to geckodriver or chromedriver executable
  • selenium_timeout (float, optional): Timeout to wait for grst request. Defaults to 60 seconds
def upload( self, file_path: str, metadata: Metadata, progress_callback: Callable[[str, float], NoneType] = <function YTUploaderSession.<lambda>>) -> str:
149    def upload(
150        self,
151        file_path: str,
152        metadata: Metadata,
153        progress_callback: Callable[[str, float], None] = lambda step, percent: None,
154    ) -> str:
155        """Upload a video
156
157        Args:
158            file_path (str): Path to video file
159            metadata (Metadata): Metadata of video to set when uploaded
160            progress_callback (Callable[[str, float], None], optional): Optional
161                progress callback. Callback receives what step uploader is on and what
162                the total percentage of the upload progress is (defined by
163                YTUploaderSession._progress_steps).
164
165        Returns:
166            str: ID of video uploaded
167        """
168        try:
169            metadata.validate()
170        except ValueError as ex:
171            raise YTUploaderException(f"Validation error: {ex}") from ex
172        progress_callback("start", self._progress_steps["start"])
173        data = self._get_session_data()
174        progress_callback("get_session_data", self._progress_steps["get_session_data"])
175        url = self._get_video_upload_url(data)
176        progress_callback("get_upload_url", self._progress_steps["get_upload_url"])
177        scotty_resource_id = self._upload_file(
178            url, file_path, progress_callback, "get_upload_url", "upload_video"
179        )
180        progress_callback("upload_video", self._progress_steps["upload_video"])
181        encrypted_video_id = self._create_video(scotty_resource_id, metadata, data)
182        if encrypted_video_id is None:
183            # could be bad session token, try to get new one
184            self._get_session_token()
185            progress_callback(
186                "get_session_token", self._progress_steps["get_session_token"]
187            )
188            encrypted_video_id = self._create_video(scotty_resource_id, metadata, data)
189            if encrypted_video_id is None:
190                raise YTUploaderException("Could not create video")
191        data.encrypted_video_id = encrypted_video_id
192        progress_callback("create_video", self._progress_steps["create_video"])
193
194        # set thumbnail
195        if metadata.thumbnail is not None:
196            url = self._get_upload_url_thumbnail(data)
197            data.thumbnail_scotty_id = self._upload_file(
198                url,
199                metadata.thumbnail,
200                progress_callback,
201                "create_video",
202                "upload_thumbnail",
203            )
204            data.thumbnail_format = self._get_thumbnail_format(metadata.thumbnail)
205
206        # playlists
207        if metadata.playlists:
208            playlists = self._get_creator_playlists(data)
209            if metadata.playlist_ids is None:
210                metadata.playlist_ids = []
211            for playlist in metadata.playlists:
212                exists = playlist.title in playlists
213                if (playlist.create_if_title_exists and exists) or (
214                    playlist.create_if_title_doesnt_exist and not exists
215                ):
216                    playlist_id = self._create_playlist(playlist, data)
217                    metadata.playlist_ids.append(playlist_id)
218                elif exists:
219                    metadata.playlist_ids.append(playlists[playlist.title])
220        # captions
221        if metadata.captions_files:
222            for caption_file in metadata.captions_files:
223                if caption_file.language is None:
224                    caption_file.language = metadata.audio_language
225                self._update_captions(caption_file, data)
226
227        self._update_metadata(metadata, data)
228        # save cookies
229        for cookie in self._session.cookies:
230            self._cookies.set_cookie(cookie)
231        self._cookies.save()
232        progress_callback("finish", self._progress_steps["finish"])
233        return data.encrypted_video_id

Upload a video

Arguments:
  • file_path (str): Path to video file
  • metadata (Metadata): Metadata of video to set when uploaded
  • progress_callback (Callable[[str, float], None], optional): Optional progress callback. Callback receives what step uploader is on and what the total percentage of the upload progress is (defined by YTUploaderSession._progress_steps).
Returns:

str: ID of video uploaded

def has_valid_cookies(self) -> bool:
235    def has_valid_cookies(self) -> bool:
236        """Check if cookies are valid
237
238        Returns:
239            bool: True if we are able to log in to YouTube with the given cookies
240        """
241        r = self._session.get("https://youtube.com/upload")
242
243        return "studio.youtube.com/channel" in r.url

Check if cookies are valid

Returns:

bool: True if we are able to log in to YouTube with the given cookies

class YTUploaderException(builtins.Exception):
37class YTUploaderException(Exception):
38    """YouTube uploader exception"""

YouTube uploader exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args
@dataclass_json
@dataclass
class Metadata:
383@dataclass_json
384@dataclass
385class Metadata:
386    """Metadata of video to upload"""
387
388    title: str
389    """Title. Max length 100. Cannot contain < or > characters"""
390
391    description: str = ""
392    """Description. Max length 5000. Cannot contain < or > characters"""
393
394    privacy: PrivacyEnum = PrivacyEnum.PRIVATE
395    """Privacy. Possible values: PUBLIC, UNLISTED, PRIVATE"""
396
397    made_for_kids: bool = False
398    """Made for kids. If true comments will be disabled"""
399
400    tags: tuple[str, ...] = ()
401    """List of tags"""
402
403    # optional metadata for update_metadata
404    scheduled_upload: Optional[datetime.datetime] = field(
405        default=None,
406        metadata=config(
407            decoder=lambda x: (
408                datetime.datetime.fromisoformat(x) if x is not None else None
409            ),
410            encoder=lambda x: datetime.datetime.isoformat(x) if x is not None else None,
411            mm_field=mm_field.DateTime("iso", allow_none=True),
412        ),
413    )
414    """
415    Date to make upload public. If set, video will be set to private until the date,
416    unless video is a premiere in which case it will be set to public. Video will not
417    be a premiere unless both premiere_countdown_duration and premiere_theme are set
418    """
419
420    premiere_countdown_duration: Optional[PremiereDurationEnum] = None
421    """
422    Duration of premiere countdown in seconds.
423    Possible values: 60, 120, 180, 240, 300
424    """
425
426    premiere_theme: Optional[PremiereThemeEnum] = None
427    """
428    Theme of premiere countdown.
429    Possible values: CLASSIC, ALTERNATIVE, AMBIENT, BRIGHT, CALM, CINEMATIC,
430    CONTEMPORARY, DRAMATIC, FUNKY, GENTLE, HAPPY, INSPIRATIONAL, KIDS, SCI_FI, SPORTS
431    """
432
433    playlist_ids: Optional[list[str]] = None
434    """List of existing playlist IDs to add video to"""
435
436    playlists: Optional[list[Playlist]] = None
437    """List of playlists to create and/or add video to"""
438
439    thumbnail: Optional[str] = None
440    """Path to thumbnail file to upload"""
441
442    publish_to_feed: Optional[bool] = None
443    """Whether to notify subscribers"""
444
445    category: Optional[CategoryEnum] = None
446    """
447    Category. Category-specific metadata is not supported yet.
448    Possible values: FILM_ANIMATION, AUTOS_VEHICLES, MUSIC, PETS_ANIMALS, SPORTS,
449    TRAVEL_EVENTS, GAMING, PEOPLE_BLOGS, COMEDY, ENTERTAINMENT, NEWS_POLITICS,
450    HOWTO_STYLE, EDUCATION, SCIENCE_TECH, NONPROFITS_ACTIVISM
451    """
452
453    auto_chapter: Optional[bool] = None
454    """Whether to use automatic video chapters"""
455
456    auto_places: Optional[bool] = None
457    """Whether to use automatic places"""
458
459    auto_concepts: Optional[bool] = None
460    """Whether to use automatic concepts"""
461
462    has_product_placement: Optional[bool] = None
463    """Whether video has product placement"""
464
465    show_product_placement_overlay: Optional[bool] = None
466    """Whether to show product placement overlay"""
467
468    recorded_date: Optional[datetime.date] = field(
469        default=None,
470        metadata=config(
471            decoder=lambda x: datetime.date.fromisoformat(x) if x is not None else None,
472            encoder=lambda x: datetime.date.isoformat(x) if x is not None else None,
473            mm_field=mm_field.Date("iso", allow_none=True),
474        ),
475    )
476    """Day, month, and year that video was recorded"""
477
478    restricted_to_over_18: Optional[bool] = None
479    """Whether video is age restricted"""
480
481    audio_language: Optional[LanguageEnum] = None
482    """Language of audio"""
483
484    captions_files: Optional[list[CaptionsFile]] = None
485    """Path to captions files (.srt)"""
486
487    license: Optional[LicenseEnum] = None
488    """License. Possible values: STANDARD, CREATIVE_COMMONS"""
489
490    allow_comments: Optional[bool] = None
491    """Whether to allow comments"""
492
493    allow_comments_mode: Optional[AllowCommentsEnum] = None
494    """Comment filtering mode. Possible values: ALL_COMMENTS, HOLD_INAPPROPRIATE,
495    HOLD_INAPPROPRIATE_STRICT, HOLD_ALL"""
496
497    can_view_ratings: Optional[bool] = None
498    """Whether video likes/dislikes can be seen"""
499
500    comments_sort_order: Optional[CommentsSortOrderEnum] = None
501    """Default comment sort order. Possible values: LATEST, TOP"""
502
503    allow_embedding: Optional[bool] = None
504    """Whether to allow embedding on 3rd party sites"""
505
506    def validate(self):
507        """Raises error if metadata is invalid"""
508        if (
509            self.premiere_countdown_duration is not None
510            or self.premiere_theme is not None
511        ):
512            if None in (
513                self.premiere_countdown_duration,
514                self.premiere_theme,
515                self.scheduled_upload,
516            ):
517                raise ValueError(
518                    "If trying to upload a premiere, premiere_countdown_duration, "
519                    "premiere_theme, and scheduled_upload must be set"
520                )
521
522        if self.captions_files is not None:
523            for caption_file in self.captions_files:
524                if caption_file.language is None and self.audio_language is None:
525                    raise ValueError(
526                        "Must either specify captions file language or audio_language"
527                    )
528
529        if self.restricted_to_over_18 and self.made_for_kids:
530            raise ValueError(
531                "Video cannot be made for kids and also restricted to over 18"
532            )
533
534        if len(self.title) > 100:
535            raise ValueError("Title must be at most 100 characters long")
536
537        if len(self.description) > 5000:
538            raise ValueError("Description must be at most 5000 characters long")
539
540        to_check = [self.title, self.description] + list(self.tags)
541        if self.playlists:
542            for p in self.playlists:
543                to_check += [p.title, p.description]
544
545        if any(c in s for c in "<>" for s in to_check):
546            raise ValueError(
547                "Titles, descriptions, and tags cannot contain angled brackets"
548            )
549
550        errors = self.schema().validate(self.to_dict())
551        if errors:
552            raise ValueError(f"{errors}")

Metadata of video to upload

Metadata( title: str, description: str = '', privacy: PrivacyEnum = <PrivacyEnum.PRIVATE: 'PRIVATE'>, made_for_kids: bool = False, tags: tuple[str, ...] = (), scheduled_upload: Optional[datetime.datetime] = None, premiere_countdown_duration: Optional[PremiereDurationEnum] = None, premiere_theme: Optional[PremiereThemeEnum] = None, playlist_ids: Optional[list[str]] = None, playlists: Optional[list[Playlist]] = None, thumbnail: Optional[str] = None, publish_to_feed: Optional[bool] = None, category: Optional[CategoryEnum] = None, auto_chapter: Optional[bool] = None, auto_places: Optional[bool] = None, auto_concepts: Optional[bool] = None, has_product_placement: Optional[bool] = None, show_product_placement_overlay: Optional[bool] = None, recorded_date: Optional[datetime.date] = None, restricted_to_over_18: Optional[bool] = None, audio_language: Optional[LanguageEnum] = None, captions_files: Optional[list[CaptionsFile]] = None, license: Optional[LicenseEnum] = None, allow_comments: Optional[bool] = None, allow_comments_mode: Optional[AllowCommentsEnum] = None, can_view_ratings: Optional[bool] = None, comments_sort_order: Optional[CommentsSortOrderEnum] = None, allow_embedding: Optional[bool] = None)
title: str

Title. Max length 100. Cannot contain < or > characters

description: str = ''

Description. Max length 5000. Cannot contain < or > characters

privacy: PrivacyEnum = <PrivacyEnum.PRIVATE: 'PRIVATE'>

Privacy. Possible values: PUBLIC, UNLISTED, PRIVATE

made_for_kids: bool = False

Made for kids. If true comments will be disabled

tags: tuple[str, ...] = ()

List of tags

scheduled_upload: Optional[datetime.datetime] = None

Date to make upload public. If set, video will be set to private until the date, unless video is a premiere in which case it will be set to public. Video will not be a premiere unless both premiere_countdown_duration and premiere_theme are set

premiere_countdown_duration: Optional[PremiereDurationEnum] = None

Duration of premiere countdown in seconds. Possible values: 60, 120, 180, 240, 300

premiere_theme: Optional[PremiereThemeEnum] = None

Theme of premiere countdown. Possible values: CLASSIC, ALTERNATIVE, AMBIENT, BRIGHT, CALM, CINEMATIC, CONTEMPORARY, DRAMATIC, FUNKY, GENTLE, HAPPY, INSPIRATIONAL, KIDS, SCI_FI, SPORTS

playlist_ids: Optional[list[str]] = None

List of existing playlist IDs to add video to

playlists: Optional[list[Playlist]] = None

List of playlists to create and/or add video to

thumbnail: Optional[str] = None

Path to thumbnail file to upload

publish_to_feed: Optional[bool] = None

Whether to notify subscribers

category: Optional[CategoryEnum] = None

Category. Category-specific metadata is not supported yet. Possible values: FILM_ANIMATION, AUTOS_VEHICLES, MUSIC, PETS_ANIMALS, SPORTS, TRAVEL_EVENTS, GAMING, PEOPLE_BLOGS, COMEDY, ENTERTAINMENT, NEWS_POLITICS, HOWTO_STYLE, EDUCATION, SCIENCE_TECH, NONPROFITS_ACTIVISM

auto_chapter: Optional[bool] = None

Whether to use automatic video chapters

auto_places: Optional[bool] = None

Whether to use automatic places

auto_concepts: Optional[bool] = None

Whether to use automatic concepts

has_product_placement: Optional[bool] = None

Whether video has product placement

show_product_placement_overlay: Optional[bool] = None

Whether to show product placement overlay

recorded_date: Optional[datetime.date] = None

Day, month, and year that video was recorded

restricted_to_over_18: Optional[bool] = None

Whether video is age restricted

audio_language: Optional[LanguageEnum] = None

Language of audio

captions_files: Optional[list[CaptionsFile]] = None

Path to captions files (.srt)

license: Optional[LicenseEnum] = None

License. Possible values: STANDARD, CREATIVE_COMMONS

allow_comments: Optional[bool] = None

Whether to allow comments

allow_comments_mode: Optional[AllowCommentsEnum] = None

Comment filtering mode. Possible values: ALL_COMMENTS, HOLD_INAPPROPRIATE, HOLD_INAPPROPRIATE_STRICT, HOLD_ALL

can_view_ratings: Optional[bool] = None

Whether video likes/dislikes can be seen

comments_sort_order: Optional[CommentsSortOrderEnum] = None

Default comment sort order. Possible values: LATEST, TOP

allow_embedding: Optional[bool] = None

Whether to allow embedding on 3rd party sites

def validate(self):
506    def validate(self):
507        """Raises error if metadata is invalid"""
508        if (
509            self.premiere_countdown_duration is not None
510            or self.premiere_theme is not None
511        ):
512            if None in (
513                self.premiere_countdown_duration,
514                self.premiere_theme,
515                self.scheduled_upload,
516            ):
517                raise ValueError(
518                    "If trying to upload a premiere, premiere_countdown_duration, "
519                    "premiere_theme, and scheduled_upload must be set"
520                )
521
522        if self.captions_files is not None:
523            for caption_file in self.captions_files:
524                if caption_file.language is None and self.audio_language is None:
525                    raise ValueError(
526                        "Must either specify captions file language or audio_language"
527                    )
528
529        if self.restricted_to_over_18 and self.made_for_kids:
530            raise ValueError(
531                "Video cannot be made for kids and also restricted to over 18"
532            )
533
534        if len(self.title) > 100:
535            raise ValueError("Title must be at most 100 characters long")
536
537        if len(self.description) > 5000:
538            raise ValueError("Description must be at most 5000 characters long")
539
540        to_check = [self.title, self.description] + list(self.tags)
541        if self.playlists:
542            for p in self.playlists:
543                to_check += [p.title, p.description]
544
545        if any(c in s for c in "<>" for s in to_check):
546            raise ValueError(
547                "Titles, descriptions, and tags cannot contain angled brackets"
548            )
549
550        errors = self.schema().validate(self.to_dict())
551        if errors:
552            raise ValueError(f"{errors}")

Raises error if metadata is invalid

def to_json( self, *, skipkeys: bool = False, ensure_ascii: bool = True, check_circular: bool = True, allow_nan: bool = True, indent: Union[str, int, NoneType] = None, separators: Optional[Tuple[str, str]] = None, default: Optional[Callable] = None, sort_keys: bool = False, **kw) -> str:
27    def to_json(self,
28                *,
29                skipkeys: bool = False,
30                ensure_ascii: bool = True,
31                check_circular: bool = True,
32                allow_nan: bool = True,
33                indent: Optional[Union[int, str]] = None,
34                separators: Optional[Tuple[str, str]] = None,
35                default: Optional[Callable] = None,
36                sort_keys: bool = False,
37                **kw) -> str:
38        return json.dumps(self.to_dict(encode_json=False),
39                          cls=_ExtendedEncoder,
40                          skipkeys=skipkeys,
41                          ensure_ascii=ensure_ascii,
42                          check_circular=check_circular,
43                          allow_nan=allow_nan,
44                          indent=indent,
45                          separators=separators,
46                          default=default,
47                          sort_keys=sort_keys,
48                          **kw)
@classmethod
def from_json( cls: Type[~A], s: Union[str, bytes, bytearray], *, parse_float=None, parse_int=None, parse_constant=None, infer_missing=False, **kw) -> ~A:
50    @classmethod
51    def from_json(cls: Type[A],
52                  s: JsonData,
53                  *,
54                  parse_float=None,
55                  parse_int=None,
56                  parse_constant=None,
57                  infer_missing=False,
58                  **kw) -> A:
59        kvs = json.loads(s,
60                         parse_float=parse_float,
61                         parse_int=parse_int,
62                         parse_constant=parse_constant,
63                         **kw)
64        return cls.from_dict(kvs, infer_missing=infer_missing)
def to_dict( self, encode_json=False) -> Dict[str, Union[dict, list, str, int, float, bool, NoneType]]:
73    def to_dict(self, encode_json=False) -> Dict[str, Json]:
74        return _asdict(self, encode_json=encode_json)
@classmethod
def from_dict( cls: Type[~A], kvs: Union[dict, list, str, int, float, bool, NoneType], *, infer_missing=False) -> ~A:
66    @classmethod
67    def from_dict(cls: Type[A],
68                  kvs: Json,
69                  *,
70                  infer_missing=False) -> A:
71        return _decode_dataclass(cls, kvs, infer_missing)
@classmethod
def schema( cls: Type[~A], *, infer_missing: bool = False, only=None, exclude=(), many: bool = False, context=None, load_only=(), dump_only=(), partial: bool = False, unknown=None) -> dataclasses_json.mm.SchemaF[~A]:
 76    @classmethod
 77    def schema(cls: Type[A],
 78               *,
 79               infer_missing: bool = False,
 80               only=None,
 81               exclude=(),
 82               many: bool = False,
 83               context=None,
 84               load_only=(),
 85               dump_only=(),
 86               partial: bool = False,
 87               unknown=None) -> "SchemaType[A]":
 88        Schema = build_schema(cls, DataClassJsonMixin, infer_missing, partial)
 89
 90        if unknown is None:
 91            undefined_parameter_action = _undefined_parameter_action_safe(cls)
 92            if undefined_parameter_action is not None:
 93                # We can just make use of the same-named mm keywords
 94                unknown = undefined_parameter_action.name.lower()
 95
 96        return Schema(only=only,
 97                      exclude=exclude,
 98                      many=many,
 99                      context=context,
100                      load_only=load_only,
101                      dump_only=dump_only,
102                      partial=partial,
103                      unknown=unknown)
@dataclass_json
@dataclass
class Playlist:
349@dataclass_json
350@dataclass
351class Playlist:
352    """Metadata of playlist to create and/or add video to"""
353
354    title: str
355    """Title. Max length 150"""
356
357    description: str = ""
358    """Description. Max length 5000"""
359
360    privacy: PrivacyEnum = PrivacyEnum.PUBLIC
361    """Privacy. Possible values: PUBLIC, UNLISTED, PRIVATE"""
362
363    create_if_title_exists: bool = False
364    """Whether to create playlist if a playlist with the same
365    title already exists on the channel"""
366
367    create_if_title_doesnt_exist: bool = True
368    """Whether to create playlist if there is no playlist with the same title"""

Metadata of playlist to create and/or add video to

Playlist( title: str, description: str = '', privacy: PrivacyEnum = <PrivacyEnum.PUBLIC: 'PUBLIC'>, create_if_title_exists: bool = False, create_if_title_doesnt_exist: bool = True)
title: str

Title. Max length 150

description: str = ''

Description. Max length 5000

privacy: PrivacyEnum = <PrivacyEnum.PUBLIC: 'PUBLIC'>

Privacy. Possible values: PUBLIC, UNLISTED, PRIVATE

create_if_title_exists: bool = False

Whether to create playlist if a playlist with the same title already exists on the channel

create_if_title_doesnt_exist: bool = True

Whether to create playlist if there is no playlist with the same title

def to_json( self, *, skipkeys: bool = False, ensure_ascii: bool = True, check_circular: bool = True, allow_nan: bool = True, indent: Union[str, int, NoneType] = None, separators: Optional[Tuple[str, str]] = None, default: Optional[Callable] = None, sort_keys: bool = False, **kw) -> str:
27    def to_json(self,
28                *,
29                skipkeys: bool = False,
30                ensure_ascii: bool = True,
31                check_circular: bool = True,
32                allow_nan: bool = True,
33                indent: Optional[Union[int, str]] = None,
34                separators: Optional[Tuple[str, str]] = None,
35                default: Optional[Callable] = None,
36                sort_keys: bool = False,
37                **kw) -> str:
38        return json.dumps(self.to_dict(encode_json=False),
39                          cls=_ExtendedEncoder,
40                          skipkeys=skipkeys,
41                          ensure_ascii=ensure_ascii,
42                          check_circular=check_circular,
43                          allow_nan=allow_nan,
44                          indent=indent,
45                          separators=separators,
46                          default=default,
47                          sort_keys=sort_keys,
48                          **kw)
@classmethod
def from_json( cls: Type[~A], s: Union[str, bytes, bytearray], *, parse_float=None, parse_int=None, parse_constant=None, infer_missing=False, **kw) -> ~A:
50    @classmethod
51    def from_json(cls: Type[A],
52                  s: JsonData,
53                  *,
54                  parse_float=None,
55                  parse_int=None,
56                  parse_constant=None,
57                  infer_missing=False,
58                  **kw) -> A:
59        kvs = json.loads(s,
60                         parse_float=parse_float,
61                         parse_int=parse_int,
62                         parse_constant=parse_constant,
63                         **kw)
64        return cls.from_dict(kvs, infer_missing=infer_missing)
def to_dict( self, encode_json=False) -> Dict[str, Union[dict, list, str, int, float, bool, NoneType]]:
73    def to_dict(self, encode_json=False) -> Dict[str, Json]:
74        return _asdict(self, encode_json=encode_json)
@classmethod
def from_dict( cls: Type[~A], kvs: Union[dict, list, str, int, float, bool, NoneType], *, infer_missing=False) -> ~A:
66    @classmethod
67    def from_dict(cls: Type[A],
68                  kvs: Json,
69                  *,
70                  infer_missing=False) -> A:
71        return _decode_dataclass(cls, kvs, infer_missing)
@classmethod
def schema( cls: Type[~A], *, infer_missing: bool = False, only=None, exclude=(), many: bool = False, context=None, load_only=(), dump_only=(), partial: bool = False, unknown=None) -> dataclasses_json.mm.SchemaF[~A]:
 76    @classmethod
 77    def schema(cls: Type[A],
 78               *,
 79               infer_missing: bool = False,
 80               only=None,
 81               exclude=(),
 82               many: bool = False,
 83               context=None,
 84               load_only=(),
 85               dump_only=(),
 86               partial: bool = False,
 87               unknown=None) -> "SchemaType[A]":
 88        Schema = build_schema(cls, DataClassJsonMixin, infer_missing, partial)
 89
 90        if unknown is None:
 91            undefined_parameter_action = _undefined_parameter_action_safe(cls)
 92            if undefined_parameter_action is not None:
 93                # We can just make use of the same-named mm keywords
 94                unknown = undefined_parameter_action.name.lower()
 95
 96        return Schema(only=only,
 97                      exclude=exclude,
 98                      many=many,
 99                      context=context,
100                      load_only=load_only,
101                      dump_only=dump_only,
102                      partial=partial,
103                      unknown=unknown)
@dataclass_json
@dataclass
class CaptionsFile:
371@dataclass_json
372@dataclass
373class CaptionsFile:
374    """Subtitles file"""
375
376    path: str
377    """Path to .srt file"""
378
379    language: Optional[LanguageEnum] = None
380    """Language of captions. If None, language will default to audio language"""

Subtitles file

CaptionsFile( path: str, language: Optional[LanguageEnum] = None)
path: str

Path to .srt file

language: Optional[LanguageEnum] = None

Language of captions. If None, language will default to audio language

def to_json( self, *, skipkeys: bool = False, ensure_ascii: bool = True, check_circular: bool = True, allow_nan: bool = True, indent: Union[str, int, NoneType] = None, separators: Optional[Tuple[str, str]] = None, default: Optional[Callable] = None, sort_keys: bool = False, **kw) -> str:
27    def to_json(self,
28                *,
29                skipkeys: bool = False,
30                ensure_ascii: bool = True,
31                check_circular: bool = True,
32                allow_nan: bool = True,
33                indent: Optional[Union[int, str]] = None,
34                separators: Optional[Tuple[str, str]] = None,
35                default: Optional[Callable] = None,
36                sort_keys: bool = False,
37                **kw) -> str:
38        return json.dumps(self.to_dict(encode_json=False),
39                          cls=_ExtendedEncoder,
40                          skipkeys=skipkeys,
41                          ensure_ascii=ensure_ascii,
42                          check_circular=check_circular,
43                          allow_nan=allow_nan,
44                          indent=indent,
45                          separators=separators,
46                          default=default,
47                          sort_keys=sort_keys,
48                          **kw)
@classmethod
def from_json( cls: Type[~A], s: Union[str, bytes, bytearray], *, parse_float=None, parse_int=None, parse_constant=None, infer_missing=False, **kw) -> ~A:
50    @classmethod
51    def from_json(cls: Type[A],
52                  s: JsonData,
53                  *,
54                  parse_float=None,
55                  parse_int=None,
56                  parse_constant=None,
57                  infer_missing=False,
58                  **kw) -> A:
59        kvs = json.loads(s,
60                         parse_float=parse_float,
61                         parse_int=parse_int,
62                         parse_constant=parse_constant,
63                         **kw)
64        return cls.from_dict(kvs, infer_missing=infer_missing)
def to_dict( self, encode_json=False) -> Dict[str, Union[dict, list, str, int, float, bool, NoneType]]:
73    def to_dict(self, encode_json=False) -> Dict[str, Json]:
74        return _asdict(self, encode_json=encode_json)
@classmethod
def from_dict( cls: Type[~A], kvs: Union[dict, list, str, int, float, bool, NoneType], *, infer_missing=False) -> ~A:
66    @classmethod
67    def from_dict(cls: Type[A],
68                  kvs: Json,
69                  *,
70                  infer_missing=False) -> A:
71        return _decode_dataclass(cls, kvs, infer_missing)
@classmethod
def schema( cls: Type[~A], *, infer_missing: bool = False, only=None, exclude=(), many: bool = False, context=None, load_only=(), dump_only=(), partial: bool = False, unknown=None) -> dataclasses_json.mm.SchemaF[~A]:
 76    @classmethod
 77    def schema(cls: Type[A],
 78               *,
 79               infer_missing: bool = False,
 80               only=None,
 81               exclude=(),
 82               many: bool = False,
 83               context=None,
 84               load_only=(),
 85               dump_only=(),
 86               partial: bool = False,
 87               unknown=None) -> "SchemaType[A]":
 88        Schema = build_schema(cls, DataClassJsonMixin, infer_missing, partial)
 89
 90        if unknown is None:
 91            undefined_parameter_action = _undefined_parameter_action_safe(cls)
 92            if undefined_parameter_action is not None:
 93                # We can just make use of the same-named mm keywords
 94                unknown = undefined_parameter_action.name.lower()
 95
 96        return Schema(only=only,
 97                      exclude=exclude,
 98                      many=many,
 99                      context=context,
100                      load_only=load_only,
101                      dump_only=dump_only,
102                      partial=partial,
103                      unknown=unknown)
class PremiereThemeEnum(builtins.str, enum.Enum):
302class PremiereThemeEnum(str, Enum):
303    CLASSIC = "VIDEO_PREMIERE_INTRO_THEME_DEFAULT"
304    ALTERNATIVE = "VIDEO_PREMIERE_INTRO_THEME_ALTERNATIVE"
305    AMBIENT = "VIDEO_PREMIERE_INTRO_THEME_AMBIENT"
306    BRIGHT = "VIDEO_PREMIERE_INTRO_THEME_BRIGHT"
307    CALM = "VIDEO_PREMIERE_INTRO_THEME_CALM"
308    CINEMATIC = "VIDEO_PREMIERE_INTRO_THEME_CINEMATIC"
309    CONTEMPORARY = "VIDEO_PREMIERE_INTRO_THEME_CONTEMPORARY"
310    DRAMATIC = "VIDEO_PREMIERE_INTRO_THEME_DRAMATIC"
311    FUNKY = "VIDEO_PREMIERE_INTRO_THEME_FUNKY"
312    GENTLE = "VIDEO_PREMIERE_INTRO_THEME_GENTLE"
313    HAPPY = "VIDEO_PREMIERE_INTRO_THEME_HAPPY"
314    INSPIRATIONAL = "VIDEO_PREMIERE_INTRO_THEME_INSPIRATIONAL"
315    KIDS = "VIDEO_PREMIERE_INTRO_THEME_KIDS"
316    SCI_FI = "VIDEO_PREMIERE_INTRO_THEME_SCI_FI"
317    SPORTS = "VIDEO_PREMIERE_INTRO_THEME_SPORTS"

An enumeration.

CLASSIC = <PremiereThemeEnum.CLASSIC: 'VIDEO_PREMIERE_INTRO_THEME_DEFAULT'>
ALTERNATIVE = <PremiereThemeEnum.ALTERNATIVE: 'VIDEO_PREMIERE_INTRO_THEME_ALTERNATIVE'>
AMBIENT = <PremiereThemeEnum.AMBIENT: 'VIDEO_PREMIERE_INTRO_THEME_AMBIENT'>
BRIGHT = <PremiereThemeEnum.BRIGHT: 'VIDEO_PREMIERE_INTRO_THEME_BRIGHT'>
CALM = <PremiereThemeEnum.CALM: 'VIDEO_PREMIERE_INTRO_THEME_CALM'>
CINEMATIC = <PremiereThemeEnum.CINEMATIC: 'VIDEO_PREMIERE_INTRO_THEME_CINEMATIC'>
CONTEMPORARY = <PremiereThemeEnum.CONTEMPORARY: 'VIDEO_PREMIERE_INTRO_THEME_CONTEMPORARY'>
DRAMATIC = <PremiereThemeEnum.DRAMATIC: 'VIDEO_PREMIERE_INTRO_THEME_DRAMATIC'>
FUNKY = <PremiereThemeEnum.FUNKY: 'VIDEO_PREMIERE_INTRO_THEME_FUNKY'>
GENTLE = <PremiereThemeEnum.GENTLE: 'VIDEO_PREMIERE_INTRO_THEME_GENTLE'>
HAPPY = <PremiereThemeEnum.HAPPY: 'VIDEO_PREMIERE_INTRO_THEME_HAPPY'>
INSPIRATIONAL = <PremiereThemeEnum.INSPIRATIONAL: 'VIDEO_PREMIERE_INTRO_THEME_INSPIRATIONAL'>
KIDS = <PremiereThemeEnum.KIDS: 'VIDEO_PREMIERE_INTRO_THEME_KIDS'>
SCI_FI = <PremiereThemeEnum.SCI_FI: 'VIDEO_PREMIERE_INTRO_THEME_SCI_FI'>
SPORTS = <PremiereThemeEnum.SPORTS: 'VIDEO_PREMIERE_INTRO_THEME_SPORTS'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class PremiereDurationEnum(builtins.str, enum.Enum):
294class PremiereDurationEnum(str, Enum):
295    ONE_MIN = "60"
296    TWO_MIN = "120"
297    THREE_MIN = "180"
298    FOUR_MIN = "240"
299    FIVE_MIN = "300"

An enumeration.

ONE_MIN = <PremiereDurationEnum.ONE_MIN: '60'>
TWO_MIN = <PremiereDurationEnum.TWO_MIN: '120'>
THREE_MIN = <PremiereDurationEnum.THREE_MIN: '180'>
FOUR_MIN = <PremiereDurationEnum.FOUR_MIN: '240'>
FIVE_MIN = <PremiereDurationEnum.FIVE_MIN: '300'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class ThumbnailFormatEnum(builtins.str, enum.Enum):
289class ThumbnailFormatEnum(str, Enum):
290    PNG = "CUSTOM_THUMBNAIL_IMAGE_FORMAT_PNG"
291    JPG = "CUSTOM_THUMBNAIL_IMAGE_FORMAT_JPEG"

An enumeration.

PNG = <ThumbnailFormatEnum.PNG: 'CUSTOM_THUMBNAIL_IMAGE_FORMAT_PNG'>
JPG = <ThumbnailFormatEnum.JPG: 'CUSTOM_THUMBNAIL_IMAGE_FORMAT_JPEG'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class CommentsSortOrderEnum(builtins.str, enum.Enum):
284class CommentsSortOrderEnum(str, Enum):
285    LATEST = "MDE_COMMENT_SORT_ORDER_LATEST"
286    TOP = "MDE_COMMENT_SORT_ORDER_TOP"

An enumeration.

LATEST = <CommentsSortOrderEnum.LATEST: 'MDE_COMMENT_SORT_ORDER_LATEST'>
TOP = <CommentsSortOrderEnum.TOP: 'MDE_COMMENT_SORT_ORDER_TOP'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class AllowCommentsEnum(builtins.str, enum.Enum):
277class AllowCommentsEnum(str, Enum):
278    ALL_COMMENTS = "ALL_COMMENTS"
279    HOLD_INAPPROPRIATE = "AUTOMATED_COMMENTS"
280    HOLD_INAPPROPRIATE_STRICT = "AUTO_MODERATED_COMMENTS_HOLD_MORE"
281    HOLD_ALL = "APPROVED_COMMENTS"

An enumeration.

ALL_COMMENTS = <AllowCommentsEnum.ALL_COMMENTS: 'ALL_COMMENTS'>
HOLD_INAPPROPRIATE = <AllowCommentsEnum.HOLD_INAPPROPRIATE: 'AUTOMATED_COMMENTS'>
HOLD_INAPPROPRIATE_STRICT = <AllowCommentsEnum.HOLD_INAPPROPRIATE_STRICT: 'AUTO_MODERATED_COMMENTS_HOLD_MORE'>
HOLD_ALL = <AllowCommentsEnum.HOLD_ALL: 'APPROVED_COMMENTS'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class LicenseEnum(builtins.str, enum.Enum):
272class LicenseEnum(str, Enum):
273    STANDARD = "standard"
274    CREATIVE_COMMONS = "creative_commons"

An enumeration.

STANDARD = <LicenseEnum.STANDARD: 'standard'>
CREATIVE_COMMONS = <LicenseEnum.CREATIVE_COMMONS: 'creative_commons'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class LanguageEnum(builtins.str, enum.Enum):
 35class LanguageEnum(str, Enum):
 36    NOT_APPLICABLE = "zxx"
 37    ABKHAZIAN = "ab"
 38    AFAR = "aa"
 39    AFRIKAANS = "af"
 40    AKAN = "ak"
 41    AKKADIAN = "akk"
 42    ALBANIAN = "sq"
 43    AMERICAN_SIGN_LANGUAGE = "ase"
 44    AMHARIC = "am"
 45    ARABIC = "ar"
 46    ARAMAIC = "arc"
 47    ARMENIAN = "hy"
 48    ASSAMESE = "as"
 49    AYMARA = "ay"
 50    AZERBAIJANI = "az"
 51    BAMBARA = "bm"
 52    BANGLA = "bn"
 53    BANGLA_INDIA = "bn-IN"
 54    BASHKIR = "ba"
 55    BASQUE = "eu"
 56    BELARUSIAN = "be"
 57    BHOJPURI = "bho"
 58    BISLAMA = "bi"
 59    BODO = "brx"
 60    BOSNIAN = "bs"
 61    BRETON = "br"
 62    BULGARIAN = "bg"
 63    BURMESE = "my"
 64    CANTONESE = "yue"
 65    CANTONESE_HONG_KONG = "yue-HK"
 66    CATALAN = "ca"
 67    CHEROKEE = "chr"
 68    CHINESE = "zh"
 69    CHINESE_CHINA = "zh-CN"
 70    CHINESE_HONG_KONG = "zh-HK"
 71    CHINESE_SIMPLIFIED = "zh-Hans"
 72    CHINESE_SINGAPORE = "zh-SG"
 73    CHINESE_TAIWAN = "zh-TW"
 74    CHINESE_TRADITIONAL = "zh-Hant"
 75    CHOCTAW = "cho"
 76    COPTIC = "cop"
 77    CORSICAN = "co"
 78    CREE = "cr"
 79    CROATIAN = "hr"
 80    CZECH = "cs"
 81    DANISH = "da"
 82    DOGRI = "doi"
 83    DUTCH = "nl"
 84    DUTCH_BELGIUM = "nl-BE"
 85    DUTCH_NETHERLANDS = "nl-NL"
 86    DZONGKHA = "dz"
 87    ENGLISH = "en"
 88    ENGLISH_AUSTRALIA = "en-AU"
 89    ENGLISH_CANADA = "en-CA"
 90    ENGLISH_INDIA = "en-IN"
 91    ENGLISH_IRELAND = "en-IE"
 92    ENGLISH_UNITED_KINGDOM = "en-GB"
 93    ENGLISH_UNITED_STATES = "en-US"
 94    ESPERANTO = "eo"
 95    ESTONIAN = "et"
 96    EWE = "ee"
 97    FAROESE = "fo"
 98    FIJIAN = "fj"
 99    FILIPINO = "fil"
100    FINNISH = "fi"
101    FRENCH = "fr"
102    FRENCH_BELGIUM = "fr-BE"
103    FRENCH_CANADA = "fr-CA"
104    FRENCH_FRANCE = "fr-FR"
105    FRENCH_SWITZERLAND = "fr-CH"
106    FULA = "ff"
107    GALICIAN = "gl"
108    GANDA = "lg"
109    GEORGIAN = "ka"
110    GERMAN = "de"
111    GERMAN_AUSTRIA = "de-AT"
112    GERMAN_GERMANY = "de-DE"
113    GERMAN_SWITZERLAND = "de-CH"
114    GREEK = "el"
115    GUARANI = "gn"
116    GUJARATI = "gu"
117    GUSII = "guz"
118    HAITIAN_CREOLE = "ht"
119    HAKKA_CHINESE = "hak"
120    HAKKA_CHINESE_TAIWAN = "hak-TW"
121    HARYANVI = "bgc"
122    HAUSA = "ha"
123    HAWAIIAN = "haw"
124    HEBREW = "he"
125    HINDI = "hi"
126    HINDI_LATIN = "hi-Latn"
127    HIRI_MOTU = "ho"
128    HUNGARIAN = "hu"
129    ICELANDIC = "is"
130    IGBO = "ig"
131    INDONESIAN = "id"
132    INTERLINGUA = "ia"
133    INTERLINGUE = "ie"
134    INUKTITUT = "iu"
135    INUPIAQ = "ik"
136    IRISH = "ga"
137    ITALIAN = "it"
138    JAPANESE = "ja"
139    JAVANESE = "jv"
140    KALAALLISUT = "kl"
141    KALENJIN = "kln"
142    KAMBA = "kam"
143    KANNADA = "kn"
144    KASHMIRI = "ks"
145    KAZAKH = "kk"
146    KHMER = "km"
147    KIKUYU = "ki"
148    KINYARWANDA = "rw"
149    KLINGON = "tlh"
150    KONKANI = "kok"
151    KOREAN = "ko"
152    KURDISH = "ku"
153    KYRGYZ = "ky"
154    LADINO = "lad"
155    LAO = "lo"
156    LATIN = "la"
157    LATVIAN = "lv"
158    LINGALA = "ln"
159    LITHUANIAN = "lt"
160    LUBA_KATANGA = "lu"
161    LUO = "luo"
162    LUXEMBOURGISH = "lb"
163    LUYIA = "luy"
164    MACEDONIAN = "mk"
165    MAITHILI = "mai"
166    MALAGASY = "mg"
167    MALAY = "ms"
168    MALAY_SINGAPORE = "ms-SG"
169    MALAYALAM = "ml"
170    MALTESE = "mt"
171    MANIPURI = "mni"
172    MAORI = "mi"
173    MARATHI = "mr"
174    MASAI = "mas"
175    MERU = "mer"
176    MIN_NAN_CHINESE = "nan"
177    MIN_NAN_CHINESE_TAIWAN = "nan-TW"
178    MIXE = "mco"
179    MIZO = "lus"
180    MONGOLIAN = "mn"
181    MONGOLIAN_MONGOLIAN = "mn-M"
182    NAURU = "na"
183    NAVAJO = "nv"
184    NEPALI = "ne"
185    NIGERIAN_PIDGIN = "pcm"
186    NORTH_NDEBELE = "nd"
187    NORTHERN_SOTHO = "nso"
188    NORWEGIAN = "no"
189    OCCITAN = "oc"
190    ODIA = "or"
191    OROMO = "om"
192    PAPIAMENTO = "pap"
193    PASHTO = "ps"
194    PERSIAN = "fa"
195    PERSIAN_AFGHANISTAN = "fa-AF"
196    PERSIAN_IRAN = "fa-IR"
197    POLISH = "pl"
198    PORTUGUESE = "pt"
199    PORTUGUESE_BRAZIL = "pt-BR"
200    PORTUGUESE_PORTUGAL = "pt-PT"
201    PUNJABI = "pa"
202    QUECHUA = "qu"
203    ROMANIAN = "ro"
204    ROMANIAN_MOLDOVA = "ro-MD"
205    ROMANSH = "rm"
206    RUNDI = "rn"
207    RUSSIAN = "ru"
208    RUSSIAN_LATIN = "ru-Latn"
209    SAMOAN = "sm"
210    SANGO = "sg"
211    SANSKRIT = "sa"
212    SANTALI = "sat"
213    SARDINIAN = "sc"
214    SCOTTISH_GAELIC = "gd"
215    SERBIAN = "sr"
216    SERBIAN_CYRILLIC = "hr-Cyrl"
217    SERBIAN_LATIN = "sr-Latn"
218    SERBO_CROATIAN = "sh"
219    SHERDUKPEN = "sdp"
220    SHONA = "sn"
221    SICILIAN = "scn"
222    SINDHI = "sd"
223    SINHALA = "si"
224    SLOVAK = "sk"
225    SLOVENIAN = "sl"
226    SOMALI = "so"
227    SOUTH_NDEBELE = "nr"
228    SOUTHERN_SOTHO = "st"
229    SPANISH = "es"
230    SPANISH_LATIN_AMERICA = "es-419"
231    SPANISH_MEXICO = "es-MX"
232    SPANISH_SPAIN = "es-ES"
233    SPANISH_UNITED_STATES = "es-US"
234    SUNDANESE = "su"
235    SWAHILI = "sw"
236    SWATI = "ss"
237    SWEDISH = "sv"
238    TAGALOG = "tl"
239    TAJIK = "tg"
240    TAMIL = "ta"
241    TATAR = "tt"
242    TELUGU = "te"
243    THAI = "th"
244    TIBETAN = "bo"
245    TIGRINYA = "ti"
246    TOK_PISIN = "tpi"
247    TOKI_PONA = "tok"
248    TONGAN = "to"
249    TSONGA = "ts"
250    TSWANA = "tn"
251    TURKISH = "tr"
252    TURKMEN = "tk"
253    TWI = "tw"
254    UKRAINIAN = "uk"
255    URDU = "ur"
256    UYGHUR = "ug"
257    UZBEK = "uz"
258    VENDA = "ve"
259    VIETNAMESE = "vi"
260    VOLAPÜK = "vo"
261    VÕRO = "vro"
262    WELSH = "cy"
263    WESTERN_FRISIAN = "fy"
264    WOLAYTTA = "wal"
265    WOLOF = "wo"
266    XHOSA = "xh"
267    YIDDISH = "yi"
268    YORUBA = "yo"
269    ZULU = "zu"

An enumeration.

NOT_APPLICABLE = <LanguageEnum.NOT_APPLICABLE: 'zxx'>
ABKHAZIAN = <LanguageEnum.ABKHAZIAN: 'ab'>
AFAR = <LanguageEnum.AFAR: 'aa'>
AFRIKAANS = <LanguageEnum.AFRIKAANS: 'af'>
AKAN = <LanguageEnum.AKAN: 'ak'>
AKKADIAN = <LanguageEnum.AKKADIAN: 'akk'>
ALBANIAN = <LanguageEnum.ALBANIAN: 'sq'>
AMERICAN_SIGN_LANGUAGE = <LanguageEnum.AMERICAN_SIGN_LANGUAGE: 'ase'>
AMHARIC = <LanguageEnum.AMHARIC: 'am'>
ARABIC = <LanguageEnum.ARABIC: 'ar'>
ARAMAIC = <LanguageEnum.ARAMAIC: 'arc'>
ARMENIAN = <LanguageEnum.ARMENIAN: 'hy'>
ASSAMESE = <LanguageEnum.ASSAMESE: 'as'>
AYMARA = <LanguageEnum.AYMARA: 'ay'>
AZERBAIJANI = <LanguageEnum.AZERBAIJANI: 'az'>
BAMBARA = <LanguageEnum.BAMBARA: 'bm'>
BANGLA = <LanguageEnum.BANGLA: 'bn'>
BANGLA_INDIA = <LanguageEnum.BANGLA_INDIA: 'bn-IN'>
BASHKIR = <LanguageEnum.BASHKIR: 'ba'>
BASQUE = <LanguageEnum.BASQUE: 'eu'>
BELARUSIAN = <LanguageEnum.BELARUSIAN: 'be'>
BHOJPURI = <LanguageEnum.BHOJPURI: 'bho'>
BISLAMA = <LanguageEnum.BISLAMA: 'bi'>
BODO = <LanguageEnum.BODO: 'brx'>
BOSNIAN = <LanguageEnum.BOSNIAN: 'bs'>
BRETON = <LanguageEnum.BRETON: 'br'>
BULGARIAN = <LanguageEnum.BULGARIAN: 'bg'>
BURMESE = <LanguageEnum.BURMESE: 'my'>
CANTONESE = <LanguageEnum.CANTONESE: 'yue'>
CANTONESE_HONG_KONG = <LanguageEnum.CANTONESE_HONG_KONG: 'yue-HK'>
CATALAN = <LanguageEnum.CATALAN: 'ca'>
CHEROKEE = <LanguageEnum.CHEROKEE: 'chr'>
CHINESE = <LanguageEnum.CHINESE: 'zh'>
CHINESE_CHINA = <LanguageEnum.CHINESE_CHINA: 'zh-CN'>
CHINESE_HONG_KONG = <LanguageEnum.CHINESE_HONG_KONG: 'zh-HK'>
CHINESE_SIMPLIFIED = <LanguageEnum.CHINESE_SIMPLIFIED: 'zh-Hans'>
CHINESE_SINGAPORE = <LanguageEnum.CHINESE_SINGAPORE: 'zh-SG'>
CHINESE_TAIWAN = <LanguageEnum.CHINESE_TAIWAN: 'zh-TW'>
CHINESE_TRADITIONAL = <LanguageEnum.CHINESE_TRADITIONAL: 'zh-Hant'>
CHOCTAW = <LanguageEnum.CHOCTAW: 'cho'>
COPTIC = <LanguageEnum.COPTIC: 'cop'>
CORSICAN = <LanguageEnum.CORSICAN: 'co'>
CREE = <LanguageEnum.CREE: 'cr'>
CROATIAN = <LanguageEnum.CROATIAN: 'hr'>
CZECH = <LanguageEnum.CZECH: 'cs'>
DANISH = <LanguageEnum.DANISH: 'da'>
DOGRI = <LanguageEnum.DOGRI: 'doi'>
DUTCH = <LanguageEnum.DUTCH: 'nl'>
DUTCH_BELGIUM = <LanguageEnum.DUTCH_BELGIUM: 'nl-BE'>
DUTCH_NETHERLANDS = <LanguageEnum.DUTCH_NETHERLANDS: 'nl-NL'>
DZONGKHA = <LanguageEnum.DZONGKHA: 'dz'>
ENGLISH = <LanguageEnum.ENGLISH: 'en'>
ENGLISH_AUSTRALIA = <LanguageEnum.ENGLISH_AUSTRALIA: 'en-AU'>
ENGLISH_CANADA = <LanguageEnum.ENGLISH_CANADA: 'en-CA'>
ENGLISH_INDIA = <LanguageEnum.ENGLISH_INDIA: 'en-IN'>
ENGLISH_IRELAND = <LanguageEnum.ENGLISH_IRELAND: 'en-IE'>
ENGLISH_UNITED_KINGDOM = <LanguageEnum.ENGLISH_UNITED_KINGDOM: 'en-GB'>
ENGLISH_UNITED_STATES = <LanguageEnum.ENGLISH_UNITED_STATES: 'en-US'>
ESPERANTO = <LanguageEnum.ESPERANTO: 'eo'>
ESTONIAN = <LanguageEnum.ESTONIAN: 'et'>
EWE = <LanguageEnum.EWE: 'ee'>
FAROESE = <LanguageEnum.FAROESE: 'fo'>
FIJIAN = <LanguageEnum.FIJIAN: 'fj'>
FILIPINO = <LanguageEnum.FILIPINO: 'fil'>
FINNISH = <LanguageEnum.FINNISH: 'fi'>
FRENCH = <LanguageEnum.FRENCH: 'fr'>
FRENCH_BELGIUM = <LanguageEnum.FRENCH_BELGIUM: 'fr-BE'>
FRENCH_CANADA = <LanguageEnum.FRENCH_CANADA: 'fr-CA'>
FRENCH_FRANCE = <LanguageEnum.FRENCH_FRANCE: 'fr-FR'>
FRENCH_SWITZERLAND = <LanguageEnum.FRENCH_SWITZERLAND: 'fr-CH'>
FULA = <LanguageEnum.FULA: 'ff'>
GALICIAN = <LanguageEnum.GALICIAN: 'gl'>
GANDA = <LanguageEnum.GANDA: 'lg'>
GEORGIAN = <LanguageEnum.GEORGIAN: 'ka'>
GERMAN = <LanguageEnum.GERMAN: 'de'>
GERMAN_AUSTRIA = <LanguageEnum.GERMAN_AUSTRIA: 'de-AT'>
GERMAN_GERMANY = <LanguageEnum.GERMAN_GERMANY: 'de-DE'>
GERMAN_SWITZERLAND = <LanguageEnum.GERMAN_SWITZERLAND: 'de-CH'>
GREEK = <LanguageEnum.GREEK: 'el'>
GUARANI = <LanguageEnum.GUARANI: 'gn'>
GUJARATI = <LanguageEnum.GUJARATI: 'gu'>
GUSII = <LanguageEnum.GUSII: 'guz'>
HAITIAN_CREOLE = <LanguageEnum.HAITIAN_CREOLE: 'ht'>
HAKKA_CHINESE = <LanguageEnum.HAKKA_CHINESE: 'hak'>
HAKKA_CHINESE_TAIWAN = <LanguageEnum.HAKKA_CHINESE_TAIWAN: 'hak-TW'>
HARYANVI = <LanguageEnum.HARYANVI: 'bgc'>
HAUSA = <LanguageEnum.HAUSA: 'ha'>
HAWAIIAN = <LanguageEnum.HAWAIIAN: 'haw'>
HEBREW = <LanguageEnum.HEBREW: 'he'>
HINDI = <LanguageEnum.HINDI: 'hi'>
HINDI_LATIN = <LanguageEnum.HINDI_LATIN: 'hi-Latn'>
HIRI_MOTU = <LanguageEnum.HIRI_MOTU: 'ho'>
HUNGARIAN = <LanguageEnum.HUNGARIAN: 'hu'>
ICELANDIC = <LanguageEnum.ICELANDIC: 'is'>
IGBO = <LanguageEnum.IGBO: 'ig'>
INDONESIAN = <LanguageEnum.INDONESIAN: 'id'>
INTERLINGUA = <LanguageEnum.INTERLINGUA: 'ia'>
INTERLINGUE = <LanguageEnum.INTERLINGUE: 'ie'>
INUKTITUT = <LanguageEnum.INUKTITUT: 'iu'>
INUPIAQ = <LanguageEnum.INUPIAQ: 'ik'>
IRISH = <LanguageEnum.IRISH: 'ga'>
ITALIAN = <LanguageEnum.ITALIAN: 'it'>
JAPANESE = <LanguageEnum.JAPANESE: 'ja'>
JAVANESE = <LanguageEnum.JAVANESE: 'jv'>
KALAALLISUT = <LanguageEnum.KALAALLISUT: 'kl'>
KALENJIN = <LanguageEnum.KALENJIN: 'kln'>
KAMBA = <LanguageEnum.KAMBA: 'kam'>
KANNADA = <LanguageEnum.KANNADA: 'kn'>
KASHMIRI = <LanguageEnum.KASHMIRI: 'ks'>
KAZAKH = <LanguageEnum.KAZAKH: 'kk'>
KHMER = <LanguageEnum.KHMER: 'km'>
KIKUYU = <LanguageEnum.KIKUYU: 'ki'>
KINYARWANDA = <LanguageEnum.KINYARWANDA: 'rw'>
KLINGON = <LanguageEnum.KLINGON: 'tlh'>
KONKANI = <LanguageEnum.KONKANI: 'kok'>
KOREAN = <LanguageEnum.KOREAN: 'ko'>
KURDISH = <LanguageEnum.KURDISH: 'ku'>
KYRGYZ = <LanguageEnum.KYRGYZ: 'ky'>
LADINO = <LanguageEnum.LADINO: 'lad'>
LAO = <LanguageEnum.LAO: 'lo'>
LATIN = <LanguageEnum.LATIN: 'la'>
LATVIAN = <LanguageEnum.LATVIAN: 'lv'>
LINGALA = <LanguageEnum.LINGALA: 'ln'>
LITHUANIAN = <LanguageEnum.LITHUANIAN: 'lt'>
LUBA_KATANGA = <LanguageEnum.LUBA_KATANGA: 'lu'>
LUO = <LanguageEnum.LUO: 'luo'>
LUXEMBOURGISH = <LanguageEnum.LUXEMBOURGISH: 'lb'>
LUYIA = <LanguageEnum.LUYIA: 'luy'>
MACEDONIAN = <LanguageEnum.MACEDONIAN: 'mk'>
MAITHILI = <LanguageEnum.MAITHILI: 'mai'>
MALAGASY = <LanguageEnum.MALAGASY: 'mg'>
MALAY = <LanguageEnum.MALAY: 'ms'>
MALAY_SINGAPORE = <LanguageEnum.MALAY_SINGAPORE: 'ms-SG'>
MALAYALAM = <LanguageEnum.MALAYALAM: 'ml'>
MALTESE = <LanguageEnum.MALTESE: 'mt'>
MANIPURI = <LanguageEnum.MANIPURI: 'mni'>
MAORI = <LanguageEnum.MAORI: 'mi'>
MARATHI = <LanguageEnum.MARATHI: 'mr'>
MASAI = <LanguageEnum.MASAI: 'mas'>
MERU = <LanguageEnum.MERU: 'mer'>
MIN_NAN_CHINESE = <LanguageEnum.MIN_NAN_CHINESE: 'nan'>
MIN_NAN_CHINESE_TAIWAN = <LanguageEnum.MIN_NAN_CHINESE_TAIWAN: 'nan-TW'>
MIXE = <LanguageEnum.MIXE: 'mco'>
MIZO = <LanguageEnum.MIZO: 'lus'>
MONGOLIAN = <LanguageEnum.MONGOLIAN: 'mn'>
MONGOLIAN_MONGOLIAN = <LanguageEnum.MONGOLIAN_MONGOLIAN: 'mn-M'>
NAURU = <LanguageEnum.NAURU: 'na'>
NAVAJO = <LanguageEnum.NAVAJO: 'nv'>
NEPALI = <LanguageEnum.NEPALI: 'ne'>
NIGERIAN_PIDGIN = <LanguageEnum.NIGERIAN_PIDGIN: 'pcm'>
NORTH_NDEBELE = <LanguageEnum.NORTH_NDEBELE: 'nd'>
NORTHERN_SOTHO = <LanguageEnum.NORTHERN_SOTHO: 'nso'>
NORWEGIAN = <LanguageEnum.NORWEGIAN: 'no'>
OCCITAN = <LanguageEnum.OCCITAN: 'oc'>
ODIA = <LanguageEnum.ODIA: 'or'>
OROMO = <LanguageEnum.OROMO: 'om'>
PAPIAMENTO = <LanguageEnum.PAPIAMENTO: 'pap'>
PASHTO = <LanguageEnum.PASHTO: 'ps'>
PERSIAN = <LanguageEnum.PERSIAN: 'fa'>
PERSIAN_AFGHANISTAN = <LanguageEnum.PERSIAN_AFGHANISTAN: 'fa-AF'>
PERSIAN_IRAN = <LanguageEnum.PERSIAN_IRAN: 'fa-IR'>
POLISH = <LanguageEnum.POLISH: 'pl'>
PORTUGUESE = <LanguageEnum.PORTUGUESE: 'pt'>
PORTUGUESE_BRAZIL = <LanguageEnum.PORTUGUESE_BRAZIL: 'pt-BR'>
PORTUGUESE_PORTUGAL = <LanguageEnum.PORTUGUESE_PORTUGAL: 'pt-PT'>
PUNJABI = <LanguageEnum.PUNJABI: 'pa'>
QUECHUA = <LanguageEnum.QUECHUA: 'qu'>
ROMANIAN = <LanguageEnum.ROMANIAN: 'ro'>
ROMANIAN_MOLDOVA = <LanguageEnum.ROMANIAN_MOLDOVA: 'ro-MD'>
ROMANSH = <LanguageEnum.ROMANSH: 'rm'>
RUNDI = <LanguageEnum.RUNDI: 'rn'>
RUSSIAN = <LanguageEnum.RUSSIAN: 'ru'>
RUSSIAN_LATIN = <LanguageEnum.RUSSIAN_LATIN: 'ru-Latn'>
SAMOAN = <LanguageEnum.SAMOAN: 'sm'>
SANGO = <LanguageEnum.SANGO: 'sg'>
SANSKRIT = <LanguageEnum.SANSKRIT: 'sa'>
SANTALI = <LanguageEnum.SANTALI: 'sat'>
SARDINIAN = <LanguageEnum.SARDINIAN: 'sc'>
SCOTTISH_GAELIC = <LanguageEnum.SCOTTISH_GAELIC: 'gd'>
SERBIAN = <LanguageEnum.SERBIAN: 'sr'>
SERBIAN_CYRILLIC = <LanguageEnum.SERBIAN_CYRILLIC: 'hr-Cyrl'>
SERBIAN_LATIN = <LanguageEnum.SERBIAN_LATIN: 'sr-Latn'>
SERBO_CROATIAN = <LanguageEnum.SERBO_CROATIAN: 'sh'>
SHERDUKPEN = <LanguageEnum.SHERDUKPEN: 'sdp'>
SHONA = <LanguageEnum.SHONA: 'sn'>
SICILIAN = <LanguageEnum.SICILIAN: 'scn'>
SINDHI = <LanguageEnum.SINDHI: 'sd'>
SINHALA = <LanguageEnum.SINHALA: 'si'>
SLOVAK = <LanguageEnum.SLOVAK: 'sk'>
SLOVENIAN = <LanguageEnum.SLOVENIAN: 'sl'>
SOMALI = <LanguageEnum.SOMALI: 'so'>
SOUTH_NDEBELE = <LanguageEnum.SOUTH_NDEBELE: 'nr'>
SOUTHERN_SOTHO = <LanguageEnum.SOUTHERN_SOTHO: 'st'>
SPANISH = <LanguageEnum.SPANISH: 'es'>
SPANISH_LATIN_AMERICA = <LanguageEnum.SPANISH_LATIN_AMERICA: 'es-419'>
SPANISH_MEXICO = <LanguageEnum.SPANISH_MEXICO: 'es-MX'>
SPANISH_SPAIN = <LanguageEnum.SPANISH_SPAIN: 'es-ES'>
SPANISH_UNITED_STATES = <LanguageEnum.SPANISH_UNITED_STATES: 'es-US'>
SUNDANESE = <LanguageEnum.SUNDANESE: 'su'>
SWAHILI = <LanguageEnum.SWAHILI: 'sw'>
SWATI = <LanguageEnum.SWATI: 'ss'>
SWEDISH = <LanguageEnum.SWEDISH: 'sv'>
TAGALOG = <LanguageEnum.TAGALOG: 'tl'>
TAJIK = <LanguageEnum.TAJIK: 'tg'>
TAMIL = <LanguageEnum.TAMIL: 'ta'>
TATAR = <LanguageEnum.TATAR: 'tt'>
TELUGU = <LanguageEnum.TELUGU: 'te'>
THAI = <LanguageEnum.THAI: 'th'>
TIBETAN = <LanguageEnum.TIBETAN: 'bo'>
TIGRINYA = <LanguageEnum.TIGRINYA: 'ti'>
TOK_PISIN = <LanguageEnum.TOK_PISIN: 'tpi'>
TOKI_PONA = <LanguageEnum.TOKI_PONA: 'tok'>
TONGAN = <LanguageEnum.TONGAN: 'to'>
TSONGA = <LanguageEnum.TSONGA: 'ts'>
TSWANA = <LanguageEnum.TSWANA: 'tn'>
TURKISH = <LanguageEnum.TURKISH: 'tr'>
TURKMEN = <LanguageEnum.TURKMEN: 'tk'>
TWI = <LanguageEnum.TWI: 'tw'>
UKRAINIAN = <LanguageEnum.UKRAINIAN: 'uk'>
URDU = <LanguageEnum.URDU: 'ur'>
UYGHUR = <LanguageEnum.UYGHUR: 'ug'>
UZBEK = <LanguageEnum.UZBEK: 'uz'>
VENDA = <LanguageEnum.VENDA: 've'>
VIETNAMESE = <LanguageEnum.VIETNAMESE: 'vi'>
VOLAPÜK = <LanguageEnum.VOLAPÜK: 'vo'>
VÕRO = <LanguageEnum.VÕRO: 'vro'>
WELSH = <LanguageEnum.WELSH: 'cy'>
WESTERN_FRISIAN = <LanguageEnum.WESTERN_FRISIAN: 'fy'>
WOLAYTTA = <LanguageEnum.WOLAYTTA: 'wal'>
WOLOF = <LanguageEnum.WOLOF: 'wo'>
XHOSA = <LanguageEnum.XHOSA: 'xh'>
YIDDISH = <LanguageEnum.YIDDISH: 'yi'>
YORUBA = <LanguageEnum.YORUBA: 'yo'>
ZULU = <LanguageEnum.ZULU: 'zu'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class CategoryEnum(builtins.int, enum.Enum):
17class CategoryEnum(int, Enum):
18    FILM_ANIMATION = 1
19    AUTOS_VEHICLES = 2
20    MUSIC = 10
21    PETS_ANIMALS = 15
22    SPORTS = 17
23    TRAVEL_EVENTS = 19
24    GAMING = 20
25    PEOPLE_BLOGS = 22
26    COMEDY = 23
27    ENTERTAINMENT = 24
28    NEWS_POLITICS = 25
29    HOWTO_STYLE = 26
30    EDUCATION = 27
31    SCIENCE_TECH = 28
32    NONPROFITS_ACTIVISM = 29

An enumeration.

FILM_ANIMATION = <CategoryEnum.FILM_ANIMATION: 1>
AUTOS_VEHICLES = <CategoryEnum.AUTOS_VEHICLES: 2>
MUSIC = <CategoryEnum.MUSIC: 10>
PETS_ANIMALS = <CategoryEnum.PETS_ANIMALS: 15>
SPORTS = <CategoryEnum.SPORTS: 17>
TRAVEL_EVENTS = <CategoryEnum.TRAVEL_EVENTS: 19>
GAMING = <CategoryEnum.GAMING: 20>
PEOPLE_BLOGS = <CategoryEnum.PEOPLE_BLOGS: 22>
COMEDY = <CategoryEnum.COMEDY: 23>
ENTERTAINMENT = <CategoryEnum.ENTERTAINMENT: 24>
NEWS_POLITICS = <CategoryEnum.NEWS_POLITICS: 25>
HOWTO_STYLE = <CategoryEnum.HOWTO_STYLE: 26>
EDUCATION = <CategoryEnum.EDUCATION: 27>
SCIENCE_TECH = <CategoryEnum.SCIENCE_TECH: 28>
NONPROFITS_ACTIVISM = <CategoryEnum.NONPROFITS_ACTIVISM: 29>
Inherited Members
enum.Enum
name
value
builtins.int
conjugate
bit_length
bit_count
to_bytes
from_bytes
as_integer_ratio
real
imag
numerator
denominator
class PrivacyEnum(builtins.str, enum.Enum):
11class PrivacyEnum(str, Enum):
12    PRIVATE = "PRIVATE"
13    UNLISTED = "UNLISTED"
14    PUBLIC = "PUBLIC"

An enumeration.

PRIVATE = <PrivacyEnum.PRIVATE: 'PRIVATE'>
UNLISTED = <PrivacyEnum.UNLISTED: 'UNLISTED'>
PUBLIC = <PrivacyEnum.PUBLIC: 'PUBLIC'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans