mirror of
https://git.nadeko.net/Fijxu/invidious.git
synced 2025-12-29 19:11:23 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -27,10 +27,21 @@ module Invidious::Routes::API::V1::Channels
|
||||
# Retrieve "sort by" setting from URL parameters
|
||||
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"
|
||||
|
||||
begin
|
||||
videos, _ = Channel::Tabs.get_videos(channel, sort_by: sort_by)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
if channel.is_age_gated
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UULF"))
|
||||
videos = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
videos = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
begin
|
||||
videos, _ = Channel::Tabs.get_videos(channel, sort_by: sort_by)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
end
|
||||
|
||||
JSON.build do |json|
|
||||
@@ -84,6 +95,7 @@ module Invidious::Routes::API::V1::Channels
|
||||
json.field "joined", channel.joined.to_unix
|
||||
|
||||
json.field "autoGenerated", channel.auto_generated
|
||||
json.field "ageGated", channel.is_age_gated
|
||||
json.field "isFamilyFriendly", channel.is_family_friendly
|
||||
json.field "description", html_to_content(channel.description_html)
|
||||
json.field "descriptionHtml", channel.description_html
|
||||
@@ -142,12 +154,23 @@ module Invidious::Routes::API::V1::Channels
|
||||
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"
|
||||
continuation = env.params.query["continuation"]?
|
||||
|
||||
begin
|
||||
videos, next_continuation = Channel::Tabs.get_60_videos(
|
||||
channel, continuation: continuation, sort_by: sort_by
|
||||
)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
if channel.is_age_gated
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UULF"))
|
||||
videos = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
videos = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
begin
|
||||
videos, next_continuation = Channel::Tabs.get_60_videos(
|
||||
channel, continuation: continuation, sort_by: sort_by
|
||||
)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
end
|
||||
|
||||
return JSON.build do |json|
|
||||
@@ -176,12 +199,23 @@ module Invidious::Routes::API::V1::Channels
|
||||
# Retrieve continuation from URL parameters
|
||||
continuation = env.params.query["continuation"]?
|
||||
|
||||
begin
|
||||
videos, next_continuation = Channel::Tabs.get_shorts(
|
||||
channel, continuation: continuation
|
||||
)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
if channel.is_age_gated
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UUSH"))
|
||||
videos = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
videos = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
begin
|
||||
videos, next_continuation = Channel::Tabs.get_shorts(
|
||||
channel, continuation: continuation
|
||||
)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
end
|
||||
|
||||
return JSON.build do |json|
|
||||
@@ -211,12 +245,23 @@ module Invidious::Routes::API::V1::Channels
|
||||
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"
|
||||
continuation = env.params.query["continuation"]?
|
||||
|
||||
begin
|
||||
videos, next_continuation = Channel::Tabs.get_60_livestreams(
|
||||
channel, continuation: continuation, sort_by: sort_by
|
||||
)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
if channel.is_age_gated
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UULV"))
|
||||
videos = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
videos = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
begin
|
||||
videos, next_continuation = Channel::Tabs.get_60_livestreams(
|
||||
channel, continuation: continuation, sort_by: sort_by
|
||||
)
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
end
|
||||
|
||||
return JSON.build do |json|
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require "html"
|
||||
|
||||
module Invidious::Routes::API::V1::Videos
|
||||
def self.videos(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
@@ -116,7 +118,7 @@ module Invidious::Routes::API::V1::Videos
|
||||
else
|
||||
caption_xml = XML.parse(caption_xml)
|
||||
|
||||
webvtt = WebVTT.build(settings_field) do |webvtt|
|
||||
webvtt = WebVTT.build(settings_field) do |builder|
|
||||
caption_nodes = caption_xml.xpath_nodes("//transcript/text")
|
||||
caption_nodes.each_with_index do |node, i|
|
||||
start_time = node["start"].to_f.seconds
|
||||
@@ -136,7 +138,7 @@ module Invidious::Routes::API::V1::Videos
|
||||
text = "<v #{md["name"]}>#{md["text"]}</v>"
|
||||
end
|
||||
|
||||
webvtt.cue(start_time, end_time, text)
|
||||
builder.cue(start_time, end_time, text)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -187,15 +189,14 @@ module Invidious::Routes::API::V1::Videos
|
||||
haltf env, 500
|
||||
end
|
||||
|
||||
storyboards = video.storyboards
|
||||
width = env.params.query["width"]?
|
||||
height = env.params.query["height"]?
|
||||
width = env.params.query["width"]?.try &.to_i
|
||||
height = env.params.query["height"]?.try &.to_i
|
||||
|
||||
if !width && !height
|
||||
response = JSON.build do |json|
|
||||
json.object do
|
||||
json.field "storyboards" do
|
||||
Invidious::JSONify::APIv1.storyboards(json, id, storyboards)
|
||||
Invidious::JSONify::APIv1.storyboards(json, id, video.storyboards)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -205,35 +206,48 @@ module Invidious::Routes::API::V1::Videos
|
||||
|
||||
env.response.content_type = "text/vtt"
|
||||
|
||||
storyboard = storyboards.select { |sb| width == "#{sb[:width]}" || height == "#{sb[:height]}" }
|
||||
# Select a storyboard matching the user's provided width/height
|
||||
storyboard = video.storyboards.select { |x| x.width == width || x.height == height }
|
||||
haltf env, 404 if storyboard.empty?
|
||||
|
||||
if storyboard.empty?
|
||||
haltf env, 404
|
||||
else
|
||||
storyboard = storyboard[0]
|
||||
end
|
||||
# Alias variable, to make the code below esaier to read
|
||||
sb = storyboard[0]
|
||||
|
||||
WebVTT.build do |vtt|
|
||||
start_time = 0.milliseconds
|
||||
end_time = storyboard[:interval].milliseconds
|
||||
# Some base URL segments that we'll use to craft the final URLs
|
||||
work_url = sb.proxied_url.dup
|
||||
template_path = sb.proxied_url.path
|
||||
|
||||
storyboard[:storyboard_count].times do |i|
|
||||
url = storyboard[:url]
|
||||
authority = /(i\d?).ytimg.com/.match!(url)[1]?
|
||||
url = url.gsub("$M", i).gsub(%r(https://i\d?.ytimg.com/sb/), "")
|
||||
url = "#{HOST_URL}/sb/#{authority}/#{url}"
|
||||
# Initialize cue timing variables
|
||||
# NOTE: videojs-vtt-thumbnails gets lost when the cue times don't overlap
|
||||
# (i.e: if cue[n] end time is 1:06:25.000, cue[n+1] start time should be 1:06:25.000)
|
||||
time_delta = sb.interval.milliseconds
|
||||
start_time = 0.milliseconds
|
||||
end_time = time_delta
|
||||
|
||||
storyboard[:storyboard_height].times do |j|
|
||||
storyboard[:storyboard_width].times do |k|
|
||||
current_cue_url = "#{url}#xywh=#{storyboard[:width] * k},#{storyboard[:height] * j},#{storyboard[:width] - 2},#{storyboard[:height]}"
|
||||
vtt.cue(start_time, end_time, current_cue_url)
|
||||
# Build a VTT file for VideoJS-vtt plugin
|
||||
vtt_file = WebVTT.build do |vtt|
|
||||
sb.images_count.times do |i|
|
||||
# Replace the variable component part of the path
|
||||
work_url.path = template_path.sub("$M", i)
|
||||
|
||||
start_time += storyboard[:interval].milliseconds
|
||||
end_time += storyboard[:interval].milliseconds
|
||||
sb.rows.times do |j|
|
||||
sb.columns.times do |k|
|
||||
# The URL fragment represents the offset of the thumbnail inside the storyboard image
|
||||
work_url.fragment = "xywh=#{sb.width * k},#{sb.height * j},#{sb.width - 2},#{sb.height}"
|
||||
|
||||
vtt.cue(start_time, end_time, work_url.to_s)
|
||||
|
||||
start_time += time_delta
|
||||
end_time += time_delta
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# videojs-vtt-thumbnails is not compliant to the VTT specification, it
|
||||
# doesn't unescape the HTML entities, so we have to do it here:
|
||||
# TODO: remove this when we migrate to VideoJS 8
|
||||
return HTML.unescape(vtt_file)
|
||||
end
|
||||
|
||||
def self.annotations(env)
|
||||
|
||||
@@ -36,12 +36,24 @@ module Invidious::Routes::Channels
|
||||
items = items.select(SearchPlaylist)
|
||||
items.each(&.author = "")
|
||||
else
|
||||
sort_options = {"newest", "oldest", "popular"}
|
||||
|
||||
# Fetch items and continuation token
|
||||
items, next_continuation = Channel::Tabs.get_videos(
|
||||
channel, continuation: continuation, sort_by: (sort_by || "newest")
|
||||
)
|
||||
if channel.is_age_gated
|
||||
sort_by = ""
|
||||
sort_options = [] of String
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UULF"))
|
||||
items = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
items = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
sort_options = {"newest", "oldest", "popular"}
|
||||
items, next_continuation = Channel::Tabs.get_videos(
|
||||
channel, continuation: continuation, sort_by: (sort_by || "newest")
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
selected_tab = Frontend::ChannelPage::TabsAvailable::Videos
|
||||
@@ -58,14 +70,27 @@ module Invidious::Routes::Channels
|
||||
return env.redirect "/channel/#{channel.ucid}"
|
||||
end
|
||||
|
||||
# TODO: support sort option for shorts
|
||||
sort_by = ""
|
||||
sort_options = [] of String
|
||||
if channel.is_age_gated
|
||||
sort_by = ""
|
||||
sort_options = [] of String
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UUSH"))
|
||||
items = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
items = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
# TODO: support sort option for shorts
|
||||
sort_by = ""
|
||||
sort_options = [] of String
|
||||
|
||||
# Fetch items and continuation token
|
||||
items, next_continuation = Channel::Tabs.get_shorts(
|
||||
channel, continuation: continuation
|
||||
)
|
||||
# Fetch items and continuation token
|
||||
items, next_continuation = Channel::Tabs.get_shorts(
|
||||
channel, continuation: continuation
|
||||
)
|
||||
end
|
||||
|
||||
selected_tab = Frontend::ChannelPage::TabsAvailable::Shorts
|
||||
templated "channel"
|
||||
@@ -81,13 +106,26 @@ module Invidious::Routes::Channels
|
||||
return env.redirect "/channel/#{channel.ucid}"
|
||||
end
|
||||
|
||||
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"
|
||||
sort_options = {"newest", "oldest", "popular"}
|
||||
if channel.is_age_gated
|
||||
sort_by = ""
|
||||
sort_options = [] of String
|
||||
begin
|
||||
playlist = get_playlist(channel.ucid.sub("UC", "UULV"))
|
||||
items = get_playlist_videos(playlist, offset: 0)
|
||||
rescue ex : InfoException
|
||||
# playlist doesnt exist.
|
||||
items = [] of PlaylistVideo
|
||||
end
|
||||
next_continuation = nil
|
||||
else
|
||||
sort_by = env.params.query["sort_by"]?.try &.downcase || "newest"
|
||||
sort_options = {"newest", "oldest", "popular"}
|
||||
|
||||
# Fetch items and continuation token
|
||||
items, next_continuation = Channel::Tabs.get_60_livestreams(
|
||||
channel, continuation: continuation, sort_by: sort_by
|
||||
)
|
||||
# Fetch items and continuation token
|
||||
items, next_continuation = Channel::Tabs.get_60_livestreams(
|
||||
channel, continuation: continuation, sort_by: sort_by
|
||||
)
|
||||
end
|
||||
|
||||
selected_tab = Frontend::ChannelPage::TabsAvailable::Streams
|
||||
templated "channel"
|
||||
|
||||
@@ -51,6 +51,12 @@ module Invidious::Routes::Search
|
||||
else
|
||||
user = env.get? "user"
|
||||
|
||||
# An URL was copy/pasted in the search box.
|
||||
# Redirect the user to the appropriate page.
|
||||
if query.url?
|
||||
return env.redirect UrlSanitizer.process(query.text).to_s
|
||||
end
|
||||
|
||||
begin
|
||||
items = query.process
|
||||
rescue ex : ChannelSearchException
|
||||
|
||||
@@ -135,7 +135,7 @@ module Invidious::Routes::VideoPlayback
|
||||
end
|
||||
|
||||
# TODO: Record bytes written so we can restart after a chunk fails
|
||||
while true
|
||||
loop do
|
||||
if !range_end && content_length
|
||||
range_end = content_length
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user