From 85c6c916541968f298badb391b718cdf6d81d332 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 19 Jun 2021 13:13:59 +0200 Subject: Support youtube description chapters --- src/plugins/Youtube.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'src/plugins') diff --git a/src/plugins/Youtube.cpp b/src/plugins/Youtube.cpp index a8c58cb..b45b5fe 100644 --- a/src/plugins/Youtube.cpp +++ b/src/plugins/Youtube.cpp @@ -5,6 +5,7 @@ #include "../../include/StringUtils.hpp" #include "../../include/Scale.hpp" #include "../../include/Notification.hpp" +#include "../../include/VideoPlayer.hpp" #include "../../include/Utils.hpp" #include extern "C" { @@ -2019,7 +2020,49 @@ R"END( return chosen_audio_format->base.url; } - PluginResult YoutubeVideoPage::load(std::string &title, std::string &channel_url) { + // Returns -1 if timestamp is in an invalid format + static int youtube_comment_timestamp_to_seconds(const char *str, size_t size) { + if(size > 30) + return -1; + + char timestamp[32]; + memcpy(timestamp, str, size); + timestamp[size] = '\0'; + + int hours = 0; + int minutes = 0; + int seconds = 0; + if(sscanf(timestamp, "%d:%d:%d", &hours, &minutes, &seconds) == 3) + return (hours * 60 * 60) + (minutes * 60) + seconds; + if(sscanf(timestamp, "%d:%d", &minutes, &seconds) == 2) + return (minutes * 60) + seconds; + if(sscanf(timestamp, "%d", &seconds) == 1) + return seconds; + return -1; + } + + static std::vector youtube_description_extract_chapters(const std::string &description) { + std::vector result; + string_split(description, '\n', [&result](const char *str, size_t size) { + const void *first_space_p = memchr(str, ' ', size); + if(!first_space_p) + return true; + + int timestamp_seconds = youtube_comment_timestamp_to_seconds(str, (const char*)first_space_p - str); + if(timestamp_seconds == -1) + return true; + + MediaChapter chapter; + chapter.start_seconds = timestamp_seconds; + chapter.title.assign((const char*)first_space_p, (str + size) - (const char*)first_space_p); + chapter.title = strip(chapter.title); + result.push_back(std::move(chapter)); + return true; + }); + return result; + } + + PluginResult YoutubeVideoPage::load(std::string &title, std::string &channel_url, std::vector &chapters) { hls_manifest_url.clear(); video_formats.clear(); audio_formats.clear(); @@ -2084,6 +2127,13 @@ R"END( const Json::Value &channel_id_json = video_details_json["channelId"]; if(channel_id_json.isString()) channel_url = "https://www.youtube.com/channel/" + channel_id_json.asString(); + + const Json::Value &description_json = video_details_json["shortDescription"]; + if(description_json.isString()) { + std::string description = description_json.asString(); + string_replace_all(description, '+', ' '); + chapters = youtube_description_extract_chapters(description); + } } const Json::Value &playback_tracing_json = json_root["playbackTracking"]; -- cgit v1.2.3