Skip to content

Commit

Permalink
Change YouTube metadata query to client.
Browse files Browse the repository at this point in the history
This should (hopefully) solve future problems on dedicated servers.
  • Loading branch information
CattoGamer committed Jul 30, 2024
1 parent 2fd73a3 commit 6a832a6
Showing 1 changed file with 76 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,58 @@ SERVICE.Name = "YouTube"
SERVICE.IsTimed = true

SERVICE.Dependency = DEPENDENCY_PARTIAL
SERVICE.ExtentedVideoInfo = true

local METADATA_URL = "https://www.youtube.com/watch?v=%s"

--[[
Credits to veitikka (https://github.com/veitikka) for fixing YouTube service and writing the
Workaround with a Metadata parser.
--]]

-- Lua search patterns to find metadata from the html
local patterns = {
["title"] = "<meta%sproperty=\"og:title\"%s-content=%b\"\">",
["title_fallback"] = "<title>.-</title>",
["duration"] = "<meta%sitemprop%s-=%s-\"duration\"%s-content%s-=%s-%b\"\">",
["live"] = "<meta%sitemprop%s-=%s-\"isLiveBroadcast\"%s-content%s-=%s-%b\"\">",
["live_enddate"] = "<meta%sitemprop%s-=%s-\"endDate\"%s-content%s-=%s-%b\"\">",
["age_restriction"] = "<meta%sproperty=\"og:restrictions:age\"%s-content=%b\"\">"
}

---
-- Function to parse video metadata straight from the html instead of using the API
--
local function ParseMetaDataFromHTML( html )
--MetaData table to return when we're done
local metadata, html = {}, html

-- Fetch title, with fallbacks if needed
metadata.title = util.ParseElementAttribute(html:match(patterns["title"]), "content")
or util.ParseElementContent(html:match(patterns["title_fallback"]))

-- Parse HTML entities in the title into symbols
metadata.title = url.htmlentities_decode(metadata.title)

metadata.familyfriendly = util.ParseElementAttribute(html:match(patterns["age_restriction"]), "content") or ""

-- See if the video is an ongoing live broadcast
-- Set duration to 0 if it is, otherwise use the actual duration
local isLiveBroadcast = tobool(util.ParseElementAttribute(html:match(patterns["live"]), "content"))
local broadcastEndDate = html:match(patterns["live_enddate"])
if isLiveBroadcast and not broadcastEndDate then
-- Mark as live video
metadata.duration = 0
else
local durationISO8601 = util.ParseElementAttribute(html:match(patterns["duration"]), "content")
if isstring(durationISO8601) then
metadata.duration = math.max(1, util.ISO_8601ToSeconds(durationISO8601))
end
end

return metadata
end

function SERVICE:Match( url )
return url.host and url.host:match("youtu.?be[.com]?")
end
Expand All @@ -26,6 +75,27 @@ if (CLIENT) then
self:LoadExFunctions(pnl)
end
end

function SERVICE:GetMetadata( data, callback )


http.Fetch(METADATA_URL:format(data), function(body, length, headers, code)
if not body or code ~= 200 then
callback({ err = ("Not expected response received from YouTube (Code: %d)"):format(code) })
return
end

local status, metadata = pcall(ParseMetaDataFromHTML, body)
if not status then
callback({ err = "Failed to parse MetaData from YouTube" })
return
end

callback(metadata)
end, function(error)
callback({ err = ("YouTube Error: %s"):format(error) })
end, {})
end
end

function SERVICE:GetURLInfo( url )
Expand Down Expand Up @@ -62,70 +132,17 @@ function SERVICE:GetURLInfo( url )
return info.Data and info or false
end

--[[
Credits to veitikka (https://github.com/veitikka) for fixing YouTube service and writing the
Workaround with a Metadata parser.
--]]

-- Lua search patterns to find metadata from the html
local patterns = {
["title"] = "<meta%sproperty=\"og:title\"%s-content=%b\"\">",
["title_fallback"] = "<title>.-</title>",
["thumb"] = "<meta%sproperty=\"og:image\"%s-content=%b\"\">",
["thumb_fallback"] = "<link%sitemprop=\"thumbnailUrl\"%s-href=%b\"\">",
["duration"] = "<meta%sitemprop%s-=%s-\"duration\"%s-content%s-=%s-%b\"\">",
["live"] = "<meta%sitemprop%s-=%s-\"isLiveBroadcast\"%s-content%s-=%s-%b\"\">",
["live_enddate"] = "<meta%sitemprop%s-=%s-\"endDate\"%s-content%s-=%s-%b\"\">",
["age_restriction"] = "<meta%sproperty=\"og:restrictions:age\"%s-content=%b\"\">"
}

---
-- Function to parse video metadata straight from the html instead of using the API
--
local function ParseMetaDataFromHTML( html )
--MetaData table to return when we're done
local metadata, html = {}, html

-- Fetch title and thumbnail, with fallbacks if needed
metadata.title = util.ParseElementAttribute(html:match(patterns["title"]), "content")
or util.ParseElementContent(html:match(patterns["title_fallback"]))

-- Parse HTML entities in the title into symbols
metadata.title = url.htmlentities_decode(metadata.title)

metadata.thumbnail = util.ParseElementAttribute(html:match(patterns["thumb"]), "content")
or util.ParseElementAttribute(html:match(patterns["thumb_fallback"]), "href")

metadata.familyfriendly = util.ParseElementAttribute(html:match(patterns["age_restriction"]), "content") or ""

-- See if the video is an ongoing live broadcast
-- Set duration to 0 if it is, otherwise use the actual duration
local isLiveBroadcast = tobool(util.ParseElementAttribute(html:match(patterns["live"]), "content"))
local broadcastEndDate = html:match(patterns["live_enddate"])
if isLiveBroadcast and not broadcastEndDate then
-- Mark as live video
metadata.duration = 0
else
local durationISO8601 = util.ParseElementAttribute(html:match(patterns["duration"]), "content")
if isstring(durationISO8601) then
metadata.duration = math.max(1, util.ISO_8601ToSeconds(durationISO8601))
end
end

return metadata
end

function SERVICE:GetVideoInfo( data, onSuccess, onFailure )

local onReceive = function( body, length, headers, code )
local status, metadata = pcall(ParseMetaDataFromHTML, body)
if not status then
return onFailure( "Theater_RequestFailed" )
theater.FetchVideoMedata( data:GetOwner(), data, function(metadata)

if metadata.err then
return onFailure(metadata.err)
end

local info = {}
info.title = metadata.title
info.thumbnail = metadata.thumbnail
info.thumbnail = ("https://img.youtube.com/vi/(%s)/hqdefault.jpg"):format(data)

if metadata.duration == 0 then
info.type = "youtubelive"
Expand All @@ -141,11 +158,7 @@ function SERVICE:GetVideoInfo( data, onSuccess, onFailure )
if onSuccess then
pcall(onSuccess, info)
end

end

local url = METADATA_URL:format( data )
self:Fetch( url, onReceive, onFailure )
end)

end

Expand Down

0 comments on commit 6a832a6

Please sign in to comment.