invidious-mod/src/invidious/routes/api/v1/misc.cr

154 行
4.6 KiB
Crystal

module Invidious::Routes::API::V1::Misc
# Stats API endpoint for Invidious
def self.stats(env)
env.response.content_type = "application/json"
if !CONFIG.statistics_enabled
return {"software" => SOFTWARE}.to_json
else
return Invidious::Jobs::StatisticsRefreshJob::STATISTICS.to_json
end
end
# APIv1 currently uses the same logic for both
# user playlists and Invidious playlists. This means that we can't
# reasonably split them yet. This should be addressed in APIv2
def self.get_playlist(env : HTTP::Server::Context)
env.response.content_type = "application/json"
plid = env.params.url["plid"]
offset = env.params.query["index"]?.try &.to_i?
offset ||= env.params.query["page"]?.try &.to_i?.try { |page| (page - 1) * 100 }
offset ||= 0
video_id = env.params.query["continuation"]?
format = env.params.query["format"]?
format ||= "json"
if plid.starts_with? "RD"
return env.redirect "/api/v1/mixes/#{plid}"
end
begin
playlist = get_playlist(plid)
rescue ex : InfoException
return error_json(404, ex)
rescue ex
return error_json(404, "Playlist does not exist.")
end
user = env.get?("user").try &.as(User)
if !playlist || playlist.privacy.private? && playlist.author != user.try &.email
return error_json(404, "Playlist does not exist.")
end
# includes into the playlist a maximum of 20 videos, before the offset
if offset > 0
lookback = offset < 50 ? offset : 50
response = playlist.to_json(offset - lookback)
json_response = JSON.parse(response)
else
# Unless the continuation is really the offset 0, it becomes expensive.
# It happens when the offset is not set.
# First we find the actual offset, and then we lookback
# it shouldn't happen often though
lookback = 0
response = playlist.to_json(offset, video_id: video_id)
json_response = JSON.parse(response)
if json_response["videos"].as_a[0]["index"] != offset
offset = json_response["videos"].as_a[0]["index"].as_i
lookback = offset < 50 ? offset : 50
response = playlist.to_json(offset - lookback)
json_response = JSON.parse(response)
end
end
if format == "html"
playlist_html = template_playlist(json_response)
index, next_video = json_response["videos"].as_a.skip(1 + lookback).select { |video| !video["author"].as_s.empty? }[0]?.try { |v| {v["index"], v["videoId"]} } || {nil, nil}
response = {
"playlistHtml" => playlist_html,
"index" => index,
"nextVideo" => next_video,
}.to_json
end
response
end
def self.mixes(env)
locale = env.get("preferences").as(Preferences).locale
env.response.content_type = "application/json"
rdid = env.params.url["rdid"]
continuation = env.params.query["continuation"]?
continuation ||= rdid.lchop("RD")[0, 11]
format = env.params.query["format"]?
format ||= "json"
begin
mix = fetch_mix(rdid, continuation, locale: locale)
if !rdid.ends_with? continuation
mix = fetch_mix(rdid, mix.videos[1].id)
index = mix.videos.index(mix.videos.select { |video| video.id == continuation }[0]?)
end
mix.videos = mix.videos[index..-1]
rescue ex
return error_json(500, ex)
end
response = JSON.build do |json|
json.object do
json.field "title", mix.title
json.field "mixId", mix.id
json.field "videos" do
json.array do
mix.videos.each do |video|
json.object do
json.field "title", video.title
json.field "videoId", video.id
json.field "author", video.author
json.field "authorId", video.ucid
json.field "authorUrl", "/channel/#{video.ucid}"
json.field "videoThumbnails" do
json.array do
Invidious::JSONify::APIv1.thumbnails(json, video.id)
end
end
json.field "index", video.index
json.field "lengthSeconds", video.length_seconds
end
end
end
end
end
end
if format == "html"
response = JSON.parse(response)
playlist_html = template_mix(response)
next_video = response["videos"].as_a.select { |video| !video["author"].as_s.empty? }[0]?.try &.["videoId"]
response = {
"playlistHtml" => playlist_html,
"nextVideo" => next_video,
}.to_json
end
response
end
end