diff --git a/config/config.example.yml b/config/config.example.yml index a3a2eeb7..bb8b5ff2 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -796,7 +796,7 @@ default_user_preferences: ## ## Default video quality. ## - ## Accepted values: dash, hd720, medium, small + ## Accepted values: hls, dash, hd720, medium, small ## Default: hd720 ## #quality: hd720 diff --git a/locales/en-US.json b/locales/en-US.json index c23f6bc3..ae3b4f50 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -81,6 +81,7 @@ "preferences_speed_label": "Default speed: ", "preferences_quality_label": "Preferred video quality: ", "preferences_quality_option_dash": "DASH (adaptive quality)", + "preferences_quality_option_hls": "HLS", "preferences_quality_option_hd720": "HD720", "preferences_quality_option_medium": "Medium", "preferences_quality_option_small": "Small", diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index d89e752c..41aa434e 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -177,7 +177,7 @@ module Invidious::Routes::API::Manifest manifest = response.body if local - manifest = manifest.gsub(/^https:\/\/\w+---.{11}\.c\.youtube\.com[^\n]*/m) do |match| + manifest = manifest.gsub(/^(https:\/\/\w+---.{11}\.c\.youtube\.com[^\n]*)|(https:\/\/\w+---.{11}\.googlevideo\.com[^\n]*)/m) do |match| path = URI.parse(match).path path = path.lchop("/videoplayback/") diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr index aabe8dfc..634b4cee 100644 --- a/src/invidious/routes/watch.cr +++ b/src/invidious/routes/watch.cr @@ -52,7 +52,11 @@ module Invidious::Routes::Watch env.params.query.delete_all("listen") begin - video = get_video(id, region: params.region) + if params.quality == "hls" + video = get_video(id, region: params.region, force_hls: true) + else + video = get_video(id, region: params.region) + end rescue ex : NotFoundException LOGGER.error("get_video not found: #{id} : #{ex.message}") return error_template(404, ex) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index ae09e736..e8165f84 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -294,8 +294,8 @@ struct Video predicate_bool upcoming, isUpcoming end -def get_video(id, refresh = true, region = nil, force_refresh = false) - if (video = Invidious::Database::Videos.select(id)) && !region +def get_video(id, refresh = true, region = nil, force_hls = false, force_refresh = false) + if (video = Invidious::Database::Videos.select(id)) && !region && !force_hls # If record was last updated over 10 minutes ago, or video has since premiered, # refresh (expire param in response lasts for 6 hours) if (refresh && @@ -312,8 +312,8 @@ def get_video(id, refresh = true, region = nil, force_refresh = false) end end else - video = fetch_video(id, region) - Invidious::Database::Videos.insert(video) if !region + video = fetch_video(id, region, force_hls) + Invidious::Database::Videos.insert(video) if !region && !force_hls end return video @@ -323,8 +323,8 @@ rescue DB::Error return fetch_video(id, region) end -def fetch_video(id, region) - info = extract_video_info(video_id: id) +def fetch_video(id, region, force_hls = false) + info = extract_video_info(video_id: id, force_hls: force_hls) if reason = info["reason"]? if reason == "Video unavailable" diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 915c9baf..7b6605f3 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -50,7 +50,7 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)? } end -def extract_video_info(video_id : String) +def extract_video_info(video_id : String, force_hls : Bool = false) # Init client config for the API client_config = YoutubeAPI::ClientConfig.new @@ -101,16 +101,29 @@ def extract_video_info(video_id : String) params["reason"] = JSON::Any.new(reason) if reason new_player_response = nil - - # Don't use Android test suite client if po_token is passed because po_token doesn't - # work for Android test suite client. - if reason.nil? && CONFIG.po_token.nil? - # Fetch the video streams using an Android client in order to get the - # decrypted URLs and maybe fix throttling issues (#2194). See the - # following issue for an explanation about decrypted URLs: - # https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 - client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite + if force_hls + client_config.client_type = YoutubeAPI::ClientType::IOS new_player_response = try_fetch_streaming_data(video_id, client_config) + else + # Don't use Android test suite client if po_token is passed because po_token doesn't + # work for Android test suite client. + if reason.nil? && CONFIG.po_token.nil? + # Fetch the video streams using an Android client in order to get the + # decrypted URLs and maybe fix throttling issues (#2194). See the + # following issue for an explanation about decrypted URLs: + # https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 + client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite + new_player_response = try_fetch_streaming_data(video_id, client_config) + else + if reason.nil? + # Fetch the video streams using an Android client in order to get the + # decrypted URLs and maybe fix throttling issues (#2194). See the + # following issue for an explanation about decrypted URLs: + # https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 + client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite + new_player_response = try_fetch_streaming_data(video_id, client_config) + end + end end # Replace player response and reset reason diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 5c28358b..b9b29601 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -4,8 +4,10 @@ <% if params.autoplay %>autoplay<% end %> <% if params.video_loop %>loop<% end %> <% if params.controls %>controls<% end %>> - <% if (hlsvp = video.hls_manifest_url) && !CONFIG.disabled?("livestreams") %> + <% if (hlsvp = video.hls_manifest_url) && video.live_now && !CONFIG.disabled?("livestreams") %> + <% elsif (hlsvp = video.hls_manifest_url) && params.quality == "hls" %> + <% else %> <% if params.listen %> <% # default to 128k m4a stream diff --git a/src/invidious/views/user/preferences.ecr b/src/invidious/views/user/preferences.ecr index cf8b5593..11f7890b 100644 --- a/src/invidious/views/user/preferences.ecr +++ b/src/invidious/views/user/preferences.ecr @@ -54,7 +54,7 @@