diff --git a/workshop/gamemodes/cinema_modded/gamemode/modules/theater/services/sh_youtube.lua b/workshop/gamemodes/cinema_modded/gamemode/modules/theater/services/sh_youtube.lua index 88aeff9..9923734 100644 --- a/workshop/gamemodes/cinema_modded/gamemode/modules/theater/services/sh_youtube.lua +++ b/workshop/gamemodes/cinema_modded/gamemode/modules/theater/services/sh_youtube.lua @@ -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"] = "", + ["title_fallback"] = ".-", + ["duration"] = "", + ["live"] = "", + ["live_enddate"] = "", + ["age_restriction"] = "" +} + +--- +-- 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 @@ -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 ) @@ -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"] = "", - ["title_fallback"] = ".-", - ["thumb"] = "", - ["thumb_fallback"] = "", - ["duration"] = "", - ["live"] = "", - ["live_enddate"] = "", - ["age_restriction"] = "" -} - ---- --- 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" @@ -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