From 9c393e3f6220d34d534bef7d9d345782003b58ad Mon Sep 17 00:00:00 2001 From: pomtnp Date: Sun, 4 Jan 2026 04:48:42 +0700 Subject: [PATCH] [ie/tiktok] Extract `save_count` (#15054) Closes #15053 Authored by: pomtnp --- README.md | 1 + test/helper.py | 2 +- yt_dlp/YoutubeDL.py | 2 +- yt_dlp/extractor/common.py | 1 + yt_dlp/extractor/tiktok.py | 18 ++++++++++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 41868cb4a9..6e3e23fcbf 100644 --- a/README.md +++ b/README.md @@ -1351,6 +1351,7 @@ The available fields are: - `repost_count` (numeric): Number of reposts of the video - `average_rating` (numeric): Average rating given by users, the scale used depends on the webpage - `comment_count` (numeric): Number of comments on the video (For some extractors, comments are only downloaded at the end, and so this field cannot be used) + - `save_count` (numeric): Number of times the video has been saved or bookmarked - `age_limit` (numeric): Age restriction for the video (years) - `live_status` (string): One of "not_live", "is_live", "is_upcoming", "was_live", "post_live" (was live, but VOD is not yet processed) - `is_live` (boolean): Whether this video is a live stream or a fixed-length video diff --git a/test/helper.py b/test/helper.py index 5a937d9617..adc5f455e5 100644 --- a/test/helper.py +++ b/test/helper.py @@ -263,7 +263,7 @@ def expect_info_dict(self, got_dict, expected_dict): # NB: Keep in sync with the docstring of extractor/common.py 'ie_key', 'url', 'id', 'ext', 'direct', 'display_id', 'title', 'alt_title', 'description', 'media_type', 'uploader', 'uploader_id', 'uploader_url', 'channel', 'channel_id', 'channel_url', 'channel_is_verified', - 'channel_follower_count', 'comment_count', 'view_count', 'concurrent_view_count', + 'channel_follower_count', 'comment_count', 'view_count', 'concurrent_view_count', 'save_count', 'like_count', 'dislike_count', 'repost_count', 'average_rating', 'age_limit', 'duration', 'thumbnail', 'heatmap', 'chapters', 'chapter', 'chapter_number', 'chapter_id', 'start_time', 'end_time', 'section_start', 'section_end', 'categories', 'tags', 'cast', 'composers', 'artists', 'album_artists', 'creators', 'genres', diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index b9bc9c9caa..aceaa59eb8 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -595,7 +595,7 @@ class YoutubeDL: 'width', 'height', 'asr', 'audio_channels', 'fps', 'tbr', 'abr', 'vbr', 'filesize', 'filesize_approx', 'timestamp', 'release_timestamp', 'available_at', - 'duration', 'view_count', 'like_count', 'dislike_count', 'repost_count', + 'duration', 'view_count', 'like_count', 'dislike_count', 'repost_count', 'save_count', 'average_rating', 'comment_count', 'age_limit', 'start_time', 'end_time', 'chapter_number', 'season_number', 'episode_number', diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index d6e2596ae1..70f143387b 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -348,6 +348,7 @@ class InfoExtractor: duration: Length of the video in seconds, as an integer or float. view_count: How many users have watched the video on the platform. concurrent_view_count: How many users are currently watching the video on the platform. + save_count: Number of times the video has been saved or bookmarked like_count: Number of positive ratings of the video dislike_count: Number of negative ratings of the video repost_count: Number of reposts of the video diff --git a/yt_dlp/extractor/tiktok.py b/yt_dlp/extractor/tiktok.py index b7e058ebe7..02ec2b2f45 100644 --- a/yt_dlp/extractor/tiktok.py +++ b/yt_dlp/extractor/tiktok.py @@ -454,6 +454,7 @@ class TikTokBaseIE(InfoExtractor): 'like_count': 'digg_count', 'repost_count': 'share_count', 'comment_count': 'comment_count', + 'save_count': 'collect_count', }, expected_type=int_or_none), **author_info, 'channel_url': format_field(author_info, 'channel_id', self._UPLOADER_URL_FORMAT, default=None), @@ -607,6 +608,7 @@ class TikTokBaseIE(InfoExtractor): 'like_count': 'diggCount', 'repost_count': 'shareCount', 'comment_count': 'commentCount', + 'save_count': 'collectCount', }), expected_type=int_or_none), 'thumbnails': [ { @@ -646,6 +648,7 @@ class TikTokIE(TikTokBaseIE): 'like_count': int, 'repost_count': int, 'comment_count': int, + 'save_count': int, 'artist': 'Ysrbeats', 'album': 'Lehanga', 'track': 'Lehanga', @@ -675,6 +678,7 @@ class TikTokIE(TikTokBaseIE): 'like_count': int, 'repost_count': int, 'comment_count': int, + 'save_count': int, 'artists': ['Evan Todd', 'Jessica Keenan Wynn', 'Alice Lee', 'Barrett Wilbert Weed', 'Jon Eidson'], 'track': 'Big Fun', }, @@ -702,6 +706,7 @@ class TikTokIE(TikTokBaseIE): 'like_count': int, 'repost_count': int, 'comment_count': int, + 'save_count': int, }, }, { # Sponsored video, only available with feed workaround @@ -725,6 +730,7 @@ class TikTokIE(TikTokBaseIE): 'like_count': int, 'repost_count': int, 'comment_count': int, + 'save_count': int, }, 'skip': 'This video is unavailable', }, { @@ -751,6 +757,7 @@ class TikTokIE(TikTokBaseIE): 'like_count': int, 'repost_count': int, 'comment_count': int, + 'save_count': int, }, }, { # hydration JSON is sent in a