From 92795f2f18717881d8761c0e975a70fa910d4c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shadowsun=E2=84=A2?= <12494967+CattoGamer@users.noreply.github.com> Date: Sun, 4 Aug 2024 04:54:45 +0200 Subject: [PATCH 1/5] Add Country names --- .../gamemode/extensions/sh_util.lua | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/workshop/gamemodes/cinema_modded/gamemode/extensions/sh_util.lua b/workshop/gamemodes/cinema_modded/gamemode/extensions/sh_util.lua index 9b19e42..97f8f70 100644 --- a/workshop/gamemodes/cinema_modded/gamemode/extensions/sh_util.lua +++ b/workshop/gamemodes/cinema_modded/gamemode/extensions/sh_util.lua @@ -50,4 +50,106 @@ function ParseElementContent( element ) local output = element:gsub( "^%s-<%w->%s-", "" ) -- Trim end return output:gsub( "%s-%s-$", "" ) +end + + +local countrys = { + DM="Dominica",IO="British Indian Ocean Territory", + FM="Micronesia, Federated States of",AM="Armenia", + JO="Jordan",CM="Cameroon", + BM="Bermuda",FO="Faroe Islands", + AO="Angola",DO="Dominican Republic", + BO="Bolivia, Plurinational State of",TK="Tokelau", + ZM="Zambia",CO="Colombia", + TM="Turkmenistan",RS="Serbia", + MS="Montserrat",PS="Palestine, State of", + PM="Saint Pierre and Miquelon",SM="San Marino", + MM="Myanmar",WS="Samoa", + TO="Tonga",BQ="Bonaire, Sint Eustatius and Saba", + RO="Romania",MO="Macao", + GQ="Equatorial Guinea",SO="Somalia", + AQ="Antarctica",BS="Bahamas", + TW="Taiwan, Province of China",AS="American Samoa", + MW="Malawi",IQ="Iraq", + ZW="Zimbabwe",ES="Spain", + GU="Guam",YE="Yemen", + AU="Australia",IS="Iceland", + LS="Lesotho",SZ="Swaziland", + ZA="South Africa",LU="Luxembourg", + AW="Aruba",NU="Niue", + GW="Guinea-Bissau",BW="Botswana", + VI="Virgin Islands, U.S.",BH="Bahrain", + CW="Curaçao",PY="Paraguay", + VE="Venezuela, Bolivarian Republic of",KY="Cayman Islands", + VU="Vanuatu",UZ="Uzbekistan",LY="Libya", + UY="Uruguay",KW="Kuwait", + UM="United States Minor Outlying Islands",US="United States", + CY="Cyprus",BY="Belarus",PH="Philippines", + AE="United Arab Emirates",GY="Guyana", + UA="Ukraine",BB="Barbados",UG="Uganda", + TV="Tuvalu",TC="Turks and Caicos Islands", + PR="Puerto Rico",GB="United Kingdom", + PN="Pitcairn",GD="Grenada", + TG="Togo",AD="Andorra", + SH="Saint Helena, Ascension and Tristan da Cunha", + CD="Congo, the Democratic Republic of the", + TH="Thailand",TZ="Tanzania, United Republic of", + LB="Lebanon",TJ="Tajikistan", + AF="Afghanistan",ID="Indonesia", + GF="French Guiana",SY="Syrian Arab Republic", + SS="South Sudan",BF="Burkina Faso", + CF="Central African Republic",SE="Sweden", + KH="Cambodia",NF="Norfolk Island", + EH="Western Sahara",SD="Sudan", + VC="Saint Vincent and the Grenadines",MD="Moldova, Republic of", + SR="Suriname",LK="Sri Lanka",CH="Switzerland", + GS="South Georgia and the South Sandwich Islands",WF="Wallis and Futuna", + TF="French Southern Territories",GH="Ghana",SB="Solomon Islands", + SI="Slovenia",PF="French Polynesia",SK="Slovakia", + SX="Sint Maarten (Dutch part)",BJ="Benin",IL="Israel", + NL="Netherlands",SG="Singapore",FJ="Fiji", + RU="Russian Federation",DJ="Djibouti",GL="Greenland", + IN="India",AL="Albania",SC="Seychelles", + CL="Chile",SN="Senegal",SA="Saudi Arabia", + BL="Saint Barthélemy",HN="Honduras",BI="Burundi", + SJ="Svalbard and Jan Mayen",GN="Guinea",MF="Saint Martin (French part)", + LC="Saint Lucia",BN="Brunei Darussalam",CN="China", + KN="Saint Kitts and Nevis",TL="Timor-Leste",MR="Mauritania", + RW="Rwanda",SL="Sierra Leone",PL="Poland",ML="Mali", + EE="Estonia",QA="Qatar",TR="Turkey",VN="Viet Nam", + TT="Trinidad and Tobago",TN="Tunisia",GP="Guadeloupe", + ST="Sao Tome and Principe",PT="Portugal",MT="Malta", + KP="Korea, Democratic People's Republic of",PG="Papua New Guinea", + BR="Brazil",JP="Japan",GG="Guernsey", + AR="Argentina",FR="France",GR="Greece", + SV="El Salvador",ER="Eritrea",PW="Palau", + KR="Korea, Republic of",HR="Croatia", + IR="Iran, Islamic Republic of",NR="Nauru", + PK="Pakistan",LR="Liberia",HU="Hungary", + LT="Lithuania",IT="Italy",MX="Mexico", + NO="Norway",HT="Haiti",BV="Bouvet Island", + CV="Cape Verde",MP="Northern Mariana Islands",BD="Bangladesh", + MZ="Mozambique",TD="Chad",LV="Latvia",NI="Nicaragua", + NZ="New Zealand",AT="Austria",NP="Nepal",CX="Christmas Island", + MV="Maldives",MK="Macedonia, the Former Yugoslav Republic of",KZ="Kazakhstan", + ME="Montenegro",MN="Mongolia",AX="Åland Islands", + FK="Falkland Islands (Malvinas)",CA="Canada",BA="Bosnia and Herzegovina", + BZ="Belize",CZ="Czech Republic",GA="Gabon",AZ="Azerbaijan", + YT="Mayotte",MU="Mauritius",DZ="Algeria",MQ="Martinique", + CC="Cocos (Keeling) Islands",LA="Lao People's Democratic Republic",MH="Marshall Islands", + NA="Namibia",MY="Malaysia",PA="Panama", + DE="Germany",GE="Georgia",MA="Morocco", + NC="New Caledonia",DK="Denmark",VA="Holy See (Vatican City State)", + BE="Belgium",BT="Bhutan",MC="Monaco",AG="Antigua and Barbuda", + NE="Niger",IE="Ireland",BG="Bulgaria",KE="Kenya",JE="Jersey", + CG="Congo",NG="Nigeria",GM="Gambia",KI="Kiribati",CU="Cuba", + PE="Peru",LI="Liechtenstein",RE="Réunion",KG="Kyrgyzstan", + VG="Virgin Islands, British",EG="Egypt",CI="Côte d'Ivoire",HK="Hong Kong", + ET="Ethiopia",MG="Madagascar",GI="Gibraltar",FI="Finland",AI="Anguilla", + EC="Ecuador",GT="Guatemala",OM="Oman",CK="Cook Islands",IM="Isle of Man", + HM="Heard Island and McDonald Islands",KM="Comoros",JM="Jamaica",CR="Costa Rica" +} + +function getCountryName(letter) + return (letter and countrys[letter] and countrys[letter]) or "Unkown" end \ No newline at end of file From 9756dd7db1ff3985001b504bf9c6c5cc630bd5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shadowsun=E2=84=A2?= <12494967+CattoGamer@users.noreply.github.com> Date: Sun, 4 Aug 2024 04:55:12 +0200 Subject: [PATCH 2/5] Add Options in Request Menu --- .../modules/scoreboard/controls/cl_htmlcontrols.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua b/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua index 74ddbd5..5c5b3ba 100644 --- a/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua +++ b/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua @@ -43,6 +43,17 @@ function PANEL:Init() self.Navigating = true end + self.OptionsButton = vgui.Create( "DImageButton", self ) + self.OptionsButton:SetSize( ButtonSize, ButtonSize ) + self.OptionsButton:SetMaterial( "icon32/tool.png" ) + self.OptionsButton:Dock( LEFT ) + self.OptionsButton:DockMargin( Spacing, Margins, Spacing, Margins ) + self.OptionsButton.DoClick = function() + local menu = DermaMenu() + menu:AddOption( "YouTube Instance Switcher", function() RunConsoleCommand( "cinema_invidious_switch" ) end ) + menu:Open() + end + self.RefreshButton = vgui.Create( "DImageButton", self ) self.RefreshButton:SetSize( ButtonSize, ButtonSize ) self.RefreshButton:SetMaterial( "gui/HTML/refresh" ) From 0676f1435eecdb1a542cce3b8181d8f6cac6f1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shadowsun=E2=84=A2?= <12494967+CattoGamer@users.noreply.github.com> Date: Sun, 4 Aug 2024 04:55:17 +0200 Subject: [PATCH 3/5] Update sh_youtube.lua --- .../modules/theater/services/sh_youtube.lua | 264 +++++++++++++----- 1 file changed, 197 insertions(+), 67 deletions(-) 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 9923734..e343e42 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 @@ -6,94 +6,83 @@ 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 +local hostname = "" function SERVICE:Match( url ) return url.host and url.host:match("youtu.?be[.com]?") end if (CLIENT) then + local THEATER_JS = [[ + var checkerInterval = setInterval(function() { + var player = document.getElementsByTagName("VIDEO")[0] + if (!!player) { + if (player.paused) {player.play();} + if (player.paused === false && player.readyState === 4) { + clearInterval(checkerInterval); + + window.cinema_controller = player; + player.style = "width:100%; height: 100%;"; + + exTheater.controllerReady(); + } + } + }, 50); + ]] + + local embedUrlParser = { + ["youtube"] = function( Video ) + return ("https://" .. hostname .. "/embed/%s?autoplay=1&thin_mode=1&controls=0&quality=auto&volume=1&t=%s"):format( + Video:Data(), + math.Round(CurTime() - Video:StartTime() + ) + ) + end, + ["youtubelive"] = function( Video ) + return ("https://" .. hostname .. "/embed/%s?autoplay=1&thin_mode=1&controls=0&quality=auto&volume=1"):format( + Video:Data() + ) + end + } function SERVICE:LoadProvider( Video, panel ) - local url = GetGlobal2String( "cinema_url", "" ) .. "youtube.html?v=%s" - panel:OpenURL( url:format( Video:Data() ) .. - (self.IsTimed and ("&t=%s"):format( - math.Round(CurTime() - Video:StartTime()) - ) or "") - ) - + panel:OpenURL( embedUrlParser[Video:Type()](Video) ) panel.OnDocumentReady = function(pnl) - self:LoadExFunctions(pnl) + self:LoadExFunctions( pnl ) + pnl:QueueJavascript(THEATER_JS) end + end function SERVICE:GetMetadata( data, callback ) + local url = ("https://%s/api/v1/videos/%s"):format(hostname, data) - http.Fetch(METADATA_URL:format(data), function(body, length, headers, code) + http.Fetch(url, function(body, length, headers, code) if not body or code ~= 200 then - callback({ err = ("Not expected response received from YouTube (Code: %d)"):format(code) }) + callback({ err = ("Not expected response received from API (Code: %d, Try diffrent Instance)"):format(code) }) return end - local status, metadata = pcall(ParseMetaDataFromHTML, body) - if not status then + local response = util.JSONToTable(body) + if not response then callback({ err = "Failed to parse MetaData from YouTube" }) return end - callback(metadata) + callback({ + title = response.title, + premium = response.premium, + lengthSeconds = response.lengthSeconds, + isListed = response.isListed, + liveNow = response.liveNow, + isUpcoming = response.isUpcoming, + isFamilyFriendly = response.isFamilyFriendly, + }) + end, function(error) - callback({ err = ("YouTube Error: %s"):format(error) }) + callback({ err = ("YouTube Error: %s, Try diffrent Instance"):format(error) }) end, {}) end end @@ -140,19 +129,31 @@ function SERVICE:GetVideoInfo( data, onSuccess, onFailure ) return onFailure(metadata.err) end + if not metadata.isListed then + return onFailure( "Service_EmbedDisabled" ) + end + + if metadata.premium then + return onFailure( "Service_PurchasableContent" ) + end + + if metadata.prisUpcomingemium then + return onFailure( "Service_StreamOffline" ) + end + local info = {} info.title = metadata.title info.thumbnail = ("https://img.youtube.com/vi/(%s)/hqdefault.jpg"):format(data) - if metadata.duration == 0 then + if (metadata.liveNow and metadata.lengthSeconds == 0) then info.type = "youtubelive" info.duration = 0 else - if metadata.familyfriendly == "18+" then + if not metadata.isFamilyFriendly then info.type = "youtubensfw" end - info.duration = metadata.duration + info.duration = metadata.lengthSeconds end if onSuccess then @@ -173,4 +174,133 @@ theater.RegisterService( "youtubelive", { Dependency = DEPENDENCY_COMPLETE, Hidden = true, LoadProvider = CLIENT and SERVICE.LoadProvider or function() end -} ) \ No newline at end of file +} ) + + +do + if not (CLIENT) then return end + + local instances, description = {}, "Invidious is a de-googled alternative to YouTube, it allows you to watch videos without ads and restrictions. It reduces the data sent to Google when watching videos." + local cInstance = CreateClientConVar("cinema_invidious_instance", "invidious.fdn.fr", true, false) + + hostname = cInstance:GetString() + + cvars.AddChangeCallback(cInstance:GetName(), function(convar, oldValue, newValue) + hostname = newValue + end, cInstance:GetName()) + + do -- Invidious Switcher menu + local function createButton(parent, pos, size, text, callback ) + local button = vgui.Create( "DButton", parent ) + button:SetPos( pos[1], pos[2] ) + button:SetSize( size[1], size[2] ) + button:SetText(text) + button:SizeToContents() + button.DoClick = callback + + return button + end + + local function switcher() + local Frame = vgui.Create( "DFrame" ) + Frame:SetTitle("(YouTube) Invidious Instance Switcher") + Frame:SetSize( 500, 500 ) + Frame:Center() + Frame:MakePopup() + + do -- Top Box + local SettingsBox = vgui.Create( "DPanel", Frame ) + SettingsBox:Dock(TOP) + SettingsBox:SetHeight(50) + SettingsBox:SetBackgroundColor(Color(255,255,255, 0)) + + local Description = vgui.Create( "RichText", SettingsBox ) + Description:Dock(FILL) + Description:SetText( description ) + end + + do -- Instance list + local InstanceList = vgui.Create( "DListView", Frame ) + InstanceList:Dock( FILL ) + InstanceList:SetMultiSelect( false ) + InstanceList:SetSortable( true ) + + InstanceList:AddColumn( "Instance" ) + InstanceList:AddColumn( "Users" ) + InstanceList:AddColumn( "Location" ) + InstanceList:AddColumn( "Health" ) + + function InstanceList:DoDoubleClick(lineID, line) + cInstance:SetString( line:GetColumnText(1) ) + + if theater.ActivePanel() then + theater.RefreshPanel(true) + end + end + + local lines = {} + for host,tbl in pairs(instances) do + if tbl["api"] then + lines[host] = InstanceList:AddLine(host, tbl["users"], tbl["region"], tbl["uptime"]) + end + end + + InstanceList:SortByColumn( 2, true ) -- Sort by Users count + + if IsValid(lines[hostname]) then + InstanceList:SelectItem( lines[hostname] ) + end + + end + + end + concommand.Add("cinema_invidious_switch", switcher, nil, "Switch the Invidious instance") + end + + do -- Instance fetcher & updater + local function fetchInstances() + local function onSuccess(body, length, headers, code) + if not body or code ~= 200 then return end + + local response = util.JSONToTable(body) + if not response then return end + + instances = {} -- Clear instance list + + for k,v in pairs(response) do + local name, tbl = v[1], v[2] + + if tbl.type ~= "https" then + continue + end + + local api = tbl["api"] + local region = tbl["region"] + local users = (tbl["stats"] and tbl["stats"]["usage"] and tbl["stats"]["usage"]["users"] and tbl["stats"]["usage"]["users"]["total"] or "-") + local uptime = (tbl["monitor"] and tbl["monitor"]["uptime"] and tbl["monitor"]["uptime"] or "-") + + instances[name] = { + api = api, + region = util.getCountryName(region), + users = users, + uptime = uptime, + } + + end + end + + local function onFailure(message) + print("[Invidious API]: " .. message) + end + + http.Fetch("https://api.invidious.io/instances.json?sort_by=type,users", onSuccess, onFailure, { + ["Accept-Encoding"] = "gzip, deflate", + ["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36", + }) + end + fetchInstances() + + if timer.Exists("Invidious.Update") then timer.Remove("Invidious.Update") end + timer.Create("Invidious.Update", 300, 0, fetchInstances) + end +end \ No newline at end of file From 2210308a328901e65ccf62e33173d49f2a965fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shadowsun=E2=84=A2?= <12494967+CattoGamer@users.noreply.github.com> Date: Sun, 4 Aug 2024 05:14:55 +0200 Subject: [PATCH 4/5] Move switcher to queue menu --- .../gamemode/modules/scoreboard/cl_queue.lua | 7 +++++++ .../modules/scoreboard/controls/cl_htmlcontrols.lua | 11 ----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/cl_queue.lua b/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/cl_queue.lua index 879d6b1..9f9ef73 100644 --- a/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/cl_queue.lua +++ b/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/cl_queue.lua @@ -60,6 +60,13 @@ function QUEUE:Init() end self.Options:AddItem(RefreshButton) + local InstanceButton = vgui.Create( "TheaterButton" ) + InstanceButton:SetText( "YouTube Instance Switcher" ) + InstanceButton.DoClick = function(self) + RunConsoleCommand( "cinema_invidious_switch" ) + end + self.Options:AddItem(InstanceButton) + end function QUEUE:AddVideo( vid ) diff --git a/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua b/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua index 5c5b3ba..74ddbd5 100644 --- a/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua +++ b/workshop/gamemodes/cinema_modded/gamemode/modules/scoreboard/controls/cl_htmlcontrols.lua @@ -43,17 +43,6 @@ function PANEL:Init() self.Navigating = true end - self.OptionsButton = vgui.Create( "DImageButton", self ) - self.OptionsButton:SetSize( ButtonSize, ButtonSize ) - self.OptionsButton:SetMaterial( "icon32/tool.png" ) - self.OptionsButton:Dock( LEFT ) - self.OptionsButton:DockMargin( Spacing, Margins, Spacing, Margins ) - self.OptionsButton.DoClick = function() - local menu = DermaMenu() - menu:AddOption( "YouTube Instance Switcher", function() RunConsoleCommand( "cinema_invidious_switch" ) end ) - menu:Open() - end - self.RefreshButton = vgui.Create( "DImageButton", self ) self.RefreshButton:SetSize( ButtonSize, ButtonSize ) self.RefreshButton:SetMaterial( "gui/HTML/refresh" ) From 454063d6fcf8f7f4d89e4f87b586befcf45520b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shadowsun=E2=84=A2?= <12494967+CattoGamer@users.noreply.github.com> Date: Sun, 4 Aug 2024 05:15:00 +0200 Subject: [PATCH 5/5] Update sh_youtube.lua --- .../gamemode/modules/theater/services/sh_youtube.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e343e42..fb96cf3 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 @@ -32,14 +32,14 @@ if (CLIENT) then local embedUrlParser = { ["youtube"] = function( Video ) - return ("https://" .. hostname .. "/embed/%s?autoplay=1&thin_mode=1&controls=0&quality=auto&volume=1&t=%s"):format( + return ("https://" .. hostname .. "/embed/%s?autoplay=0&thin_mode=1&controls=0&quality=auto&volume=1&t=%s"):format( Video:Data(), math.Round(CurTime() - Video:StartTime() ) ) end, ["youtubelive"] = function( Video ) - return ("https://" .. hostname .. "/embed/%s?autoplay=1&thin_mode=1&controls=0&quality=auto&volume=1"):format( + return ("https://" .. hostname .. "/embed/%s?autoplay=0&thin_mode=1&controls=0&quality=auto&volume=1"):format( Video:Data() ) end