-- видеоскрипт для сайта https://www.youtube.com (25/1/20)
-- открывает подобные ссылки (см. https://pastebin.com/raw/MWAe9hK1 )
-- ## Поиск видео ##
-- из меню 'Файл' - "Открыть URL" (Ctrl+N)
-- использовать префиксы: - (видео), -- (плейлисты), --- (каналы), -+ (прямые трансляции)
-- открывает подобные запросы: -Маша и Медведь, --мосфильм, --- Ariana Grande, -+ вселенная
-- ## Авторизация ##
-- файл формата "Netscape HTTP Cookie File" - cookies.txt поместить в папку 'work'
-- (см. https://addons.mozilla.org/en-US/firefox/addon/cookies-txt )
-- ##
-- предпочитаемый язык субтитров, установить в настройках (см. https://pastebin.com/raw/N8XraJpT )
-- ##
-- показать плейлист на OSD: Ctrl+M
-- ##
local debug = false -- false | true
-- ##
		if m_simpleTV.Control.ChangeAddress ~= 'No' then return end
		if not m_simpleTV.Control.CurrentAddress:match('^[%p%a%s]*https?://[%a%.]*youtu[%.combe]')
			and not m_simpleTV.Control.CurrentAddress:match('^https?://[w%.]*hooktube%.com')
			and not m_simpleTV.Control.CurrentAddress:match('^https?://[%a%.]*invidio[%a]*%.')
			and not m_simpleTV.Control.CurrentAddress:match('^[%p%a%s]*https?://y2u%.be')
			and not m_simpleTV.Control.CurrentAddress:match('^%s*%-')
		then
		 return
		end
	local urlAdr = m_simpleTV.Control.CurrentAddress
	local inAdr = urlAdr
	if debug then
		debug = os.clock()
	end
	m_simpleTV.OSD.ShowMessageT({text = '', showTime = 1000, id = 'channelName'})
	require 'lfs'
	m_simpleTV.Control.ChangeAddress = 'Yes'
	m_simpleTV.Control.CurrentAddress = ''
	if inAdr:match('https?://')
		and not (inAdr:match('isChPlst=true')
			or inAdr:match('isPlst=true')
			or inAdr:match('browse_ajax')
			or inAdr:match('isLogo=false')
			or inAdr:match('&restart'))
	then
		if not m_simpleTV.Common.isUTF8(inAdr) then
			inAdr = m_simpleTV.Common.multiByteToUTF8(inAdr)
		end
		inAdr = inAdr:gsub('^.-https?://', 'https://')
		inAdr = inAdr:gsub('[\'"%[%]%(%)]+.-$', '')
		inAdr = m_simpleTV.Common.fromPersentEncoding(inAdr)
		inAdr = inAdr:gsub('[\'"]+.-$', '')
		inAdr = inAdr:gsub('amp;', '')
		inAdr = inAdr:gsub('\\', '/')
		inAdr = inAdr:gsub('%$OPT:.-$', '')
		inAdr = inAdr:gsub('disable_polymer=%w+', '')
		inAdr = inAdr:gsub('flow=list', '')
		inAdr = inAdr:gsub('no_autoplay=%w+', '')
		inAdr = inAdr:gsub('start_radio=%d+', '')
		inAdr = inAdr:gsub('time_continue=', 't=')
		inAdr = inAdr:gsub('/videoseries', '/playlist')
		inAdr = inAdr:gsub('list_id=', 'list=')
		inAdr = inAdr:gsub('/feed%?', '?')
		inAdr = inAdr:gsub('//music%.', '//')
		inAdr = inAdr:gsub('//gaming%.', '//')
		inAdr = inAdr:gsub('/featured%?', '')
		inAdr = inAdr:gsub('&nohtml5=%w+', '')
		inAdr = inAdr:gsub('/tv%#/.-%?', '/watch?')
		inAdr = inAdr:gsub('&resume', '')
		inAdr = inAdr:gsub('&spf=%w+', '')
		inAdr = inAdr:gsub('/live%?.-$', '/live')
		inAdr = inAdr:gsub('%#t=', '&t=')
		inAdr = inAdr:gsub('&t=0s', '')
		inAdr = inAdr:gsub('&+', '&')
		inAdr = inAdr:gsub('%?+', '?')
		inAdr = inAdr:gsub('[&%?/]+$', '')
		inAdr = inAdr:gsub('%s+', '')
		inAdr = inAdr:gsub('/([%?=&])', '%1' .. '')
	end
	if not m_simpleTV.User then
		m_simpleTV.User = {}
	end
	if not m_simpleTV.User.YT then
		m_simpleTV.User.YT = {}
	end
	if not m_simpleTV.User.YT.logoDisk then
		local path = m_simpleTV.Common.GetMainPath(1) .. 'Channel/'
		local f = path .. 'logo/Icons/YT_logo.png'
		local size = lfs.attributes(f, 'size')
		if not size then
			lfs.mkdir(path)
			local pathL = path .. 'logo/'
			lfs.mkdir(pathL)
			local pathI = pathL .. 'Icons/'
			lfs.mkdir(pathI)
			local fhandle = io.open(f, 'w+b')
			if fhandle then
				local logo = '  '
				logo = decode64(logo)
				fhandle:write(logo)
				fhandle:close()
			else
				f = ''
			end
		end
		m_simpleTV.User.YT.logoDisk = f
	end
	if not (inAdr:match('/c/')
			or inAdr:match('/watch_videos')
			or inAdr:match('/shared%?')
			or inAdr:match('/channel/')
			or inAdr:match('/user/')
			or inAdr:match('isChPlst=true')
			or inAdr:match('isPlst=true')
			or inAdr:match('isLogo=false')
			or inAdr:match('browse_ajax')
			or inAdr:match('&restart'))
		or
			(inAdr:match('/videos') and not inAdr:match('&restart'))
		or
			(inAdr:match('/channel/') and inAdr:match('/live$'))
	then
		if m_simpleTV.Control.MainMode == 0 then
			m_simpleTV.Interface.SetBackground({BackColor = 0, PictFileName = m_simpleTV.User.YT.logoDisk, TypeBackColor = 0, UseLogo = 3, Once = 1})
		end
	elseif inAdr:match('isPlst=true') or inAdr:match('isLogo=false') then
		if m_simpleTV.Control.MainMode == 0 then
			m_simpleTV.Interface.SetBackground({BackColor = 0, PictFileName = '', TypeBackColor = 0, UseLogo = 0, Once = 1})
		end
		if not inAdr:match('list=RD') then
			inAdr = inAdr:gsub('&isLogo=false', '')
		end
	end
	local userAgent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36'
	local session = m_simpleTV.Http.New(userAgent)
		if not session then return end
	m_simpleTV.Http.SetTimeout(session, 12000)
	local debug_0
	local plstId
	local plstIndex
	local selectPos
	local isWin_epgInfo
	local isVideo = true
	local isPlst = false
	local isChPlst = false
	local isChVideos = false
	local isPanel = true
	local i_panel = m_simpleTV.Config.GetValue('mainOsd/showTimeInfoPanel', 'simpleTVConfig')
	if i_panel and tostring(i_panel) == '0' then
		isPanel = false
	end
	if isPanel == true then
		local win_epgInfo = m_simpleTV.Config.GetValue('mainOsd/showEpgInfoAsWindow', 'simpleTVConfig')
		local win_epgInfoText = m_simpleTV.Config.GetValue('mainOsd/showEpgInfoTextSelectable', 'simpleTVConfig')
		if win_epgInfo and tostring(win_epgInfo) == 'true'
			and win_epgInfoText and tostring(win_epgInfoText) == 'true'
		then
			isWin_epgInfo = true
		end
	end
	local videoId = inAdr:match('[%?&/]v[=/](.+)')
				or inAdr:match('/embed/(.+)')
				or inAdr:match('/watch/(.+)')
				or inAdr:match('y[2out]*u%.be/(.+)')
				or ''
	videoId = videoId:sub(1, 11)
	if not m_simpleTV.User.YT.Lng then
		m_simpleTV.User.YT.Lng = {}
		if m_simpleTV.Interface.GetLanguage() == 'ru' then
			m_simpleTV.User.YT.Lng.desc = 'описание'
			m_simpleTV.User.YT.Lng.qlty = 'качество'
			m_simpleTV.User.YT.Lng.savePlstFolder = 'сохраненые плейлисты'
			m_simpleTV.User.YT.Lng.savePlst_1 = 'плейлист сохранен в файл'
			m_simpleTV.User.YT.Lng.savePlst_2 = 'в папку'
			m_simpleTV.User.YT.Lng.savePlst_3 = 'невозможно сохранить плейлист'
			m_simpleTV.User.YT.Lng.sub = 'субтитры'
			m_simpleTV.User.YT.Lng.subTr = 'перевод'
			m_simpleTV.User.YT.Lng.preview = 'предосмотр'
			m_simpleTV.User.YT.Lng.audio = 'аудио'
			m_simpleTV.User.YT.Lng.noAudio = 'нет аудио'
			m_simpleTV.User.YT.Lng.plst = 'плейлист'
			m_simpleTV.User.YT.Lng.error = 'ошибка'
			m_simpleTV.User.YT.Lng.live = 'прямая трансляция'
			m_simpleTV.User.YT.Lng.upLoadOnCh = 'загружено на канал'
			m_simpleTV.User.YT.Lng.loading = 'загрузка'
			m_simpleTV.User.YT.Lng.videoNotAvail = 'видео не доступно'
			m_simpleTV.User.YT.Lng.videoNotExst = 'видео не существует'
			m_simpleTV.User.YT.Lng.adaptive = '▫ адаптивное'
			m_simpleTV.User.YT.Lng.page = 'стр.'
			m_simpleTV.User.YT.Lng.hl = 'ru_RU'
			m_simpleTV.User.YT.Lng.hl_sub = 'ru'
			m_simpleTV.User.YT.Lng.camera = 'вид с видеокамеры'
			m_simpleTV.User.YT.Lng.camera_plst_title = 'список видеокамер'
			m_simpleTV.User.YT.Lng.channel = 'канал'
			m_simpleTV.User.YT.Lng.video = 'видео'
			m_simpleTV.User.YT.Lng.search = 'поиск'
			m_simpleTV.User.YT.Lng.notFound = 'не найдено'
			m_simpleTV.User.YT.Lng.started = 'начало в'
			m_simpleTV.User.YT.Lng.published = 'опубликовано'
			m_simpleTV.User.YT.Lng.duration = 'продолжительность'
			m_simpleTV.User.YT.Lng.relatedVideos = 'похожие видео'
			m_simpleTV.User.YT.Lng.chapter = 'часть'
			m_simpleTV.User.YT.Lng.link = 'ссылка'
		elseif m_simpleTV.Interface.GetLanguage() == 'pt' then
			m_simpleTV.User.YT.Lng.desc = 'descrição'
			m_simpleTV.User.YT.Lng.qlty = 'qualidade'
			m_simpleTV.User.YT.Lng.savePlstFolder = 'playlists salvas'
			m_simpleTV.User.YT.Lng.savePlst_1 = 'lista de reprodução salva em arquivo'
			m_simpleTV.User.YT.Lng.savePlst_2 = 'para pasta'
			m_simpleTV.User.YT.Lng.savePlst_3 = 'não é possível salvar a playlist'
			m_simpleTV.User.YT.Lng.sub = 'legendas'
			m_simpleTV.User.YT.Lng.subTr = 'traduzido'
			m_simpleTV.User.YT.Lng.preview = 'preview'
			m_simpleTV.User.YT.Lng.audio = 'áudio'
			m_simpleTV.User.YT.Lng.noAudio = 'sem áudio'
			m_simpleTV.User.YT.Lng.plst = 'lista de reprodução'
			m_simpleTV.User.YT.Lng.error = 'erro'
			m_simpleTV.User.YT.Lng.live = 'em direto'
			m_simpleTV.User.YT.Lng.upLoadOnCh = 'uploads do canal'
			m_simpleTV.User.YT.Lng.loading = 'a carregar'
			m_simpleTV.User.YT.Lng.videoNotAvail = 'vídeo não disponível'
			m_simpleTV.User.YT.Lng.videoNotExst = 'vídeo não existe'
			m_simpleTV.User.YT.Lng.adaptive = '▫ ádaptativo'
			m_simpleTV.User.YT.Lng.page = 'página'
			m_simpleTV.User.YT.Lng.hl = 'pt_PT'
			m_simpleTV.User.YT.Lng.hl_sub = 'pt'
			m_simpleTV.User.YT.Lng.camera = 'visão da câmera'
			m_simpleTV.User.YT.Lng.camera_plst_title = 'álternar câmera'
			m_simpleTV.User.YT.Lng.channel = 'chanel'
			m_simpleTV.User.YT.Lng.video = 'vídeo'
			m_simpleTV.User.YT.Lng.search = 'procurar'
			m_simpleTV.User.YT.Lng.notFound = 'não encontrado'
			m_simpleTV.User.YT.Lng.started = 'started'
			m_simpleTV.User.YT.Lng.published = 'published'
			m_simpleTV.User.YT.Lng.duration = 'duration'
			m_simpleTV.User.YT.Lng.relatedVideos = 'vídeos relacionados'
			m_simpleTV.User.YT.Lng.chapter = 'chapter'
			m_simpleTV.User.YT.Lng.link = 'link'
		elseif m_simpleTV.Interface.GetLanguage() == 'vi' then
			m_simpleTV.User.YT.Lng.desc = 'Sự miêu tả'
			m_simpleTV.User.YT.Lng.qlty = 'Chất lượng'
			m_simpleTV.User.YT.Lng.savePlstFolder = 'Đã lưu danh sách phát'
			m_simpleTV.User.YT.Lng.savePlst_1 = 'Danh sách phát được lưu thành file'
			m_simpleTV.User.YT.Lng.savePlst_2 = 'vào thư mục'
			m_simpleTV.User.YT.Lng.savePlst_3 = 'Không thể lưu'
			m_simpleTV.User.YT.Lng.sub = 'Phụ đề'
			m_simpleTV.User.YT.Lng.subTr = 'Google dịch'
			m_simpleTV.User.YT.Lng.preview = 'Xem lại'
			m_simpleTV.User.YT.Lng.audio = 'Âm thanh'
			m_simpleTV.User.YT.Lng.noAudio = 'Không có âm thanh'
			m_simpleTV.User.YT.Lng.plst = 'Danh sách phát'
			m_simpleTV.User.YT.Lng.error = 'Lỗi'
			m_simpleTV.User.YT.Lng.live = 'Trực tiếp'
			m_simpleTV.User.YT.Lng.upLoadOnCh = 'Kênh'
			m_simpleTV.User.YT.Lng.loading = 'Đang tải'
			m_simpleTV.User.YT.Lng.videoNotAvail = 'Video không có sẵn'
			m_simpleTV.User.YT.Lng.videoNotExst = 'Video không tồn tại'
			m_simpleTV.User.YT.Lng.adaptive = '▫ Tự động'
			m_simpleTV.User.YT.Lng.page = 'Trang.'
			m_simpleTV.User.YT.Lng.hl = 'vi'
			m_simpleTV.User.YT.Lng.hl_sub = 'vi'
			m_simpleTV.User.YT.Lng.camera = 'Xem camera'
			m_simpleTV.User.YT.Lng.camera_plst_title = 'Đổi camera'
			m_simpleTV.User.YT.Lng.channel = 'Kênh'
			m_simpleTV.User.YT.Lng.video = 'Video'
			m_simpleTV.User.YT.Lng.search = 'Tìm kiếm'
			m_simpleTV.User.YT.Lng.notFound = 'Không tìm thấy'
			m_simpleTV.User.YT.Lng.started = 'Bắt đầu'
			m_simpleTV.User.YT.Lng.published = 'Xuất bản'
			m_simpleTV.User.YT.Lng.duration = 'Thời lượng'
			m_simpleTV.User.YT.Lng.relatedVideos = 'Video liên quan'
			m_simpleTV.User.YT.Lng.chapter = 'Chương'
			m_simpleTV.User.YT.Lng.link = 'link'
		else
			m_simpleTV.User.YT.Lng.desc = 'description'
			m_simpleTV.User.YT.Lng.qlty = 'quality'
			m_simpleTV.User.YT.Lng.savePlstFolder = 'saved playlists'
			m_simpleTV.User.YT.Lng.savePlst_1 = 'playlist saved to file'
			m_simpleTV.User.YT.Lng.savePlst_2 = 'to folder'
			m_simpleTV.User.YT.Lng.savePlst_3 = 'unable to save playlist'
			m_simpleTV.User.YT.Lng.sub = 'subtitles'
			m_simpleTV.User.YT.Lng.subTr = 'translated'
			m_simpleTV.User.YT.Lng.preview = 'preview'
			m_simpleTV.User.YT.Lng.audio = 'audio'
			m_simpleTV.User.YT.Lng.noAudio = 'no audio'
			m_simpleTV.User.YT.Lng.plst = 'playlist'
			m_simpleTV.User.YT.Lng.error = 'error'
			m_simpleTV.User.YT.Lng.live = 'live'
			m_simpleTV.User.YT.Lng.upLoadOnCh = 'uploads from channel'
			m_simpleTV.User.YT.Lng.loading = 'loading'
			m_simpleTV.User.YT.Lng.videoNotAvail = 'video not available'
			m_simpleTV.User.YT.Lng.videoNotExst = 'video does not exist'
			m_simpleTV.User.YT.Lng.adaptive = '▫ adaptive'
			m_simpleTV.User.YT.Lng.page = 'page'
			m_simpleTV.User.YT.Lng.hl = 'en_US'
			m_simpleTV.User.YT.Lng.hl_sub = 'en'
			m_simpleTV.User.YT.Lng.camera = 'camera view'
			m_simpleTV.User.YT.Lng.camera_plst_title = 'switch camera'
			m_simpleTV.User.YT.Lng.channel = 'channel'
			m_simpleTV.User.YT.Lng.video = 'video'
			m_simpleTV.User.YT.Lng.search = 'search'
			m_simpleTV.User.YT.Lng.notFound = 'not found'
			m_simpleTV.User.YT.Lng.started = 'started'
			m_simpleTV.User.YT.Lng.published = 'published'
			m_simpleTV.User.YT.Lng.duration = 'duration'
			m_simpleTV.User.YT.Lng.relatedVideos = 'related videos'
			m_simpleTV.User.YT.Lng.chapter = 'chapter'
			m_simpleTV.User.YT.Lng.link = 'link'
		end
	end
	if not m_simpleTV.User.YT.cookies then
			local function GetNetscapeFileFormat()
				local f = m_simpleTV.Common.GetMainPath(1) .. '/cookies.txt'
				local fhandle = io.open(f, 'r')
					if not fhandle then return end
				local t, i = {}, 1
				local yt_cooki_name, yt_cooki_val
					for line in fhandle:lines() do
						_, _, _, yt_cooki_name, yt_cooki_val = line:match('^[%#%a_]*%.youtube%.com%s+(%a+)%s+/%s+(%a+)%s+(%d+)%s+(.-)%s+(.-)$')
						if yt_cooki_name and yt_cooki_val then
							t[i] = {}
							t[i] = yt_cooki_name .. '=' .. yt_cooki_val
							i = i + 1
						end
					end
				fhandle:close()
					if i == 1 then return end
			 return table.concat(t, ';')
			end
		local cookies = GetNetscapeFileFormat()
		if cookies then
			local p = cookies:gsub('[\'"%s]+', ';') .. ';'
			local VISITOR_INFO1_LIVE = p:match('VISITOR_INFO1_LIVE=.-;') or ''
			local SID = p:match('SID=.-;') or ''
			local HSID = p:match('HSID=.-;') or ''
			local SSID = p:match('SSID=.-;') or ''
			local APISID = p:match('APISID=.-;') or ''
			local LOGIN_INFO = p:match('LOGIN_INFO=.-;') or ''
			local YSC = p:match('YSC=.-;') or ''
			cookies = VISITOR_INFO1_LIVE .. SID .. HSID .. SSID .. APISID .. LOGIN_INFO .. YSC
		else
			cookies = ''
		end
		m_simpleTV.User.YT.cookies = cookies .. 'PREF=hl=' .. m_simpleTV.User.YT.Lng.hl .. ';'
	end
	if not m_simpleTV.User.YT.ChPlst then
		m_simpleTV.User.YT.ChPlst = {}
	end
	if not m_simpleTV.User.YT.ChPlst.Urls then
		m_simpleTV.User.YT.ChPlst.Urls = {}
	end
	if not m_simpleTV.User.YT.Plst then
		m_simpleTV.User.YT.Plst = {}
	end
	if not m_simpleTV.User.YT.qlty then
		m_simpleTV.User.YT.qlty = tonumber(m_simpleTV.Config.GetValue('YT_qlty') or '1080')
	end
	if not m_simpleTV.User.YT.qlty_live then
		m_simpleTV.User.YT.qlty_live = tonumber(m_simpleTV.Config.GetValue('YT_qlty_live') or '1080')
	end
	if m_simpleTV.User.YT.isChPlst then
		m_simpleTV.User.YT.isChPlst = nil
	end
	local function lunajson_decode(json_, pos_, nullv_, arraylen_)
	-- The MIT License (MIT)
	-- Copyright (c) 2015-2017 Shunsuke Shimizu (grafi)
	-- https://github.com/grafi-tt/lunajson
		local setmetatable, tonumber, tostring = setmetatable, tonumber, tostring
		local floor, inf = math.floor, math.huge
		local mininteger, tointeger = math.mininteger or nil, math.tointeger or nil
		local byte, char, find, gsub, match, sub = string.byte, string.char, string.find, string.gsub, string.match, string.sub
		local function _decode_error(pos, errmsg)
			error("parse error at " .. pos .. ": " .. errmsg, 2)
		end
		local f_str_ctrl_pat
		if _VERSION == "Lua 5.1" then
			f_str_ctrl_pat = '[^\32-\255]'
		else
			f_str_ctrl_pat = '[\0-\31]'
		end
		local _ENV = nil
		local json, pos, nullv, arraylen, rec_depth
		local dispatcher, f
		local function decode_error(errmsg)
		 return _decode_error(pos, errmsg)
		end
		local function f_err()
			decode_error('invalid value')
		end
		local function f_nul()
			if sub(json, pos, pos+2) == 'ull' then
				pos = pos+3
			return nullv
			end
			decode_error('invalid value')
		end
		local function f_fls()
			if sub(json, pos, pos+3) == 'alse' then
				pos = pos+4
				return false
			end
			decode_error('invalid value')
		end
		local function f_tru()
			if sub(json, pos, pos+2) == 'rue' then
				pos = pos+3
				return true
			end
			decode_error('invalid value')
		end
		local radixmark = match(tostring(0.5), '[^0-9]')
		local fixedtonumber = tonumber
		if radixmark ~= '.' then
			if find(radixmark, '%W') then
				radixmark = '%' .. radixmark
			end
			fixedtonumber = function(s)
				return tonumber(gsub(s, '.', radixmark))
			end
		end
		local function number_error()
			return decode_error('invalid number')
		end
		local function f_zro(mns)
			local num, c = match(json, '^(%.?[0-9]*)([-+.A-Za-z]?)', pos)
			if num == '' then
				if c == '' then
					if mns then
						return -0.0
					end
					return 0
				end
				if c == 'e' or c == 'E' then
					num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos)
					if c == '' then
						pos = pos + #num
						if mns then
							return -0.0
						end
						return 0.0
					end
				end
				number_error()
			end
			if byte(num) ~= 0x2E or byte(num, -1) == 0x2E then
				number_error()
			end
			if c ~= '' then
				if c == 'e' or c == 'E' then
					num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos)
				end
				if c ~= '' then
					number_error()
				end
			end
			pos = pos + #num
			c = fixedtonumber(num)
			if mns then
				c = -c
			end
			return c
		end
		local function f_num(mns)
			pos = pos-1
			local num, c = match(json, '^([0-9]+%.?[0-9]*)([-+.A-Za-z]?)', pos)
			if byte(num, -1) == 0x2E then
				number_error()
			end
			if c ~= '' then
				if c ~= 'e' and c ~= 'E' then
					number_error()
				end
				num, c = match(json, '^([^eE]*[eE][-+]?[0-9]+)([-+.A-Za-z]?)', pos)
				if not num or c ~= '' then
					number_error()
				end
			end
			pos = pos + #num
			c = fixedtonumber(num)
			if mns then
				c = -c
				if c == mininteger and not find(num, '[^0-9]') then
					c = mininteger
				end
			end
			return c
		end
		local function f_mns()
			local c = byte(json, pos)
			if c then
				pos = pos+1
				if c > 0x30 then
					if c < 0x3A then
						return f_num(true)
					end
				else
					if c > 0x2F then
						return f_zro(true)
					end
				end
			end
			decode_error('invalid number')
		end
		local f_str_hextbl = {
			0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
			0x8, 0x9, inf, inf, inf, inf, inf, inf,
			inf, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, inf,
			inf, inf, inf, inf, inf, inf, inf, inf,
			inf, inf, inf, inf, inf, inf, inf, inf,
			inf, inf, inf, inf, inf, inf, inf, inf,
			inf, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF,
			__index = function()
				return inf
			end
		}
		setmetatable(f_str_hextbl, f_str_hextbl)
		local f_str_escapetbl = {
			['"'] = '"',
			['\\'] = '\\',
			['/'] = '/',
			['b'] = '\b',
			['f'] = '\f',
			['n'] = '\n',
			['r'] = '\r',
			['t'] = '\t',
			__index = function()
				decode_error("invalid escape sequence")
			end
		}
		setmetatable(f_str_escapetbl, f_str_escapetbl)
		local function surrogate_first_error()
			return decode_error("1st surrogate pair byte not continued by 2nd")
		end
		local f_str_surrogate_prev = 0
		local function f_str_subst(ch, ucode)
			if ch == 'u' then
				local c1, c2, c3, c4, rest = byte(ucode, 1, 5)
				ucode = f_str_hextbl[c1-47] * 0x1000 +
						f_str_hextbl[c2-47] * 0x100 +
						f_str_hextbl[c3-47] * 0x10 +
						f_str_hextbl[c4-47]
				if ucode ~= inf then
					if ucode < 0x80 then
						if rest then
							return char(ucode, rest)
						end
						return char(ucode)
					elseif ucode < 0x800 then
						c1 = floor(ucode / 0x40)
						c2 = ucode - c1 * 0x40
						c1 = c1 + 0xC0
						c2 = c2 + 0x80
						if rest then
							return char(c1, c2, rest)
						end
						return char(c1, c2)
					elseif ucode < 0xD800 or 0xE000 <= ucode then
						c1 = floor(ucode / 0x1000)
						ucode = ucode - c1 * 0x1000
						c2 = floor(ucode / 0x40)
						c3 = ucode - c2 * 0x40
						c1 = c1 + 0xE0
						c2 = c2 + 0x80
						c3 = c3 + 0x80
						if rest then
							return char(c1, c2, c3, rest)
						end
						return char(c1, c2, c3)
					elseif 0xD800 <= ucode and ucode < 0xDC00 then
						if f_str_surrogate_prev == 0 then
							f_str_surrogate_prev = ucode
							if not rest then
								return ''
							end
							surrogate_first_error()
						end
						f_str_surrogate_prev = 0
						surrogate_first_error()
					else
						if f_str_surrogate_prev ~= 0 then
							ucode = 0x10000 +
									(f_str_surrogate_prev - 0xD800) * 0x400 +
									(ucode - 0xDC00)
							f_str_surrogate_prev = 0
							c1 = floor(ucode / 0x40000)
							ucode = ucode - c1 * 0x40000
							c2 = floor(ucode / 0x1000)
							ucode = ucode - c2 * 0x1000
							c3 = floor(ucode / 0x40)
							c4 = ucode - c3 * 0x40
							c1 = c1 + 0xF0
							c2 = c2 + 0x80
							c3 = c3 + 0x80
							c4 = c4 + 0x80
							if rest then
								return char(c1, c2, c3, c4, rest)
							end
							return char(c1, c2, c3, c4)
						end
						decode_error("2nd surrogate pair byte appeared without 1st")
					end
				end
				decode_error("invalid unicode codepoint literal")
			end
			if f_str_surrogate_prev ~= 0 then
				f_str_surrogate_prev = 0
				surrogate_first_error()
			end
			return f_str_escapetbl[ch] .. ucode
		end
		local f_str_keycache = setmetatable({}, {__mode="v"})
		local function f_str(iskey)
			local newpos = pos
			local tmppos, c1, c2
			repeat
				newpos = find(json, '"', newpos, true)
				if not newpos then
					decode_error("unterminated string")
				end
				tmppos = newpos-1
				newpos = newpos+1
				c1, c2 = byte(json, tmppos-1, tmppos)
				if c2 == 0x5C and c1 == 0x5C then
					repeat
						tmppos = tmppos-2
						c1, c2 = byte(json, tmppos-1, tmppos)
					until c2 ~= 0x5C or c1 ~= 0x5C
					tmppos = newpos-2
				end
			until c2 ~= 0x5C
			local str = sub(json, pos, tmppos)
			pos = newpos
			if iskey then
				tmppos = f_str_keycache[str]
				if tmppos then
					return tmppos
				end
				tmppos = str
			end
			if find(str, f_str_ctrl_pat) then
				decode_error("unescaped control string")
			end
			if find(str, '\\', 1, true) then
				str = gsub(str, '\\(.)([^\\]?[^\\]?[^\\]?[^\\]?[^\\]?)', f_str_subst)
				if f_str_surrogate_prev ~= 0 then
					f_str_surrogate_prev = 0
					decode_error("1st surrogate pair byte not continued by 2nd")
				end
			end
			if iskey then
				f_str_keycache[tmppos] = str
			end
			return str
		end
		local function f_ary()
			rec_depth = rec_depth + 1
			if rec_depth > 1000 then
				decode_error('too deeply nested json (> 1000)')
			end
			local ary = {}
			pos = match(json, '^[ \n\r\t]*()', pos)
			local i = 0
			if byte(json, pos) == 0x5D then
				pos = pos+1
			else
				local newpos = pos
				repeat
					i = i+1
					f = dispatcher[byte(json,newpos)]
					pos = newpos+1
					ary[i] = f()
					newpos = match(json, '^[ \n\r\t]*,[ \n\r\t]*()', pos)
				until not newpos
				newpos = match(json, '^[ \n\r\t]*%]()', pos)
				if not newpos then
					decode_error("no closing bracket of an array")
				end
				pos = newpos
			end
			if arraylen then
				ary[0] = i
			end
			rec_depth = rec_depth - 1
			return ary
		end
		local function f_obj()
			rec_depth = rec_depth + 1
			if rec_depth > 1000 then
				decode_error('too deeply nested json (> 1000)')
			end
			local obj = {}
			pos = match(json, '^[ \n\r\t]*()', pos)
			if byte(json, pos) == 0x7D then
				pos = pos+1
			else
				local newpos = pos
				repeat
					if byte(json, newpos) ~= 0x22 then
						decode_error("not key")
					end
					pos = newpos+1
					local key = f_str(true)
					f = f_err
					local c1, c2, c3 = byte(json, pos, pos+3)
					if c1 == 0x3A then
						if c2 ~= 0x20 then
							f = dispatcher[c2]
							newpos = pos+2
						else
							f = dispatcher[c3]
							newpos = pos+3
						end
					end
					if f == f_err then
						newpos = match(json, '^[ \n\r\t]*:[ \n\r\t]*()', pos)
						if not newpos then
							decode_error("no colon after a key")
						end
						f = dispatcher[byte(json, newpos)]
						newpos = newpos+1
					end
					pos = newpos
					obj[key] = f()
					newpos = match(json, '^[ \n\r\t]*,[ \n\r\t]*()', pos)
				until not newpos
				newpos = match(json, '^[ \n\r\t]*}()', pos)
				if not newpos then
					decode_error("no closing bracket of an object")
				end
				pos = newpos
			end
			rec_depth = rec_depth - 1
			return obj
		end
		dispatcher = { [0] =
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_str, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_mns, f_err, f_err,
			f_zro, f_num, f_num, f_num, f_num, f_num, f_num, f_num,
			f_num, f_num, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_ary, f_err, f_err, f_err, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_fls, f_err,
			f_err, f_err, f_err, f_err, f_err, f_err, f_nul, f_err,
			f_err, f_err, f_err, f_err, f_tru, f_err, f_err, f_err,
			f_err, f_err, f_err, f_obj, f_err, f_err, f_err, f_err,
			__index = function()
				decode_error("unexpected termination")
			end
		}
		setmetatable(dispatcher, dispatcher)
		json, pos, nullv, arraylen = json_, pos_, nullv_, arraylen_
		rec_depth = 0
		pos = match(json, '^[ \n\r\t]*()', pos)
		f = dispatcher[byte(json, pos)]
		pos = pos+1
		local v = f()
		if pos_ then
		 return v, pos
		else
			f, pos = find(json, '^[ \n\r\t]*', pos)
			if pos ~= #json then
				-- decode_error('json ended')
			end
		 return v
		end
	 return decode
	end
	local function GetApiKey()
		local sessionGetApiKey = m_simpleTV.Http.New('Mozilla/5.0 (SMART-TV; Linux; Tizen 4.0.0.2) AppleWebkit/605.1.15 (KHTML, like Gecko) SamsungBrowser/9.2 TV Safari/605.1.15')
			if not sessionGetApiKey then return end
		m_simpleTV.Http.SetTimeout(sessionGetApiKey, 12000)
			local function ApiKey()
				local apiKeyHeader = 'Referer: https://www.youtube.com/tv/'
				local url = 'https://www.youtube.com/s/tv/html5/loader/live.js'
				m_simpleTV.Http.SetCookies(sessionGetApiKey, url, m_simpleTV.User.YT.cookies, '')
				local rc, answer = m_simpleTV.Http.Request(sessionGetApiKey, {url = url, headers = apiKeyHeader})
					if rc ~= 200 then return end
				local labels = answer:match('labels={\'default\':\'([^\']+)')
					if not labels then return end
				url = 'https://www.youtube.com/s/tv/html5/' .. labels .. '/app-prod.js'
				m_simpleTV.Http.SetCookies(sessionGetApiKey, url, m_simpleTV.User.YT.cookies, '')
				rc, answer = m_simpleTV.Http.Request(sessionGetApiKey, {url = url, headers = apiKeyHeader})
					if rc ~= 200 then return end
		 return answer:match('%?.%.getApiKey%(%):.-"([^"]+)'), apiKeyHeader
		end
		local apiKey, apiKeyHeader = ApiKey()
		if not apiKey then
			apiKey = decode64('QUl6YVN5Q05DZVgwbUpYWHZDSVNuaHBCS3pkc1hKSWVVc19KQmxJ')
			apiKeyHeader = 'Referer: ' .. decode64('aHR0cHM6Ly9uZXh0ZXJyLmZvci55b3V0dWJlLnNpbXBsZXR2')
		end
		m_simpleTV.Http.Close(sessionGetApiKey)
		m_simpleTV.User.YT.apiKey = apiKey
		m_simpleTV.User.YT.apiKeyHeader = apiKeyHeader
	end
	local function split_str(source, delimiters)
		local elements = {}
		local pattern
		if not delimiters or delimiters == '' then
			pattern = '.'
		else
			pattern = '([^' .. delimiters .. ']+)'
		end
		source:gsub(pattern, function(value) elements[#elements + 1] = value end)
	 return elements
	end
	local function table_reversa(t)
		local tbl = {}
		local p = #tbl
			for i = #t, 1, -1 do
				p = p + 1
				tbl[p] = t[i]
			end
	 return tbl
	end
	local function timeStamp(isodt)
		local pattern = '(%d+)%-(%d+)%-(%d+)T(%d+):(%d+)'
		local xyear, xmonth, xday, xhour, xminute = isodt:match(pattern)
			if not (xyear or xmonth or xday or xhour or xminute) then
				 return ''
				end
		local currenttime = os.time()
		local datetime = os.date('!*t', currenttime)
		datetime.isdst = true
		local offset = currenttime - os.time(datetime)
		local convertedTimestamp = os.time({year = xyear, month = xmonth, day = xday, hour = xhour, min = xminute})
	 return (convertedTimestamp + offset)
	end
	local function secondsToClock(sec)
			if not sec or tonumber(sec) < 3 then
			 return ''
			end
		sec = tonumber(sec)
		sec = string.format('%01d:%02d:%02d',
									math.floor(sec / 3600),
									math.floor(sec / 60) % 60,
									math.floor(sec % 60))
	 return sec:gsub('^0[0:]+(.+:)', '%1' .. '')
	end
	local function unescape_html(str)
		str = str:gsub('&#39;', '\'')
		str = str:gsub('&ndash;', '-')
		str = str:gsub('&#8217;', '\'')
		str = str:gsub('&raquo;', '"')
		str = str:gsub('&laquo;', '"')
		str = str:gsub('&lt;', '<')
		str = str:gsub('&gt;', '>')
		str = str:gsub('&quot;', '"')
		str = str:gsub('&apos;', '\'')
		str = str:gsub('&#(%d+);', function(n) return string.char(n) end)
		str = str:gsub('&#x(%d+);', function(n) return string.char(tonumber(n, 16)) end)
		str = str:gsub('&amp;', '&')
	 return str
	end
	local function clean_title(s)
		s = m_simpleTV.Common.fromPersentEncoding(s)
		s = s:gsub('\\u0026', '&')
		s = unescape_html(s)
	 return s
	end
	local function clean_desc(d)
		d = m_simpleTV.Common.fromPersentEncoding(d)
		d = d:gsub('\\u200f', '')
		d = d:gsub('\\u200e', '')
		d = d:gsub('\\r\\n', '\n')
		d = d:gsub('\\n\\n', '\n')
		d = d:gsub('\\n', '\n')
		d = d:gsub('\\r', '\n')
		d = unescape_html(d)
	 return d
	end
	local function ShowMessage(m)
		m_simpleTV.OSD.ShowMessageT({text = m, color = ARGB(255, 155, 155, 255), showTime = 1000 * 5, id = 'channelName'})
	end
	local function ShowInfo(info, bcolor, txtparm, color)
			local function datScr()
				local f = m_simpleTV.MainScriptDir .. 'user/video/!youtube.lua'
				local fhandle = io.open(f, 'r')
					if not fhandle then
					 return ''
					end
				local dat = fhandle:read(85)
				fhandle:close()
			 return dat:match('%(.+%)') or ''
			end
		m_simpleTV.Control.ExecuteAction(37)
		if not info then
				local function dumpInfo(o)
					if type(o) == 'table' then
						local s = '{ '
						for k, v in pairs(o) do
							if type(k) ~= 'number' then
								k = '"' .. k .. '"'
							end
							s = s .. '[' .. k .. '] = ' .. dumpInfo(v) .. ',' .. '\n'
						end
					 return s .. '} '
					else
					 return tostring(o)
					end
				end
				local function truncateUtf8(str, n)
						if m_simpleTV.Common.midUTF8 then
						 return m_simpleTV.Common.midUTF8(str, 0, n)
						end
					str = m_simpleTV.Common.UTF8ToUTF16(str)
					str = str:sub(1, n)
					str = m_simpleTV.Common.UTF16ToUTF8(str)
				 return str
				end
			color = ARGB(0xFF, 0x80, 0x80, 0xFF)
			bcolor = ARGB(0x90, 0, 0, 0)
			txtparm = 1 + 4
			local codec = ''
			local title
			if #m_simpleTV.User.YT.title > 70 then
				title = truncateUtf8(m_simpleTV.User.YT.title, 65) .. '...'
			else
				title = m_simpleTV.User.YT.title
			end
			local ti = m_simpleTV.Control.GetCodecInfo()
			if ti then
				local codecD, typeD, resD
				local t, i = {}, 1
					for w in dumpInfo(ti):gmatch('%[%d+%] =.-}') do
						t[i] = {}
						codecD = w:match('%["Codec"%] = (.-),\n')
						typeD = w:match('%["Type"%] = (.-),\n')
						if codecD and typeD then
							typeD = typeD:gsub('Video', m_simpleTV.User.YT.Lng.video .. ': ')
							typeD = typeD:gsub('Audio', m_simpleTV.User.YT.Lng.audio .. ': ')
							typeD = typeD:gsub('Subtitle', m_simpleTV.User.YT.Lng.sub .. ': ')
							codecD = typeD .. codecD
							codecD = '\n' .. codecD
						end
						resD = w:match('%["Video resolution"%] = (.-),\n') or w:match('%["Resolution"%] = (.-),\n')
						if resD then
							resD = m_simpleTV.User.YT.Lng.qlty .. ': ' .. resD
							resD = '\n' .. resD
						end
						t[i] = (codecD or '') .. (resD or '')
						i = i + 1
					end
				codec = table.concat(t)
			end
			local dur, publishedAt, author
			if m_simpleTV.User.YT.isLive == true then
				dur = ''
				author = m_simpleTV.User.YT.Lng.live .. ' | '
						.. m_simpleTV.User.YT.Lng.channel .. ': '
						.. m_simpleTV.User.YT.author
				local timeSt = timeStamp(m_simpleTV.User.YT.actualStartTime)
				timeSt = os.date('%y %d %m %H %M', tonumber(timeSt))
				local year, day, month, hour, min = timeSt:match('(%d+) (%d+) (%d+) (%d+) (%d+)')
				publishedAt = m_simpleTV.User.YT.Lng.started .. ': '
						.. string.format('%d:%02d (%d/%d/%02d)', hour, min, day, month, year)
			else
				dur = m_simpleTV.User.YT.Lng.duration .. ': ' .. secondsToClock(m_simpleTV.User.YT.duration)
				author = m_simpleTV.User.YT.Lng.upLoadOnCh .. ': ' .. m_simpleTV.User.YT.author
				local year, month, day = m_simpleTV.User.YT.publishedAt:match('(%d+)%-(%d+)%-(%d+)')
				year = year:sub(2, 4)
				publishedAt = m_simpleTV.User.YT.Lng.published .. ': '
						.. string.format('%d/%d/%02d', day, month, year)
			end
			info = title .. '\n'
					.. author .. '\n'
					.. publishedAt .. '\n'
					.. dur .. '\n'
					.. codec
			info = info:gsub('[%\n]+', '\n')
			info = info:gsub('%\n$', '')
		end
		local addElement = m_simpleTV.OSD.AddElement
		local removeElement = m_simpleTV.OSD.RemoveElement
		local q = {}
		q.once = 1
		q.zorder = 0
		q.cx = 0
		q.cy = 0
		q.id = 'YT_TEXT_INFO'
		q.class = 'TEXT'
		q.align = 0x0202
		q.top = 0
		q.color = color or ARGB(0xFF, 0xFF, 0xFF, 0xFF)
		q.font_italic = 0
		q.font_addheight = 6
		q.padding = 20
		q.textparam = txtparm or (1 + 4)
		q.text = info
		q.background = 0
		q.backcolor0 = bcolor or ARGB(0x90, 100, 0, 0)
		addElement(q)
		q = {}
		q.id = 'YT_DIV_CR'
		q.cx = 200
		q.cy = 200
		q.class = 'DIV'
		q.minresx = 800
		q.minresy = 600
		q.align = 0x0103
		q.left = 0
		q.once = 1
		q.zorder = 1
		q.background = -1
		addElement(q)
		q = {}
		q.id = 'YT_DIV_CR_TEXT'
		q.cx = 0
		q.cy = 0
		q.class = 'TEXT'
		q.minresx = 0
		q.minresy = 0
		q.align = 0x0103
		q.text = 'YouTube by nexterr ' .. datScr()
		q.color = ARGB(0x40, 211, 211, 211)
		q.font_height = -15
		q.font_weight = 700
		q.font_underline = 0
		q.font_italic = 0
		q.font_name = 'Arial'
		q.textparam = 0
		q.left = 5
		q.top = 5
		q.glow = 1
		q.glowcolor = ARGB(0x90, 0x00, 0x00, 0x00)
		addElement(q,'YT_DIV_CR')
			if m_simpleTV.Common.WaitUserInput(5000) == 1 then
				removeElement('YT_TEXT_INFO')
				removeElement('YT_DIV_CR')
			 return
			end
			if m_simpleTV.Common.WaitUserInput(5000) == 1 then
				removeElement('YT_TEXT_INFO')
				removeElement('YT_DIV_CR')
			 return
			end
		removeElement('YT_TEXT_INFO')
		removeElement('YT_DIV_CR')
	end
	local function StopOnErr(e, t)
		m_simpleTV.Control.CurrentAddress = m_simpleTV.User.YT.logoDisk .. '$OPT:video-filter=adjust$OPT:saturation=0$OPT:video-filter=gaussianblur$OPT:image-duration=5'
		if session then
			m_simpleTV.Http.Close(session)
		end
		local err = '❗️ YouTube ' .. m_simpleTV.User.YT.Lng.error .. ' [' .. e .. ']\n' .. (t or '')
		m_simpleTV.Control.SetTitle(err)
		ShowMessage(err)
	end
	local function Search(sAdr)
		local types, yt, header, stopSearch, url
		local eventType = ''
		if sAdr:match('^%s*%-%s*%-%s*%-') then
			types = 'channel'
			header = m_simpleTV.User.YT.Lng.channel
			yt = 'channel/'
			stopSearch = 90
		elseif sAdr:match('^%s*%-%s*%-') then
			types = 'playlist'
			header = m_simpleTV.User.YT.Lng.plst
			yt = 'playlist?list='
			stopSearch = 120
		elseif sAdr:match('^%s*%-%s*%+') then
			eventType = '&eventType=live'
			types = 'video'
			header = m_simpleTV.User.YT.Lng.live
			yt = 'watch?v='
			stopSearch = 60
		elseif sAdr:match('^%-related=') then
			sAdr = sAdr:gsub('%-related=', '')
			types = 'related'
			header = m_simpleTV.User.YT.Lng.relatedVideos
			yt = 'watch?v='
			stopSearch = 90
		else
			types = 'video&videoDimension=2d'
			header = m_simpleTV.User.YT.Lng.video
			yt = 'watch?v='
			stopSearch = 50
		end
			if types == 'video&videoDimension=2d' then
				sAdr = sAdr:gsub('^[%-%+%s]+(.-)%s*$', '%1')
				sAdr = m_simpleTV.Common.multiByteToUTF8(sAdr)
				sAdr = m_simpleTV.Common.toPersentEncoding(sAdr)
				local k, i = 1, 1
				local tab, name, dur, desc, length_seconds, desc0, panelDescName
				local t = {}
					for j = 1, 10 do
							if k > stopSearch then break end
						url = 'https://youtube.com/search_ajax?style=json&search_query=' .. sAdr .. '&page=' .. j .. '&hl=' .. m_simpleTV.User.YT.Lng.hl
						local rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
							if rc ~= 200 then break end
						tab = lunajson_decode(answer)
							if not tab then return end
						i = 1
							while true do
									if not tab.video[i] then break end
								length_seconds = tonumber(tab.video[i].length_seconds or '0')
								if length_seconds > 0 then
									name = clean_title(tab.video[i].title)
									dur = tab.video[i].duration
									t[k] = {}
									t[k].Id = k
									t[k].Address = 'https://www.youtube.com/' .. yt .. tab.video[i].encrypted_id
									t[k].Name = name .. ' (' .. dur .. ')'
									if isPanel == true then
										t[k].InfoPanelLogo = 'https://i.ytimg.com/vi/' .. tab.video[i].encrypted_id .. '/default.jpg'
										t[k].InfoPanelName = name
										t[k].InfoPanelShowTime = 10000
										if isWin_epgInfo == true then
											desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
													.. ':\n' .. t[k].Address
										else
											desc0 = nil
										end
										desc = tab.video[i].description
										panelDescName = nil
										if desc and desc ~= '' then
											panelDescName = m_simpleTV.User.YT.Lng.desc .. ' | '
											if desc0 then
												desc0 = desc0 .. '\n'
											else
												desc0 = ''
											end
											desc0 = desc0 .. '💢 '
													.. m_simpleTV.User.YT.Lng.desc .. ':\n'
													.. clean_desc(desc)
										elseif desc0 then
											panelDescName = ' | '
										end
										t[k].InfoPanelDesc = desc0
										t[k].InfoPanelTitle = (panelDescName or '')
															.. m_simpleTV.User.YT.Lng.channel
															.. ': ' .. clean_title(tab.video[i].author)
															.. ' | ' .. dur
									end
									k = k + 1
								end
								i = i + 1
							end
						j = j + 1
					end
					if k == 1 then return end
			 return t, types, header
			end
		if not m_simpleTV.User.YT.apiKey then
			GetApiKey()
		end
		if types == 'related' then
			url = 'https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=50&fields=nextPageToken,items/snippet/title,items/id/videoId,items/snippet/thumbnails/default/url,items/snippet/description,items/snippet/liveBroadcastContent,items/snippet/channelTitle&type=video&relatedToVideoId=' .. sAdr .. '&key=' .. m_simpleTV.User.YT.apiKey .. '&relevanceLanguage=' .. m_simpleTV.User.YT.Lng.hl
		else
			sAdr = sAdr:gsub('^[%-%+%s]+(.-)%s*$', '%1')
			sAdr = m_simpleTV.Common.multiByteToUTF8(sAdr)
			sAdr = m_simpleTV.Common.toPersentEncoding(sAdr)
			url = 'https://www.googleapis.com/youtube/v3/search?part=snippet&q=' .. sAdr .. '&type=' .. types .. '&fields=nextPageToken,items/id,items/snippet/title,items/snippet/thumbnails/default/url,items/snippet/description,items/snippet/liveBroadcastContent,items/snippet/channelTitle&maxResults=50' .. eventType .. '&key=' .. m_simpleTV.User.YT.apiKey .. '&relevanceLanguage=' .. m_simpleTV.User.YT.Lng.hl
		end
		local rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
			if rc ~= 200 then return end
		local tab = lunajson_decode(answer)
			if not tab then return end
		local t = {}
		local k, i = 1, 1
		local name, desc0, desc, panelDescName
			while true do
					if not tab.items[i] or k > stopSearch then break end
				if eventType == '&eventType=live'
					or (eventType == '' and tab.items[i].snippet.liveBroadcastContent ~= 'live')
				then
					name = clean_title(tab.items[i].snippet.title)
					t[k] = {}
					t[k].Id = k
					t[k].Name = name
					t[k].Address = 'https://www.youtube.com/' .. yt .. (tab.items[i].id.videoId or tab.items[i].id.playlistId or tab.items[i].id.channelId)
					if isPanel == true then
						if tab.items[i].snippet
							and tab.items[i].snippet.thumbnails
							and tab.items[i].snippet.thumbnails.default
							and tab.items[i].snippet.thumbnails.default.url
						then
							t[k].InfoPanelLogo = tab.items[i].snippet.thumbnails.default.url
						else
							t[k].InfoPanelLogo = m_simpleTV.User.YT.logoDisk
						end
						t[k].InfoPanelName = name
						t[k].InfoPanelShowTime = 10000
						if isWin_epgInfo == true then
							desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
									.. ':\n' .. t[k].Address
						else
							desc0 = nil
						end
						desc = tab.items[i].snippet.description
						panelDescName = nil
						if desc and desc ~= '' then
							panelDescName = m_simpleTV.User.YT.Lng.desc .. ' | '
							if desc0 then
								desc0 = desc0 .. '\n'
							else
								desc0 = ''
							end
							desc0 = desc0 .. '💢 '
									.. m_simpleTV.User.YT.Lng.desc .. ':\n'
									.. clean_desc(desc)
						elseif desc0 then
							panelDescName = ' | '
						end
						t[k].InfoPanelDesc = desc0
						if tab.items[i].snippet.channelTitle then
							t[k].InfoPanelTitle = (panelDescName or '')
												.. m_simpleTV.User.YT.Lng.channel
												.. ': '
												.. clean_title(tab.items[i].snippet.channelTitle)
						end
					end
					k = k + 1
				end
				i = i + 1
			end
			if k == 1 then return end
		local j, adrUrl, nextPageToken
			while true do
					if k > stopSearch then break end
				nextPageToken = answer:match('"nextPageToken": "([^"]+)')
					if not nextPageToken then break end
				adrUrl = url .. '&pageToken=' .. nextPageToken
				rc, answer = m_simpleTV.Http.Request(session, {url = adrUrl, headers = m_simpleTV.User.YT.apiKeyHeader})
					if rc ~= 200 then break end
					if not answer:match('"id"') then break end
				tab = lunajson_decode(answer)
					if not tab then break end
				j = 1
					while true do
							if not tab.items[j] or k > stopSearch then break end
						if eventType == '&eventType=live'
							or (eventType == '' and tab.items[j].snippet.liveBroadcastContent ~= 'live')
						then
							name = clean_title(tab.items[j].snippet.title)
							t[k] = {}
							t[k].Id = k
							t[k].Name = name
							t[k].Address = 'https://www.youtube.com/' .. yt .. (tab.items[j].id.videoId or tab.items[j].id.playlistId or tab.items[j].id.channelId)
							if isPanel == true then
								if tab.items[j].snippet
									and tab.items[j].snippet.thumbnails
									and tab.items[j].snippet.thumbnails.default
									and tab.items[j].snippet.thumbnails.default.url
								then
									t[k].InfoPanelLogo = tab.items[j].snippet.thumbnails.default.url
								else
									t[k].InfoPanelLogo = m_simpleTV.User.YT.logoDisk
								end
								t[k].InfoPanelName = name
								t[k].InfoPanelShowTime = 10000
								if isWin_epgInfo == true then
									desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
											.. ':\n' .. t[k].Address
								else
									desc0 = nil
								end
								desc = tab.items[j].snippet.description
								panelDescName = nil
								if desc and desc ~= '' then
									panelDescName = m_simpleTV.User.YT.Lng.desc .. ' | '
									if desc0 then
										desc0 = desc0 .. '\n'
									else
										desc0 = ''
									end
									desc0 = desc0 .. '💢 '
											.. m_simpleTV.User.YT.Lng.desc .. ':\n'
											.. clean_desc(desc)
								elseif desc0 then
									panelDescName = ' | '
								end
								t[k].InfoPanelDesc = desc0
								if tab.items[j].snippet.channelTitle then
									t[k].InfoPanelTitle = (panelDescName or '')
														.. m_simpleTV.User.YT.Lng.channel
														.. ': ' .. clean_title(tab.items[j].snippet.channelTitle)
								end
							end
							k = k + 1
						end
						j = j + 1
					end
			end
	 return t, types, header
	end
	local function GetUrlWatchVideos(url)
		local session = m_simpleTV.Http.New(userAgent, nil, true)
			if not session then return end
		m_simpleTV.Http.SetTimeout(session, 12000)
		m_simpleTV.Http.SetRedirectAllow(session, false)
		m_simpleTV.Http.SetCookies(session, url, m_simpleTV.User.YT.cookies, '')
		local rc = m_simpleTV.Http.Request(session, {url = url})
		local raw = m_simpleTV.Http.GetRawHeader(session)
		m_simpleTV.Http.Close(session)
			if not raw then return end
	 return raw:match('Location: (.-)\n')
	end
	local function Chapters()
		local lengthSeconds = tonumber(m_simpleTV.User.YT.duration)
			if lengthSeconds < 3600 * 5 then return end
		local seekpoint = 3600 * 2
		local chap = math.floor(lengthSeconds / seekpoint)
		local last_chap
		if lengthSeconds - (chap * seekpoint) < seekpoint / 2 then
			last_chap = 0
		else
			last_chap = 1
		end
		local chaptersT = {}
		chaptersT.chapters = {}
			for i = 1, chap + last_chap do
				chaptersT.chapters[i] = {}
				chaptersT.chapters[i].name = i .. ' ' .. m_simpleTV.User.YT.Lng.chapter
				chaptersT.chapters[i].seekpoint = seekpoint * 1000 * (i - 1)
			end
		m_simpleTV.Control.SetChaptersDesc(chaptersT)
	end
	local function Thumbs(storyboards)
		local currentAddress = m_simpleTV.User.YT.localAdrPlst or urlAdr
		m_simpleTV.User.YT.localAdrPlst = nil
			if m_simpleTV.Control.MainMode ~= 0 then return end
		m_simpleTV.User.YT.ThumbsInfo = nil
		local t = split_str(storyboards, '|')
			if not t or #t < 2 then return end
		local urlPattern = t[1]
			if urlPattern == '' then return end
		local q = split_str(t[#t], '#')
			if not q or #q < 8 then return end
		local samplingFrequency = tonumber(q[6]) or 0
		local thumbsPerImage = (tonumber(q[4]) or 0) * (tonumber(q[5]) or 0)
		local thumbWidth = tonumber(q[1]) or 0
		local thumbHeight = tonumber(q[2]) or 0
		local NPattern = q[7]
		urlPattern = urlPattern:gsub('$L', #t - 2)
		urlPattern = urlPattern .. '&sigh=' .. m_simpleTV.Common.toPersentEncoding(q[8])
			if samplingFrequency == 0
				or thumbsPerImage == 0
				or thumbWidth == 0
				or thumbHeight == 0
				or NPattern == nil
			then
			 return
			end
		m_simpleTV.User.YT.ThumbsInfo = {}
		m_simpleTV.User.YT.ThumbsInfo.currentAddress = currentAddress
		m_simpleTV.User.YT.ThumbsInfo.samplingFrequency = samplingFrequency
		m_simpleTV.User.YT.ThumbsInfo.thumbsPerImage = thumbsPerImage
		m_simpleTV.User.YT.ThumbsInfo.thumbWidth = thumbWidth
		m_simpleTV.User.YT.ThumbsInfo.thumbHeight = thumbHeight
		m_simpleTV.User.YT.ThumbsInfo.urlPattern = urlPattern
		m_simpleTV.User.YT.ThumbsInfo.NPattern = NPattern
		if not m_simpleTV.User.YT.PositionThumbsHandler then
			local handlerInfo = {}
			handlerInfo.luaFunction = 'PositionThumbs_YT'
			handlerInfo.regexString = '.*\.youtu.*|^\-.*'
			handlerInfo.sizeFactor = 0.17
			handlerInfo.backColor = ARGB(200, 0, 0, 0)
			handlerInfo.textColor = ARGB(230, 127, 255, 0)
			handlerInfo.glowParams = 'glow="7" samples="5" extent="4" color="0xB0000000"'
			handlerInfo.minImageWidth = 80
			handlerInfo.minImageHeight = 44
			m_simpleTV.User.YT.PositionThumbsHandler = m_simpleTV.PositionThumbs.AddHandler(handlerInfo)
		end
	end
	local function SetBackground(b)
		m_simpleTV.Interface.SetBackground({BackColor = 0, BackColorEnd = 255, PictFileName = (b or ''), TypeBackColor = 0, UseLogo = 3, Once = 1})
	end
	local function GetAdr(url, isCipher)
		if isCipher then
			url = m_simpleTV.Common.fromPersentEncoding(url)
			url = url:gsub('(.-)url=(.+)', '%2' ..'&' .. '%1')
			url = url:gsub('&s=', '$CIPHER=')
		end
	 return url
	end
	local function GetSignScr()
		local sessionGetSignScr = m_simpleTV.Http.New(userAgent)
			if not sessionGetSignScr then return end
		m_simpleTV.Http.SetTimeout(sessionGetSignScr, 8000)
		local adr = 'https://www.youtube.com/embed/' .. m_simpleTV.User.YT.vId
		m_simpleTV.Http.SetCookies(sessionGetSignScr, adr, m_simpleTV.User.YT.cookies, '')
		local rc, answer = m_simpleTV.Http.Request(sessionGetSignScr, {url = adr})
			if rc ~= 200 then
				m_simpleTV.Http.Close(sessionGetSignScr)
			 return
			end
		local url = answer:match('[^"\']+base%.js')
			if not url then
				m_simpleTV.Http.Close(sessionGetSignScr)
			 return
			end
		url = 'https://www.youtube.com' .. url
		m_simpleTV.Http.SetCookies(sessionGetSignScr, url, m_simpleTV.User.YT.cookies, '')
		rc, answer = m_simpleTV.Http.Request(sessionGetSignScr, {url = url, headers = 'Referer: ' .. adr})
		m_simpleTV.Http.Close(sessionGetSignScr)
			if rc ~= 200 then return end
		local l, obj = answer:match('%(a%){a=a%.split%(""%)((;..)%.[^%s]+)')
			if not l or not obj then return end
		local func, p
		local signScr, i = {}, 1
			for param in l:gmatch(obj .. '%.([^%)]+)') do
				func, p = param:match('(..)%(a,(%d+)')
				func = answer:match('[%p%s]' .. func .. ':function([^}]+)')
				if func:match('a%.reverse') then
					p = 0
				end
				if func:match('a%.splice') then
					p = '-' .. p
				end
				signScr[i] = tonumber(p)
				i = i + 1
			end
		m_simpleTV.User.YT.sts = answer:match('%)%);var f=(%d+)')
		m_simpleTV.User.YT.signScr = signScr
	end
	local function DeCipherSign(adr)
			local function table_swap(t, a)
					if a >= #t then return end
				local c = t[1]
				local p = (a % #t) + 1
				t[1] = t[p]
				t[p] = c
			 return t
			end
			local function table_slica(tbl, first, last, step)
				local sliced = {}
				local p = #sliced
					for i = first or 1, last or #tbl, step or 1 do
						p = p + 1
						sliced[p] = tbl[i]
					end
			 return sliced
			end
			local function sign_decode(s, signScr)
				local t = split_str(s)
					if #t == 0 or not signScr then
					 return s
					end
				local math_abs = math.abs
					for i = 1, #signScr do
						local a = signScr[i]
						if a == 0 then
							t = table_reversa(t)
						else
							if a > 0 then
								t = table_swap(t, a)
							else
								t = table_slica(t, math_abs(a) + 1)
							end
						end
					end
			 return table.concat(t)
			end
		for cipherSign in adr:gmatch('$CIPHER=([^&]*)') do
			local signature = sign_decode(cipherSign, m_simpleTV.User.YT.signScr)
			adr = adr:gsub('$CIPHER=[^&]*', '&sig=' .. signature, 1)
		end
	 return adr
	end
	local function GetQltyIndex(t)
		if (m_simpleTV.User.YT.qlty < 300
			and m_simpleTV.User.YT.qlty > 100)
		then
			m_simpleTV.User.YT.qlty = m_simpleTV.User.YT.qlty0
			or tonumber(m_simpleTV.Config.GetValue('YT_qlty') or '360')
		end
		local index
			for u = 1, #t do
					if t[u].qltyLive
						and m_simpleTV.User.YT.qlty_live < t[u].qltyLive
					then
					 return index or 1
					end
					if t[u].qlty
						and m_simpleTV.User.YT.qlty < t[u].qlty
					then
					 break
					end
				index = u
			end
		if index == 1
			and m_simpleTV.User.YT.qlty > 100
		then
			if #t > 1 then
				index = 2
			end
		end
	 return index or 1
	end
	local function GetStreamsTab(vId)
		m_simpleTV.Http.Close(session)
		m_simpleTV.User.YT.vId = vId
		m_simpleTV.User.YT.chId = ''
		m_simpleTV.User.YT.title = ''
		m_simpleTV.User.YT.publishedAt = ''
		m_simpleTV.User.YT.actualStartTime = ''
		m_simpleTV.User.YT.duration = nil
		m_simpleTV.User.YT.pic = nil
		m_simpleTV.User.YT.isLive = false
		m_simpleTV.User.YT.isLiveContent = false
		m_simpleTV.User.YT.isTrailer = false
		m_simpleTV.User.YT.desc = nil
		m_simpleTV.User.YT.isMusic = false
		local sTime = inAdr:match('[%?&]t=[^&]*')
		if sTime and videoId == m_simpleTV.User.YT.vId then
			local h = sTime:match('(%d+)h') or 0
			local m = sTime:match('(%d+)m') or 0
			local s = sTime:match('(%d+)s') or 0
			local d = sTime:match('(%d+)') or 0
			local st = (h * 3600) + (m * 60) + s
			if st ~= 0 then
				sTime = st
			else
				sTime = d
			end
			sTime = '$OPT:start-time=' .. sTime
		end
		local session = m_simpleTV.Http.New(userAgent)
			if not session then
			 return nil, 'GetStreamsTab session error 1'
			end
		m_simpleTV.Http.SetTimeout(session, 8000)
		if not m_simpleTV.User.YT.signScr then
			GetSignScr()
		end
		local referer = urlAdr:match('$OPT:http%-referrer=(.+)') or 'https://www.youtube.com'
		local url = 'https://www.youtube.com/get_video_info?eurl=' .. referer .. '&hl=' .. m_simpleTV.User.YT.Lng.hl .. '&sts=' .. (m_simpleTV.User.YT.sts or '') .. '&video_id='
		m_simpleTV.Http.SetCookies(session, url, m_simpleTV.User.YT.cookies, '')
		if debug then
			debug_0 = os.clock()
		end
		local rc, answer = m_simpleTV.Http.Request(session, {url = url .. m_simpleTV.User.YT.vId})
		if debug then
			debug_0 = string.format('%.3f', (os.clock() - debug_0))
		end
		answer = answer or ''
		local trailer = answer:match('trailerVideoId%%22%%3A%%22(.-)%%22')
		if trailer then
			m_simpleTV.User.YT.vId = trailer
			m_simpleTV.User.YT.isTrailer = true
			rc, answer = m_simpleTV.Http.Request(session, {url = url .. m_simpleTV.User.YT.vId})
			answer = answer or ''
		end
		if not answer:match('status%%22%%3A%%22OK') then
			if not m_simpleTV.User.YT.cookies:match('^PREF') then
				m_simpleTV.Http.Close(session)
				session = m_simpleTV.Http.New(userAgent)
					if not session then
					 return nil, 'GetStreamsTab session error 2'
					end
				m_simpleTV.Http.SetTimeout(session, 8000)
			end
			url = 'https://www.youtube.com/get_video_info?el=detailpage&eurl=' .. referer .. '&cco=1&video_id=' .. m_simpleTV.User.YT.vId .. '&hl=' .. m_simpleTV.User.YT.Lng.hl .. '&sts=' .. (m_simpleTV.User.YT.sts or '')
			m_simpleTV.Http.SetCookies(session, url, m_simpleTV.User.YT.cookies .. 'PREF=gl=US;', '')
			rc, answer = m_simpleTV.Http.Request(session, {url = url})
			answer = answer or ''
		end
		local answerJS = answer:match('player_response=([^&]*)')
			if not answerJS then
			 return nil, m_simpleTV.User.YT.Lng.videoNotExst
			end
		answerJS = answerJS:gsub('%+', ' ')
		answerJS = m_simpleTV.Common.fromPersentEncoding(answerJS)
		if debug then
			debug_in_file(answerJS, m_simpleTV.Common.GetMainPath(2) .. 'YouTube_answerJS.txt', true)
		end
		local tab = lunajson_decode(answerJS)
			if not tab then
			 return nil, 'GetStreamsTab Error 1'
			end
			if tab.multicamera
				and isVideo
				and tab.multicamera.playerLegacyMulticameraRenderer
				and tab.multicamera.playerLegacyMulticameraRenderer.metadataList
				and not inAdr:match('&restart')
				and not inAdr:match('&isPlst=true')
				and not inAdr:match('list=')
			then
				local t, i = {}, 1
				local metadataList = tab.multicamera.playerLegacyMulticameraRenderer.metadataList
				metadataList = m_simpleTV.Common.fromPersentEncoding(metadataList)
					for vId in metadataList:gmatch('/vi/([^/]+)') do
						t[i] = {}
						t[i] = vId
						i = i + 1
					end
					if i == 1 then
					 return nil, 'no list multicamers'
					end
				t = table.concat(t, ',')
				inAdr = 'https://www.youtube.com/watch_videos?video_ids=' .. t .. '&title=' .. m_simpleTV.User.YT.Lng.camera_plst_title:gsub('%s', '%+')
				inAdr = GetUrlWatchVideos(inAdr)
				m_simpleTV.Http.Close(session)
					if not inAdr then
					 return nil, 'not get adrs multicamers'
					end
				inAdr = inAdr .. '&isLogo=false'
			 return inAdr
			end
		if tab.videoDetails then
			if tab.videoDetails.author then
				m_simpleTV.User.YT.author = tab.videoDetails.author
			end
			if tab.videoDetails.channelId then
				m_simpleTV.User.YT.chId = tab.videoDetails.channelId
			end
			if tab.videoDetails.title then
				m_simpleTV.User.YT.title = tab.videoDetails.title
			end
			if tab.videoDetails.isLive == true then
				m_simpleTV.User.YT.isLive = true
			end
			if tab.videoDetails.isLiveContent == true then
				m_simpleTV.User.YT.isLiveContent = true
			end
			if tab.videoDetails.lengthSeconds and tonumber(tab.videoDetails.lengthSeconds) > 2 then
				m_simpleTV.User.YT.duration = tab.videoDetails.lengthSeconds
			end
			if tab.videoDetails.shortDescription then
				m_simpleTV.User.YT.desc = tab.videoDetails.shortDescription
			end
		end
		if tab.microformat
			and tab.microformat.playerMicroformatRenderer
		then
			if m_simpleTV.User.YT.isLive
				and tab.microformat.playerMicroformatRenderer.liveBroadcastDetails
				and tab.microformat.playerMicroformatRenderer.liveBroadcastDetails.startTimestamp
			then
				m_simpleTV.User.YT.actualStartTime = tab.microformat.playerMicroformatRenderer.liveBroadcastDetails.startTimestamp
			end
			if m_simpleTV.User.YT.duration == nil
				and tab.microformat.playerMicroformatRenderer.lengthSeconds
				and tonumber(tab.microformat.playerMicroformatRenderer.lengthSeconds) > 2
			then
				m_simpleTV.User.YT.duration = tab.microformat.playerMicroformatRenderer.lengthSeconds
			end
			if tab.microformat.playerMicroformatRenderer.publishDate then
				m_simpleTV.User.YT.publishedAt = tab.microformat.playerMicroformatRenderer.publishDate
			end
			if tab.microformat.playerMicroformatRenderer.thumbnail
				and tab.microformat.playerMicroformatRenderer.thumbnail.thumbnails
				and tab.microformat.playerMicroformatRenderer.thumbnail.thumbnails[1]
				and tab.microformat.playerMicroformatRenderer.thumbnail.thumbnails[1].url
			then
				m_simpleTV.User.YT.pic = tab.microformat.playerMicroformatRenderer.thumbnail.thumbnails[1].url
			end
			if tab.microformat.playerMicroformatRenderer.category == 'Music' then
				m_simpleTV.User.YT.isMusic = true
			end
		end
		local title = clean_title(m_simpleTV.User.YT.title)
		if tab.multicamera then
			title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.camera
		end
			if tab.streamingData and tab.streamingData.hlsManifestUrl
				and (tab.videoDetails.isLiveContent == true or tab.videoDetails.isLive == true)
			then
				local opt
				if m_simpleTV.Common.GetVlcVersion() > 3000 then
					opt = ''
				else
					opt = '$OPT:no-ts-trust-pcr'
				end
				rc, answer = m_simpleTV.Http.Request(session, {url = tab.streamingData.hlsManifestUrl})
				m_simpleTV.Http.Close(session)
					if rc ~= 200 then
					 return nil, 'GetStreamsTab live Error 1'
					end
				local t, i = {}, 1
					for name, fps, adr in answer:gmatch('RESOLUTION=(.-),.-RATE=(%d+).-\n(.-)\n') do
						name = tonumber(name:match('x(%d+)') or '0')
						if name > 240 then
							if tonumber(fps) > 30 then
								qlty = name + 6
								fps = ' ' .. fps .. ' FPS'
							else
								qlty = name
								fps = ''
							end
							t[i] = {}
							t[i].Id = i
							t[i].Name = name .. 'p' .. fps
							t[i].Address = adr .. opt
							t[i].qltyLive = qlty
							i = i + 1
						end
					end
					if i == 1 then
					 return nil, 'GetStreamsTab live Error 2'
					end
				if m_simpleTV.Common.GetVlcVersion() > 3000 then
					t[i] = {Id = i, Name = m_simpleTV.User.YT.Lng.adaptive, qltyLive = 5000, Address = url .. opt}
				end
				if tab.videoDetails.isLive == true then
					title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.live
				end
			 return t, title
			end
		local t, i = {}, 1
		if tab.streamingData and tab.streamingData.formats then
			local k = 1
			while true do
					if not tab.streamingData.formats[k] then break end
				t[i] = {}
				t[i].itag = tab.streamingData.formats[k].itag
				t[i].fps = tab.streamingData.formats[k].fps
				t[i].qlty = tab.streamingData.formats[k].height
				t[i].width = tab.streamingData.formats[k].width
				t[i].Address = tab.streamingData.formats[k].cipher or tab.streamingData.formats[k].url
				t[i].adaptive = false
				if tab.streamingData.formats[k].cipher then
					t[i].isCipher = true
				end
				i = i + 1
				k = k + 1
			end
		end
		if tab.streamingData and tab.streamingData.adaptiveFormats then
			local k = 1
			while true do
					if not tab.streamingData.adaptiveFormats[k] then break end
				if tab.streamingData.adaptiveFormats[k].contentLength then
					t[i] = {}
					t[i].itag = tab.streamingData.adaptiveFormats[k].itag
					t[i].qlty = tab.streamingData.adaptiveFormats[k].height
					t[i].width = tab.streamingData.adaptiveFormats[k].width
					t[i].fps = tab.streamingData.adaptiveFormats[k].fps
					t[i].Address = tab.streamingData.adaptiveFormats[k].cipher or tab.streamingData.adaptiveFormats[k].url
					t[i].adaptive = true
					if tab.streamingData.adaptiveFormats[k].cipher then
						t[i].isCipher = true
					end
					i = i + 1
				end
				k = k + 1
			end
		end
			if #t == 0 then
				local title_err, stream_tab_err
				if tab.playabilityStatus then
					if tab.playabilityStatus.status
						and tab.playabilityStatus.status == 'LOGIN_REQUIRED'
					then
						if m_simpleTV.Interface.GetLanguage() == 'ru' then
							title_err = 'ТРЕБУЕТСЯ ВХОД: используйте "cookies файл" для авторизации'
						elseif m_simpleTV.Interface.GetLanguage() == 'pt' then
							title_err = 'LOGIN NECESSÁRIO: usar "cookies file" para autorização'
						elseif m_simpleTV.Interface.GetLanguage() == 'vi' then
							title_err = 'YÊU CẦU ĐĂNG NHẬP: sử dụng "cookies file" để ủy quyền'
						else
							title_err = 'LOGIN REQUIRED: use "cookies file" for authorization'
						end
					elseif tab.playabilityStatus.errorScreen
						and tab.playabilityStatus.errorScreen.playerErrorMessageRenderer
						and tab.playabilityStatus.errorScreen.playerErrorMessageRenderer.subreason
						and tab.playabilityStatus.errorScreen.playerErrorMessageRenderer.subreason.runs
						and tab.playabilityStatus.errorScreen.playerErrorMessageRenderer.subreason.runs[1]
						then
							local t, i = {}, 1
								for i = 1, #tab.playabilityStatus.errorScreen.playerErrorMessageRenderer.subreason.runs do
									t[i] = {}
									t[i] = tab.playabilityStatus.errorScreen.playerErrorMessageRenderer.subreason.runs[i].text
									i = i + 1
								end
							title_err = table.concat(t)
					elseif tab.playabilityStatus.liveStreamability
						and tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer
						and tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate
						and tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate.liveStreamOfflineSlateRenderer
						and tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate.liveStreamOfflineSlateRenderer.mainText
						and tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate.liveStreamOfflineSlateRenderer.mainText.runs[1]
						then
							local t, i = {}, 1
								for i = 1, #tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate.liveStreamOfflineSlateRenderer.mainText.runs do
									t[i] = {}
									t[i] = tab.playabilityStatus.liveStreamability.liveStreamabilityRenderer.offlineSlate.liveStreamOfflineSlateRenderer.mainText.runs[i].text
									i = i + 1
								end
							title_err = table.concat(t)
					elseif tab.playabilityStatus.reason then
						title_err = tab.playabilityStatus.reason
					end
					if not title_err or title_err == '' then
						title_err = m_simpleTV.User.YT.Lng.videoNotAvail
					end
					title_err = title .. '\n⚠️ ' .. title_err
					if m_simpleTV.User.YT.pic then
						stream_tab_err = {{Name = '', Address = m_simpleTV.User.YT.pic .. '$OPT:NO-STIMESHIFT$OPT:image-duration=6'}}
					end
				end
				isPanel = false
				m_simpleTV.Http.Close(session)
			 return stream_tab_err, title_err
			end
		local captions, captions_title
		local audio_itags, video_itags, opt_2xx, opt_3xx, opt_3xx_demux_avcodec
		local audioAdr, audioItag, audioAdr_isCipher, audioAdrName, audioId
		if m_simpleTV.Common.GetVlcVersion() > 3000 then
				video_itags = {
							394, 17, 160, 278, -- 144
							395, 36, 133, 242, -- 240
							43, 18, 134, 243, -- 360
							135, 244, -- 480
							-- 22, -- 720 mp4
							136, 247, -- 720
							298, -- 720 (60 fps)
							-- 302, 334, -- 720 (60 fps, HDR)
							137, 248, -- 1080
							-- 299, 335, -- 1080 (60 fps, HDR)
							264, 271, 308, 336, -- 1440 (60 fps, HDR)
							266, 313, 315, 337, -- 2160 (60 fps, HDR)
							272, -- 4320 (60 fps)
							}
				audio_itags = {
							258, -- aac
							251, -- opus
							140, -- aac
							171, -- vorbis
							}
				opt_3xx = ''
			elseif m_simpleTV.Common.GetVlcVersion() == 2280 then
				video_itags = {
							17, 278, -- 144
							36, 242, -- 240
							43, 18, 243, -- 360
							244, -- 480
							-- 22, -- 720 mp4
							247, -- 720
							-- 302, 334, -- 720 (60 fps, HDR)
							248, -- 1080
							-- 335, -- 1080 (60 fps, HDR)
							271, 308, 336, -- 1440 (60 fps, HDR)
							313, 315, 337, -- 2160 (60 fps, HDR)
							272, -- 4320 (60 fps)
							}
				audio_itags = {171} -- vorbis
				opt_2xx = '$OPT:demux=avcodec'
			else
				video_itags = {
							17, -- 144
							36, -- 240
							43, 18, -- 360
							-- 22, -- 720 mp4
							136, -- 720 (dash)
							137, -- 1080 (dash)
							}
				audio_itags = {171} -- vorbis
				opt_2xx = '$OPT:demux=avcodec'
		end
		if tab.captions
			and tab.captions.playerCaptionsTracklistRenderer
			and tab.captions.playerCaptionsTracklistRenderer.captionTracks
		then
				local function Subtitle()
					local subt = {}
					local subtList = tostring(m_simpleTV.Config.GetValue('subtitle/lang', 'simpleTVConfig'))
					if subtList == 'nil'
								or subtList == 'none'
								or subtList == ''
					then
						subt[1] = m_simpleTV.User.YT.Lng.hl_sub
					else
						subtList = subtList:gsub('%s', ',')
						subtList = subtList:gsub('[^%d%a,%-_]', '')
						subtList = subtList:gsub('_', '-')
						subtList = subtList:gsub(',+', ',')
						subt = split_str(subtList, ',')
						if #subt == 0 then
							subt[1] = m_simpleTV.User.YT.Lng.hl_sub
						end
					end
					local r = 1
					local languageCode, kind, q, subtAdr
					while true do
							if not subt[r] or subtAdr then break end
						q = 1
						while true do
								if not tab.captions.playerCaptionsTracklistRenderer.captionTracks[q] then break end
							languageCode = tab.captions.playerCaptionsTracklistRenderer.captionTracks[q].languageCode
							kind = tab.captions.playerCaptionsTracklistRenderer.captionTracks[q].kind
								if languageCode
									and (not kind or kind ~= 'asr')
									and languageCode == subt[r]
								then
									subtAdr = '#' .. tab.captions.playerCaptionsTracklistRenderer.captionTracks[q].baseUrl .. '&fmt=vtt'
								 break
								end
							q = q + 1
						end
						r = r + 1
					end
						if subtAdr then
						 return subtAdr, ''
						end
						if not tab.captions.playerCaptionsTracklistRenderer.translationLanguages
							or not tab.captions.playerCaptionsTracklistRenderer.translationLanguages[1]
						then
						 return
						end
					r = 1
					local lngCodeTr
					while true do
							if not subt[r] or lngCodeTr then break end
						q = 1
						while true do
								if not tab.captions.playerCaptionsTracklistRenderer.translationLanguages[q] then break end
							languageCode = tab.captions.playerCaptionsTracklistRenderer.translationLanguages[q].languageCode
								if languageCode
									and languageCode == subt[r]
								then
									lngCodeTr = languageCode
								 break
								end
							q = q + 1
						end
						r = r + 1
					end
						if not lngCodeTr then return end
					r = 1
					while true do
							if not tab.captions.playerCaptionsTracklistRenderer.captionTracks[r] then break end
						languageCode = tab.captions.playerCaptionsTracklistRenderer.captionTracks[r].languageCode
						kind = tab.captions.playerCaptionsTracklistRenderer.captionTracks[r].kind
							if languageCode
								and (not kind or kind ~= 'asr')
								and languageCode ~= 'na'
							then
								subtAdr = '#' .. tab.captions.playerCaptionsTracklistRenderer.captionTracks[r].baseUrl .. '&tlang=' .. lngCodeTr .. '&fmt=vtt'
							 break
							end
						r = r + 1
					end
						if not subtAdr then return end
				 return subtAdr, ' (' .. m_simpleTV.User.YT.Lng.subTr .. ')'
				end
			captions, captions_title = Subtitle()
			if captions and m_simpleTV.Common.GetVlcVersion() < 3000 then
				captions = captions:gsub('://', '/subtitle://')
			end
		end
			for i = 1, #t do
				t[i].qlty = tonumber(t[i].qlty or '0')
				t[i].width = tonumber(t[i].width or '0')
				t[i].fps = tonumber(t[i].fps or '0')
				t[i].itag = tonumber(t[i].itag or '0')
				if (t[i].qlty > 340 and t[i].qlty < 500) and t[i].width > 640 then
					t[i].qlty = 480
				end
				if (t[i].qlty > 250 and t[i].qlty < 300) and t[i].width > 600 then
					t[i].qlty = 360
				end
				if (t[i].qlty > 760 and t[i].qlty < 1200) and t[i].width > 1600 then
					t[i].qlty = 1080
				end
				if t[i].qlty > 0 and t[i].qlty <= 180 then
					t[i].qlty = 144
				elseif t[i].qlty > 180 and t[i].qlty <= 300 then
					t[i].qlty = 240
				elseif t[i].qlty > 300 and t[i].qlty <= 400 then
					t[i].qlty = 360
				elseif t[i].qlty > 400 and t[i].qlty <= 500 then
					t[i].qlty = 480
				elseif t[i].qlty > 500 and t[i].qlty <= 780 then
					t[i].qlty = 720
				elseif t[i].qlty > 780 and t[i].qlty <= 1200 then
					t[i].qlty = 1080
				elseif t[i].qlty > 1200 and t[i].qlty <= 1500 then
					t[i].qlty = 1440
				elseif t[i].qlty > 1500 and t[i].qlty <= 2800 then
					t[i].qlty = 2160
				elseif t[i].qlty > 2800 and t[i].qlty <= 4500 then
					t[i].qlty = 4320
				end
				t[i].Name = t[i].qlty .. 'p'
				if t[i].fps > 30 then
					t[i].Name = t[i].Name .. ' ' .. t[i].fps .. ' FPS'
					if t[i].itag == 334
						or t[i].itag == 335
						or t[i].itag == 336
						or t[i].itag == 337
					then
						t[i].qlty = t[i].qlty + 7
						t[i].Name = t[i].Name .. ' HDR'
					else
						t[i].qlty = t[i].qlty + 6
					end
				end
			end
			for i = 1, #audio_itags do
				for z = 1, #t do
					if audio_itags[i] == t[z].itag then
						audioAdr = GetAdr(t[z].Address, t[z].isCipher)
						audioAdr_isCipher = t[z].isCipher
						if debug then
							audioItag = t[z].itag
						end
					 break
					end
				end
				if audioAdr then
				 break
				end
			end
		local sort_video_itags, u = {}, 1
			for i = 1, #video_itags do
				for z = 1, #t do
					if video_itags[i] == t[z].itag then
						sort_video_itags[u] = t[z]
						u = u + 1
					 break
					end
				end
			end
		if #sort_video_itags == 0 then
			sort_video_itags = t
		end
		local hash, sort_video = {}, {}
			for i = 1, #sort_video_itags do
				if not hash[sort_video_itags[i].Name] then
					u = #sort_video + 1
					sort_video[u] = sort_video_itags[i]
					hash[sort_video_itags[i].Name] = true
				end
			end
		t, u = {}, 1
		local opt = '$OPT:sub-track=0$OPT:NO-STIMESHIFT$OPT:input-slave='
			for _, v in pairs(sort_video) do
				if v.qlty > 300 then
					if v.adaptive == true and audioAdr then
						t[u] = v
						if m_simpleTV.Common.GetVlcVersion() == 2150 then
							t[u].Name = v.Name .. ' (dash)'
							t[u].qlty = v.qlty + 2
							t[u].Address = GetAdr(v.Address, v.isCipher)
										.. '$OPT:POSITIONTOCONTINUE=0'
										.. opt
										.. audioAdr
										.. (captions or '')
						else
							if v.qlty > 1080 and opt_3xx then
								opt_3xx_demux_avcodec = '$OPT:demux=avcodec,any'
							else
								opt_3xx_demux_avcodec = nil
							end
							t[u].Address = GetAdr(v.Address, v.isCipher)
										.. (sTime or '')
										.. (opt_3xx or '')
										.. (opt_3xx_demux_avcodec or '')
										.. opt
										.. audioAdr
										.. (captions or '')
						end
						u = u + 1
					end
					if v.adaptive == false then
						t[u] = v
						t[u].Address = GetAdr(v.Address, v.isCipher)
								.. (sTime or '')
								.. (opt_3xx or '')
								.. (opt_2xx or '')
								.. opt
								.. (captions or '')
						u = u + 1
					end
				end
			end
		if #t == 0 then
			for _, v in pairs(sort_video) do
				if v.adaptive == true and audioAdr then
					t[u] = v
					if m_simpleTV.Common.GetVlcVersion() == 2150 then
						t[u].Name = v.Name .. ' (dash)'
						t[u].qlty = v.qlty + 2
						t[u].Address = GetAdr(v.Address, v.isCipher)
									.. '$OPT:POSITIONTOCONTINUE=0'
									.. opt
									.. audioAdr
									.. (captions or '')
					else
						t[u].Address = GetAdr(v.Address, v.isCipher)
									.. (sTime or '')
									.. (opt_3xx or '')
									.. opt
									.. audioAdr
									.. (captions or '')
					end
					u = u + 1
				end
				if v.adaptive == false then
					t[u] = v
					t[u].Address = GetAdr(v.Address, v.isCipher)
							.. (sTime or '')
							.. (opt_3xx or '')
							.. (opt_2xx or '')
							.. opt
							.. (captions or '')
					u = u + 1
				end
			end
		end
			if #t == 0 then
				m_simpleTV.Http.Close(session)
			 return nil, 'GetStreamsTab Error 2'
			end
		if audioAdr then
			audioAdr = audioAdr .. (sTime or '') .. (opt_2xx or '') .. (opt_3xx or '') .. '$OPT:NO-STIMESHIFT'
			audioAdrName = '🔉 ' .. m_simpleTV.User.YT.Lng.audio
			audioId = 99
		else
			audioAdr = 'vlc://pause:5'
			audioAdrName = '🔇 ' .. m_simpleTV.User.YT.Lng.noAudio
			audioId = 10
		end
		t[u] = {Name = audioAdrName, qlty = audioId, Address = audioAdr, isCipher = audioAdr_isCipher}
		table.sort(t, function(a, b) return a.qlty < b.qlty end)
			for i = 1, #t do
				t[i].Id = i
			end
		if debug then
			t[1].audioItag = audioItag
		end
		if m_simpleTV.User.YT.qlty < 100 then
			if audioId == 99 then
				title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.audio
			else
				title = title .. '\n☐ ' .. m_simpleTV.User.YT.Lng.noAudio
			end
			local visual = tostring(m_simpleTV.Config.GetValue('vlc/audio/visual/module', 'simpleTVConfig'))
			if visual == 'nil'
				or visual == 'none'
				or visual == ''
			then
				SetBackground(m_simpleTV.User.YT.pic or m_simpleTV.User.YT.logoDisk)
			else
				SetBackground()
			end
		elseif captions_title then
			if tostring(m_simpleTV.Config.GetValue('subtitle/disableAtStart', 'simpleTVConfig')) == 'true' then
				title = title .. '\n☐ ' .. m_simpleTV.User.YT.Lng.sub .. captions_title
			else
				title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.sub .. captions_title
			end
		end
		if not m_simpleTV.User.YT.cookies:match('^PREF')
			and m_simpleTV.User.YT.isLive == false
			and m_simpleTV.User.YT.isLiveContent == false
			and m_simpleTV.User.YT.isTrailer == false
			and tab.playbackTracking
			and tab.playbackTracking.videostatsPlaybackUrl
			and tab.playbackTracking.videostatsPlaybackUrl.baseUrl
		then
			local cpn_alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_'
			local t = {}
			local math_random = math.random
			local cpn_l = #cpn_alphabet
				for i = 1, 16 do
					local random_d = math_random(1, cpn_l)
					t[i] = {}
					t[i] = cpn_alphabet:sub(random_d, random_d)
				end
			url = tab.playbackTracking.videostatsPlaybackUrl.baseUrl
				.. '&ver=2&fs=0&volume=100&muted=0&cpn='
				.. table.concat(t)
			m_simpleTV.Http.SetCookies(session, url, m_simpleTV.User.YT.cookies, '')
			m_simpleTV.Http.RequestA(session, {callback = 'MarkWatched_YT', url = url})
		else
			m_simpleTV.Http.Close(session)
		end
		if m_simpleTV.User.YT.duration then
			Chapters()
		end
		if tab.storyboards
			and tab.storyboards.playerStoryboardSpecRenderer
			and tab.storyboards.playerStoryboardSpecRenderer.spec
		then
			Thumbs(tab.storyboards.playerStoryboardSpecRenderer.spec)
		end
	 return t, title
	end
	local function AddInPl_Videos_YT(str, tab)
		local i = #tab + 1
		local ret = false
		local desc0, sep
		str = str:gsub('\\"', '%%22')
			for adr, name, times in str:gmatch('gridVideoRenderer.-"videoId":"(.-)".-"simpleText":"(.-)".-"accessibilityData".-"simpleText":"(.-)"') do
				name = clean_title(name)
				tab[i] = {}
				tab[i].Id = i
				tab[i].Address = 'https://www.youtube.com/watch?v=' .. adr .. '&isPlst=true'
				if isPanel == false then
					tab[i].Name = name .. ' (' .. times .. ')'
				else
					tab[i].Name = name
					tab[i].InfoPanelLogo = 'https://i.ytimg.com/vi/' .. adr .. '/default.jpg'
					tab[i].InfoPanelName = name
					if isWin_epgInfo == true then
						desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
								.. ':\nhttps://www.youtube.com/watch?v=' .. adr
						sep = ' | '
					else
						desc0 = nil
						sep = nil
					end
					tab[i].InfoPanelDesc = desc0
					tab[i].InfoPanelTitle = (sep or '') .. times
					tab[i].InfoPanelShowTime = 10000
				end
				i = i + 1
				ret = true
			end
	 return ret
	end
	local function AddInPl_Plst_YT(str, tab)
		local i = #tab + 1
		local ret = false
		local desc0, panelDescName
		str = str:gsub('\\"', '%%22')
			for name, desc, adr in str:gmatch('"title": "(.-)".-"description": "(.-)".-"videoId": "(.-)"') do
				if name ~= 'Deleted video' and name ~= 'Private video' then
					name = clean_title(name)
					tab[i] = {}
					tab[i].Id = i
					if adr == videoId and not selectPos then
						selectPos = i
					end
					tab[i].Address = 'https://www.youtube.com/watch?v=' .. adr .. '&isPlst=true'
					tab[i].Name = name
					if isPanel == true then
						tab[i].InfoPanelLogo = 'https://i.ytimg.com/vi/' .. adr .. '/default.jpg'
						tab[i].InfoPanelName = name
						if isWin_epgInfo == true then
							desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
									.. ':\nhttps://www.youtube.com/watch?v=' .. adr
						else
							desc0 = nil
						end
						panelDescName = nil
						if desc and desc ~= '' then
							panelDescName = m_simpleTV.User.YT.Lng.desc
							if desc0 then
								desc0 = desc0 .. '\n'
							else
								desc0 = ''
							end
							desc0 = desc0 .. '💢 '
									.. m_simpleTV.User.YT.Lng.desc .. ':\n'
									.. clean_desc(desc)
						end
						tab[i].InfoPanelDesc = desc0
						tab[i].InfoPanelTitle = (panelDescName or ' ')
						tab[i].InfoPanelShowTime = 10000
					end
					i = i + 1
					ret = true
				end
			end
	 return ret
	end
	function AsynPlsCallb_Videos_YT(session, rc, answer, userstring, params)
		answer = answer:gsub('\\"', '%%22')
		local ret = {}
			if rc ~= 200 then
				ret.Cancel = true
			 return ret
			end
		if params.User.First == true then
			params.User.First = false
			params.User.Title = answer:match('channelMetadataRenderer.-"title":"(.-)"') or '???'
			params.User.Title = clean_title(params.User.Title)
			m_simpleTV.Control.SetTitle(params.User.Title)
		end
			if not AddInPl_Videos_YT(answer, params.User.tab) then
				ret.Done = true
			 return ret
			end
		local nextContinuationData = answer:match('"nextContinuationData"(.-)$')
			if not nextContinuationData then
				ret.Done = true
			 return ret
			end
		local continuation, itct = nextContinuationData:match('"continuation":"(.-)".-"clickTrackingParams":"(.-)"')
			if not continuation or not itct then
				ret.Done = true
			 return ret
			end
		ret.request = {}
		ret.request.url = 'https://www.youtube.com/browse_ajax?ctoken=' .. continuation .. '&continuation=' .. continuation .. '&itct=' .. itct
		ret.request.headers = 'X-YouTube-Client-Name: 1\nX-YouTube-Client-Version: 2.20181220\nReferer: https://www.youtube.com/'
		ret.Count = #params.User.tab
		ret.Progress = ret.Count / params.User.plstTotalResults
	 return ret
	end
	function AsynPlsCallb_Plst_YT(session, rc, answer, userstring, params)
		local ret = {}
			if rc ~= 200 then
				params.User.rc = rc
				ret.Cancel = true
			 return ret
			end
			if not AddInPl_Plst_YT(answer, params.User.tab) then
				ret.Done = true
			 return ret
			end
		local nextPageToken = answer:match('"nextPageToken": "([^"]+)')
			if not nextPageToken then
				ret.Done = true
			 return ret
			end
		ret.request = {}
		ret.request.url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&fields=nextPageToken,items(snippet/title,snippet/resourceId/videoId,snippet/description)&playlistId=' .. plstId .. '&key=' .. m_simpleTV.User.YT.apiKey .. '&pageToken=' .. nextPageToken .. '&hl=' .. m_simpleTV.User.YT.Lng.hl
		ret.request.headers = m_simpleTV.User.YT.apiKeyHeader
		ret.Count = #params.User.tab
		ret.Progress = ret.Count / params.User.plstTotalResults
	 return ret
	end
	function PositionThumbs_YT(queryType, address, forTime)
		if queryType == 'testAddress' then
		 return false
		end
		if queryType == 'getThumbs' then
				if not m_simpleTV.User.YT.ThumbsInfo
					or m_simpleTV.User.YT.ThumbsInfo.currentAddress ~= address
					and not address:match('^%-')
				then
				 return true
				end
			local imgLen = m_simpleTV.User.YT.ThumbsInfo.samplingFrequency * m_simpleTV.User.YT.ThumbsInfo.thumbsPerImage
			local index = math.floor(forTime / imgLen)
			local t = {}
			t.playAddress = address
			local NPattern = m_simpleTV.User.YT.ThumbsInfo.NPattern:gsub('$M', index)
			t.url = m_simpleTV.User.YT.ThumbsInfo.urlPattern:gsub('$N', NPattern)
			t.httpParams = {}
			t.httpParams.userAgent = userAgent
			t.httpParams.extHeader = 'Referer: https://www.youtube.com/'
			t.elementWidth = m_simpleTV.User.YT.ThumbsInfo.thumbWidth
			t.elementHeight = m_simpleTV.User.YT.ThumbsInfo.thumbHeight
			t.startTime = index * imgLen
			t.length = imgLen
			m_simpleTV.PositionThumbs.AppendThumb(t)
		 return true
		end
	end
	function SavePlst_YT()
		if m_simpleTV.User.YT.Plst and m_simpleTV.User.YT.plstHeader then
			local t = m_simpleTV.User.YT.Plst
			local header = m_simpleTV.User.YT.plstHeader
			local adr, name, logo
			local m3ustr = '#EXTM3U $ExtFilter="YouTube" $BorpasFileFormat="1"\n'
				for i = 1, #t do
					name = t[i].Name
					logo = t[i].Address:match('v=([^&]*)') or ''
					adr = t[i].Address:gsub('&isPlst=true', '')
					m3ustr = m3ustr
							.. '#EXTINF:-1'
							.. ' group-title="' .. header .. '"'
							.. ' tvg-logo="https://i.ytimg.com/vi/' .. logo .. '/hqdefault.jpg"'
							.. ','
							.. name
							.. '\n' .. adr .. '\n'
				end
			if m_simpleTV.User.YT.ChTitleForSave then
				header = header .. ' [' .. m_simpleTV.User.YT.Lng.channel
						.. ' - ' .. m_simpleTV.User.YT.ChTitleForSave .. '] '
			end
			header = m_simpleTV.Common.UTF8ToMultiByte(header)
			header = header:gsub('%c', '')
			header = header:gsub('[\\/"%*:<>%|%?]+', ' ')
			header = header:gsub('%s+', ' ')
			header = header:gsub('^%s*(.-)%s*$', '%1')
			local fileEnd = ' (youtube ' .. os.date('%d.%m.%y') .. ').m3u8'
			local folder = m_simpleTV.Common.GetMainPath(2) .. m_simpleTV.Common.UTF8ToMultiByte(m_simpleTV.User.YT.Lng.savePlstFolder) .. '/'
			lfs.mkdir(folder)
			local folderYT = folder .. 'YouTube/'
			lfs.mkdir(folderYT)
			local filePath = folderYT .. header .. fileEnd
			local fhandle = io.open(filePath, 'w+')
			if fhandle then
				fhandle:write(m3ustr)
				fhandle:close()
				ShowInfo(
							m_simpleTV.User.YT.Lng.savePlst_1 .. '\n'
							.. m_simpleTV.Common.multiByteToUTF8(header) .. '\n'
							.. m_simpleTV.User.YT.Lng.savePlst_2 .. '\n'
							.. m_simpleTV.Common.multiByteToUTF8(folderYT)
						)
			else
				ShowInfo(m_simpleTV.User.YT.Lng.savePlst_3)
			end
		end
	end
	function Qlty_YT()
		local t = m_simpleTV.User.YT.QltyTab
			if not t or #t < 2 then
				m_simpleTV.Control.ExecuteAction(37)
			 return
			end
		t.ExtButton1 = {ButtonEnable = true, ButtonName = '❕'}
		t.ExtParams = {FilterType = 2}
		if not m_simpleTV.User.YT.isVideo then
			t.ExtButton0 = {ButtonEnable = true, ButtonName = '💾'}
		else
			t.ExtButton0 = {ButtonEnable = true, ButtonName = '🔎'}
		end
		local index = m_simpleTV.User.YT.QltyIndex
		local ret, id = m_simpleTV.OSD.ShowSelect_UTF8('⚙ ' .. m_simpleTV.User.YT.Lng.qlty, index - 1, t, 5000, 1 + 4)
		if not id then
			m_simpleTV.Control.ExecuteAction(37)
		end
		m_simpleTV.Control.ExecuteAction(37)
		if ret == 1 then
			if t[id].qltyLive then
				m_simpleTV.Config.SetValue('YT_qlty_live', t[id].qltyLive)
				m_simpleTV.User.YT.qlty_live = t[id].qltyLive
			else
				if t[id].qlty > 300 then
					m_simpleTV.Config.SetValue('YT_qlty', t[id].qlty)
					m_simpleTV.User.YT.qlty0 = t[id].qlty
				end
				if t[id].qlty < 100 then
					local visual = tostring(m_simpleTV.Config.GetValue('vlc/audio/visual/module', 'simpleTVConfig'))
					if visual == 'nil'
						or visual == 'none'
						or visual == ''
					then
						SetBackground(m_simpleTV.User.YT.pic or m_simpleTV.User.YT.logoDisk)
					else
						SetBackground()
					end
				end
				m_simpleTV.User.YT.qlty = t[id].qlty
			end
			if (t[id].qlty and t[id].qlty > 100) or t[id].qltyLive then
				SetBackground()
			end
			m_simpleTV.User.YT.QltyIndex = id
			m_simpleTV.Control.SetTitle(' ')
			if isPanel == false then
				ShowMessage(t[id].Name)
			end
			local adr = t[id].Address:gsub('%$OPT:start%-time=%d+', '')
			if t[id].isCipher then
				adr = DeCipherSign(adr)
			end
			m_simpleTV.Control.SetNewAddress(adr, m_simpleTV.Control.GetPosition())
		end
		if ret == 2
			and not m_simpleTV.User.YT.isVideo
		then
			SavePlst_YT()
		elseif ret == 2 and m_simpleTV.User.YT.isVideo and id then
			m_simpleTV.Control.ExecuteAction(105)
			local info_text = '🔎 ' .. m_simpleTV.User.YT.Lng.search .. ' YouTube:\n'
							.. '- ' .. m_simpleTV.User.YT.Lng.video .. '\n'
							.. '-- ' .. m_simpleTV.User.YT.Lng.plst .. '\n'
							.. '--- ' .. m_simpleTV.User.YT.Lng.channel .. '\n'
							.. '-+ ' .. m_simpleTV.User.YT.Lng.live
			ShowInfo(info_text, ARGB(0x80, 0, 0, 0), 4)
		end
		if ret == 3
		then
			ShowInfo()
		end
	end
	function ChPlst_YT()
		local tab = m_simpleTV.User.YT.ChPlstTab
			if not tab then return end
		local num = m_simpleTV.User.YT.ChPlst.Num
		local index = 0
			for k, v in ipairs(tab) do
				if tonumber(num) == tonumber(v.Name:match('^(%d+)')) then
					index = k
				end
			end
		tab.ExtParams = {FilterType = 2}
		local ret, id = m_simpleTV.OSD.ShowSelect_UTF8('📋 ' .. m_simpleTV.User.YT.ChTitle, index - 1, tab, 30000, 1 + 4 + 2 + 128)
		if not id then
			m_simpleTV.Control.ExecuteAction(37)
		end
			if ret == 1 then
				m_simpleTV.User.YT.ChPlst.Refresh = true
				m_simpleTV.User.YT.ChPlst.Num = tab[id].Name:match('^(%d+)') or tab[1].Name
				m_simpleTV.User.YT.ChPlst.Header = tab[id].Name:match('^%d+%. (.+)') or tab[1].Name
				m_simpleTV.Control.SetNewAddress(tab[id].Address)
			 return
			end
			if ret == 2 then
				PrevChPlst_YT()
			 return
			end
			if ret == 3 then
				NextChPlst_YT()
			 return
			end
	end
	function NextChPlst_YT()
		m_simpleTV.User.YT.ChPlst.Refresh = true
		local tab = table_reversa(m_simpleTV.User.YT.ChPlst.Urls)
		if #tab == 0 then
			tab[1] = m_simpleTV.User.YT.ChPlst.FirstUrl
		end
		m_simpleTV.Control.ChangeAddress = 'No'
		m_simpleTV.Control.CurrentAddress = tab[1] .. '&restart'
		dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
	end
	function PrevChPlst_YT()
		m_simpleTV.User.YT.ChPlst.Refresh = false
		local tab = m_simpleTV.User.YT.ChPlst.Urls
		if #tab > 1 then
			tab[#tab] = nil
			tab[#tab] = nil
		end
		if #tab == 0 then
			m_simpleTV.Control.CurrentAddress = m_simpleTV.User.YT.ChPlst.MainUrl
		else
			m_simpleTV.Control.CurrentAddress = tab[#tab]
		end
		m_simpleTV.User.YT.ChPlst.Urls = tab
		m_simpleTV.Control.ChangeAddress = 'No'
		dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
	end
	function MarkWatched_YT(session)
		m_simpleTV.Http.Close(session)
	end
	function OnCancel_YT()
		m_simpleTV.Control.ExecuteAction(36)
	end
		if inAdr:match('/watch_videos')
			or inAdr:match('/shared%?ci=')
		then
			inAdr = GetUrlWatchVideos(inAdr)
				if not inAdr then
					StopOnErr(0)
				 return
				end
			m_simpleTV.Http.Close(session)
			m_simpleTV.Control.PlayAddress(inAdr)
		 return
		end
	if inAdr:match('^%s*%-') then
		if m_simpleTV.Control.MainMode == 0 then
			m_simpleTV.Control.ChangeChannelLogo(m_simpleTV.User.YT.logoDisk, m_simpleTV.Control.ChannelID)
		end
		local t, types, header = Search(inAdr)
			if not t or #t == 0 then
				m_simpleTV.Http.Close(session)
				StopOnErr(5.1, m_simpleTV.User.YT.Lng.notFound)
			 return
			end
		local title, SortOrder
		if types == 'related' then
			if not m_simpleTV.User.YT.apiKey then
				GetApiKey()
			end
			local vId = inAdr:match('=(.-)$')
			local url = 'https://www.googleapis.com/youtube/v3/videos?id=' .. vId .. '&part=snippet&fields=items/snippet/localized/title' .. '&hl=' .. m_simpleTV.User.YT.Lng.hl .. '&key=' .. m_simpleTV.User.YT.apiKey
			local rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
			answer = answer or ''
			answer = answer:gsub('\\"', '%%22')
			title = answer:match('"title": "([^"]+)') or m_simpleTV.User.YT.Lng.video
			title = clean_title(title)
			title = m_simpleTV.User.YT.Lng.search .. ': ' .. header .. ' — ' .. title
		else
			title = inAdr:gsub('^[%-%+%s]+(.-)%s*$', '%1')
			title = m_simpleTV.Common.multiByteToUTF8(title)
			title = title .. ' - ' .. m_simpleTV.User.YT.Lng.search .. ' (' .. header .. ')'
		end
		local FilterType, AutoNumberFormat
		if #t > 1 then
			FilterType = 1
			AutoNumberFormat = '%1. %2'
		else
			FilterType = 2
			AutoNumberFormat = ''
		end
		t.ExtParams = {FilterType = FilterType, AutoNumberFormat = AutoNumberFormat}
		t.ExtButton1 = {ButtonEnable = true, ButtonName = '✕'}
		local ret, id = m_simpleTV.OSD.ShowSelect_UTF8('🔎 ' .. title, 0, t, 30000, 1 + 4 + 8 + 2 + 128)
		m_simpleTV.Control.ExecuteAction(37)
		m_simpleTV.Http.Close(session)
			if not id or ret == 3 then
				m_simpleTV.Control.ExecuteAction(11)
			 return
			end
		t = t[id].Address .. '&isLogo=false'
		m_simpleTV.Control.PlayAddress(t)
	 return
	end
	if inAdr:match('isPlst=true') then
		isVideo = false
	end
	if inAdr:match('/user/.-/videos')
		or inAdr:match('/channel/.-/videos')
		or inAdr:match('/feeds/videos%.xml%?channel_id=')
		or inAdr:match('/c/.-/videos')
			then
				isChPlst = false
				isChVideos = true
				plstIndex = 1
	elseif inAdr:match('/user/.-$')
		or inAdr:match('/channel/.-$')
		or inAdr:match('/c/.-$')
		or inAdr:match('&numVideo=')
		or inAdr:match('youtube%.com/%w+/?$')
		or inAdr:match('/live$')
		or inAdr:match('/embed/live_stream%?')
			then
				isChPlst = true
				isChVideos = false
				plstIndex = 1
	elseif inAdr:match('youtube%.com/%w+/videos')
			then
				isChPlst = false
				isChVideos = true
				plstIndex = 1
	end
	if inAdr:match('list=') then
		plstId = inAdr:match('list=([^&]*)')
		if plstId ~= '' then
			isPlst = true
			plstIndex = 1
		end
	end
	if inAdr:match('isChPlst=true') then
		m_simpleTV.User.YT.isChPlst = true
	end
	if ((inAdr:match('list=RD') or inAdr:match('list=TL'))
			and not inAdr:match('/embed'))
		or ((inAdr:match('list=WL') or inAdr:match('list=OL'))
			and not inAdr:match('index='))
	then
		inAdr = inAdr .. '&index=1'
	end
	if isChVideos then
		m_simpleTV.Control.ExecuteAction(37)
		if not m_simpleTV.User.YT.isChPlst then
			m_simpleTV.User.YT.ChTitleForSave = nil
		end
		m_simpleTV.User.YT.isVideo = false
		local url = inAdr:gsub('&restart', '')
		url = url:gsub('(.+)/feeds/videos%.xml%?channel_id=([%w_%-]+)', '%1' .. '/channel/' .. '%2' .. '/videos')
		local tab = {}
		local plstTotalResults = 0
		local Url0
		local user = url:match('user/([_%w%-%.]+)') or url:match('/c/([_%w%-%.]+)')
		local channel = url:match('channel/([_%w%-%.]+)')
		if not (user or channel) then
			user = url:match('youtu[%.combe]/([_%w%-%.]+)')
		end
		if user then
			Url0 = '&forUsername=' .. user
		end
		if channel then
			Url0 = '&id=' .. channel
		end
		if Url0 then
			if not m_simpleTV.User.YT.apiKey then
				GetApiKey()
			end
			if m_simpleTV.User.YT.apiKey then
					local function PlstTotalResults()
						local url = 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&fields=items(contentDetails/relatedPlaylists)' .. Url0 .. '&key=' .. m_simpleTV.User.YT.apiKey
						local rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
							if rc ~= 200 then return end
						plstId = answer:match('"uploads".-"([^"]+)')
							if not plstId then return end
						url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&fields=pageInfo&playlistId=' .. plstId .. '&key=' .. m_simpleTV.User.YT.apiKey
						rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
							if rc ~= 200 then return end
					 return tonumber(answer:match('"totalResults": (%d+)'))
					end
				plstTotalResults = PlstTotalResults() or 0
			end
		end
		if not url:match('sort=') and not url:match('/c/') then
			if user then
				url = 'https://www.youtube.com/user/' .. user .. '/videos?shelf_id=0&view=0&sort=dd'
			end
			if channel then
				url = 'https://www.youtube.com/channel/' .. channel .. '/videos?view=0&sort=dd&shelf_id=0'
			end
		end
		local t0 = {}
		t0.url = url
		t0.method = 'get'
		local params = {}
		params.Message = '⇩ ' .. m_simpleTV.User.YT.Lng.loading
		params.Callback = AsynPlsCallb_Videos_YT
		params.ProgressColor = ARGB(0x80, 255, 0, 0)
		params.User = {}
		params.User.tab = {}
		params.User.plstTotalResults = plstTotalResults
		params.ProgressEnabled = true
		if plstTotalResults < 61 then
			params.delayedShow = 1500
		end
		params.User.Title = ''
		params.User.First = true
		require 'asynPlsLoaderHelper'
		asynPlsLoaderHelper.Work(session, t0, params)
		local header = params.User.Title
		tab = params.User.tab
			if #tab == 0 then
				StopOnErr(1)
			 return
			end
		m_simpleTV.User.YT.Plst = tab
		m_simpleTV.User.YT.plstHeader = header
		if isPanel == false then
			ShowMessage(header)
		end
		tab.ExtButton0 = {ButtonEnable = true, ButtonName = '⚙', ButtonScript = 'Qlty_YT()'}
		local pl = 0
		if #tab == 1 then
			pl = 32
		end
		local FilterType, AutoNumberFormat
		if #tab > 1 then
			FilterType = 1
			AutoNumberFormat = '%1. %2'
		else
			FilterType = 2
			AutoNumberFormat = ''
		end
		tab.ExtParams = {FilterType = FilterType, AutoNumberFormat = AutoNumberFormat, LuaOnCancelFunName = 'OnCancel_YT'}
		tab.ExtButton1 = {ButtonEnable = true, ButtonName = '📋', ButtonScript = [[
					m_simpleTV.Control.ExecuteAction(37)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = 'https://www.youtube.com/channel/' .. m_simpleTV.User.YT.chId .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				]]}
		local vId = tab[1].Address:match('v=([^&]+)')
		m_simpleTV.User.YT.AddToBaseUrlinAdr = inAdr
		m_simpleTV.User.YT.AddToBaseVideoIdPlst = vId
		m_simpleTV.OSD.ShowSelect_UTF8(header, 0, tab, 10000, pl)
		m_simpleTV.Control.CurrentTitle_UTF8 = header
		local t, title = GetStreamsTab(vId)
			if not t or type(t) ~= 'table' then
				StopOnErr(2, title)
			 return
			end
		m_simpleTV.User.YT.QltyTab = t
		local index = GetQltyIndex(t)
		local retAdr = t[index].Address
		m_simpleTV.User.YT.QltyIndex = index
		if isPanel == false then
			if m_simpleTV.User.YT.isTrailer == true then
				title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.preview
			end
			local fps = t[index].Name:match('%d+ FPS')
			if fps then
				title = title .. '\n☑ ' .. fps
			end
			ShowMessage(title)
		end
		if t[index].isCipher then
			retAdr = DeCipherSign(retAdr)
		end
		if m_simpleTV.Control.MainMode == 0 then
			m_simpleTV.Control.ChangeChannelLogo('https://i.ytimg.com/vi/' .. vId .. '/hqdefault.jpg', m_simpleTV.Control.ChannelID)
		end
		m_simpleTV.Control.CurrentAddress = retAdr
	 return
	end
	if isChPlst then
		local url = inAdr
		if url:match('/live$') or url:match('/embed/live_stream%?') then
			local rc, answer = m_simpleTV.Http.Request(session, {url = url})
				if rc ~= 200 then StopOnErr(3) return end
			local liveId = answer:match('"liveStreamabilityRenderer\\":{\\"videoId\\":\\"(.-)\\"') or answer:match('"watchEndpoint\\":{\\"videoId\\":\\"(.-)\\"')
				if liveId then
					m_simpleTV.Http.Close(session)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = 'https://www.youtube.com/watch?v=' .. liveId .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				 return
				end
			url = url:gsub('/live$', '')
			url = url:gsub('embed/live_stream%?channel=', 'channel/')
		end
		if not url:match('/playlists') then
			url = url:gsub('/$', '') .. '/playlists'
		end
		if not url:match('sort=') and not url:match('browse_ajax') then
			url = url:gsub('(^.-/playlists)', '%1') .. '?view=1&sort=lad&shelf_id=0&restart'
		end
		url = url:gsub('&restart', '') .. '&restart'
		if not m_simpleTV.User.YT.ChPlst.countErr then
			m_simpleTV.User.YT.ChPlst.countErr = 0
		end
		if not m_simpleTV.User.YT.ChPlst.MainUrl then
			m_simpleTV.User.YT.ChPlst.MainUrl = url
		end
		if #m_simpleTV.User.YT.ChPlst.Urls > 0 then
			if m_simpleTV.User.YT.ChPlst.MainUrl == url then
				m_simpleTV.User.YT.ChPlst.Urls = nil
				m_simpleTV.User.YT.ChPlst.FirstUrl = nil
				m_simpleTV.User.YT.ChPlst.Num = nil
			end
		end
		if m_simpleTV.User.YT.ChPlst.MainUrl ~= url then
			if not url:match('browse_ajax') then
				m_simpleTV.User.YT.ChPlst.MainUrl = url
				m_simpleTV.User.YT.ChPlst.Urls = nil
				m_simpleTV.User.YT.ChPlst.FirstUrl = nil
				m_simpleTV.User.YT.ChPlst.Num = nil
			end
		end
		if not m_simpleTV.User.YT.ChPlst.Urls then
			m_simpleTV.User.YT.ChPlst.Urls = {}
		end
		local num = 0
		if url:match('browse_ajax') then
			url, num = url:match('^(.-)&numVideo=(%d+)')
				if not url or not num then
					StopOnErr(3.1)
				 return
				end
		end
		m_simpleTV.Http.SetCookies(session, url, 'PREF=hl=' .. m_simpleTV.User.YT.Lng.hl .. ';', '')
		local rc, answer = m_simpleTV.Http.Request(session, {url = url:gsub('&restart', ''), headers = 'X-YouTube-Client-Name: 1\nX-YouTube-Client-Version: 2.20190620\nReferer: https://www.youtube.com/'})
			if rc ~= 200 then
				StopOnErr(4, 'cant load channal page')
			 return
			end
		answer = answer:gsub('\\"', '%%22')
		answer = answer:gsub('\\/', '/')
		local chTitle = answer:match('channelMetadataRenderer.-"title":"([^"]+)') or 'playlists'
		if chTitle == 'playlists' and not url:match('browse_ajax') then
			m_simpleTV.Http.SetCookies(session, url, 'PREF=hl=' .. m_simpleTV.User.YT.Lng.hl .. ';', '')
			rc, answer = m_simpleTV.Http.Request(session, {url = url:gsub('&restart', ''), headers = 'X-YouTube-Client-Name: 1\nX-YouTube-Client-Version: 2.20190620\nReferer: https://www.youtube.com/'})
				if rc ~= 200 then
					StopOnErr(4.11, 'cant load channal page')
				 return
				end
			answer = answer:gsub('\\"', '%%22')
			answer = answer:gsub('\\/', '/')
			chTitle = answer:match('channelMetadataRenderer.-"title":"([^"]+)') or 'playlists'
		end
		chTitle = clean_title(chTitle)
		m_simpleTV.User.YT.ChTitle = chTitle
		local channel_banner = answer:match('"mobileBanner":{"thumbnails":%[{"url":"(.-)%-fcrop')
		local channel_avatar = answer:match('"avatar":{"thumbnails":%[{"url":"([^"]+)')
		if channel_banner then
			channel_banner = channel_banner:gsub('^//', 'https://')
		end
		if channel_avatar then
			channel_avatar = channel_avatar:gsub('^//', 'https://')
		end
		if not url:match('browse_ajax') and not inAdr:match('&restart') then
			SetBackground(channel_banner or m_simpleTV.User.YT.logoDisk)
			m_simpleTV.Control.SetTitle(chTitle)
		end
		local buttonNext = false
		local nextContinuationData = answer:match('"nextContinuationData"(.-)$')
		if nextContinuationData then
			buttonNext = true
			local continuation, itct = nextContinuationData:match('"continuation":"([^"]+).-"clickTrackingParams":"([^"]+)')
			if continuation and itct then
				url = 'https://www.youtube.com/browse_ajax?ctoken=' .. continuation .. '&continuation=' .. continuation .. '&itct=' .. itct
			end
		end
		answer = answer:gsub('"title":{"simpleText"', '"text"')
		answer = answer:gsub('{', '')
		answer = answer:gsub('}', '')
		local chId = answer:match('/channel/([%w_%-%.]+)') or ''
		local tab, i = {}, 1
		local j = 1 + tonumber(num)
		local desc0, sep
		local shelf = inAdr:match('shelf_id=(%d+)') or '0'
		if j == 1 and chId and shelf == '0' then
			if not m_simpleTV.User.YT.apiKey then
				GetApiKey()
			end
			if m_simpleTV.User.YT.apiKey then
					local function PlstTotalResults()
						local plstId
						local plstTotalResults
						local url = 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&fields=items(contentDetails/relatedPlaylists)&id=' .. chId .. '&key=' .. m_simpleTV.User.YT.apiKey
						local rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
							if rc ~= 200 then return end
						plstId = answer:match('"uploads".-"([^"]+)')
							if not plstId then return end
						url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&fields=pageInfo&playlistId=' .. plstId .. '&key=' .. m_simpleTV.User.YT.apiKey
						rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
							if rc ~= 200 then return end
						plstTotalResults = tonumber(answer:match('"totalResults": (%d+)') or '0')
							if plstTotalResults > 0 then
								local t = {}
								t[1] = {}
								t[1].Id = 1
								t[1].Name = '🔺 '
											.. m_simpleTV.User.YT.Lng.upLoadOnCh
											.. ' (' .. plstTotalResults .. ')'
								t[1].count = plstTotalResults
								t[1].Address = 'https://www.youtube.com/playlist?list='
											.. plstId
											.. '&isChPlst=true'
								if isPanel == true then
									t[1].InfoPanelLogo = channel_avatar or channel_banner or m_simpleTV.User.YT.logoDisk
									t[1].InfoPanelShowTime = 10000
									t[1].InfoPanelName = m_simpleTV.User.YT.Lng.channel
														.. ': ' .. chTitle
									if isWin_epgInfo == true then
										desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
												.. ':\nhttps://www.youtube.com/playlist?list=' .. plstId
										sep = ' | '
									else
										desc0 = nil
										sep = nil
									end
									t[1].InfoPanelDesc = desc0
									t[1].InfoPanelTitle = (sep or '')
														.. m_simpleTV.User.YT.Lng.plst
														.. ': '
														.. m_simpleTV.User.YT.Lng.upLoadOnCh .. ' ('
														.. plstTotalResults .. ' '
														.. m_simpleTV.User.YT.Lng.video .. ')'
								end
							 return t
							end
					 return
					end
				local plstTotalResults = PlstTotalResults()
				if plstTotalResults then
					tab = plstTotalResults
					i = 2
				end
			end
		end
			for adr, logo, name, count in answer:gmatch('PlaylistRenderer":"playlistId":"([^"]+).-"thumbnails":%["url":"([^"]+).-"text":"([^"]+).-"videoCountShortText":"simpleText":"([^"]+)') do
				tab[i] = {}
				tab[i].Id = i
				tab[i].count = count or '0'
				name = clean_title(name)
				tab[i].Name = j .. '. ' .. name .. ' (' .. count .. ')'
				tab[i].Address = 'https://www.youtube.com/playlist?list=' .. adr .. '&isChPlst=true'
				if isPanel == true then
					logo = logo:gsub('hqdefault', 'default')
					logo = logo:gsub('^//', 'https://')
					logo = logo:gsub('/vi_webp/', '/vi/')
					logo = logo:gsub('movieposter%.webp', 'default.jpg')
					tab[i].InfoPanelLogo = logo
					tab[i].InfoPanelShowTime = 10000
					tab[i].InfoPanelName = m_simpleTV.User.YT.Lng.channel .. ': ' .. chTitle
					if isWin_epgInfo == true then
						desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
							.. ':\nhttps://www.youtube.com/playlist?list=' .. adr
							sep = ' | '
					else
						desc0 = nil
						sep = nil
					end
					tab[i].InfoPanelDesc = desc0
					tab[i].InfoPanelTitle = (sep or '')
											.. m_simpleTV.User.YT.Lng.plst
											.. ': '
											.. name .. ' ('
											.. count .. ' '
											.. m_simpleTV.User.YT.Lng.video .. ')'
				end
				j = j + 1
				i = i + 1
			end
				if i == 1 then
					m_simpleTV.Http.Close(session)
					m_simpleTV.User.YT.ChPlst.countErr = m_simpleTV.User.YT.ChPlst.countErr + 1
						if m_simpleTV.User.YT.ChPlst.countErr == 3 then
							m_simpleTV.User.YT.ChPlst.countErr = nil
							StopOnErr(4.1, 'cant parse channal page')
						 return
						end
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = 'https://www.youtube.com/channel/' .. chId .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				 return
				end
		m_simpleTV.User.YT.ChPlstTab = tab
		m_simpleTV.User.YT.isChPlst = true
		local buttonPrev = false
		if #m_simpleTV.User.YT.ChPlst.Urls >= 1 then
			buttonPrev = true
		end
		tab.ExtButton0 = {ButtonEnable = buttonPrev, ButtonName = '🢀'}
		tab.ExtButton1 = {ButtonEnable = buttonNext, ButtonName = '🢂'}
		num = #tab + tonumber(num)
		local nom1ChPlstTab = tonumber(tab[1].Name:match('^(%d+)') or '1')
		if nom1ChPlstTab == 1 then
			m_simpleTV.User.YT.Nom1ChPlstTab = 1
			m_simpleTV.User.YT.pageChPlst = 1
		end
		if nom1ChPlstTab > m_simpleTV.User.YT.Nom1ChPlstTab then
			m_simpleTV.User.YT.pageChPlst = m_simpleTV.User.YT.pageChPlst + 1
		end
		if nom1ChPlstTab < m_simpleTV.User.YT.Nom1ChPlstTab then
			m_simpleTV.User.YT.pageChPlst = m_simpleTV.User.YT.pageChPlst - 1
		end
		m_simpleTV.User.YT.Nom1ChPlstTab = nom1ChPlstTab
		if m_simpleTV.User.YT.pageChPlst > 1 then
			m_simpleTV.User.YT.ChTitle = m_simpleTV.User.YT.ChTitle .. ' (' .. m_simpleTV.User.YT.Lng.page .. ' ' .. m_simpleTV.User.YT.pageChPlst .. ')'
		end
		url = url .. '&numVideo=' .. num
		table.insert(m_simpleTV.User.YT.ChPlst.Urls, url)
		if not m_simpleTV.User.YT.ChPlst.FirstUrl then
			m_simpleTV.User.YT.ChPlst.FirstUrl = url
		end
		if not m_simpleTV.User.YT.ChPlst.Num then
			m_simpleTV.User.YT.ChPlst.Num = 0
		end
		local index = 0
		if m_simpleTV.User.YT.ChPlst.Refresh then
			index = 0
		end
		num = m_simpleTV.User.YT.ChPlst.Num
			for k, v in ipairs(tab) do
				if tonumber(num) == tonumber(v.Name:match('^(%d+)')) then
					index = k
				end
			end
		tab.ExtParams = {FilterType = 2, LuaOnCancelFunName = 'OnCancel_YT'}
		local ret, id = m_simpleTV.OSD.ShowSelect_UTF8('📋 ' .. m_simpleTV.User.YT.ChTitle, index - 1, tab, 30000, 1 + 4 + 8 + 2 + 128)
		m_simpleTV.Control.CurrentTitle_UTF8 = chTitle
			if not id then
				m_simpleTV.Control.ExecuteAction(37)
				m_simpleTV.Http.Close(session)
			 return
			end
		if ret == 1 then
			plstId = tab[id].Address:gsub('^.-list=(.+)', '%1')
			m_simpleTV.User.YT.ChPlst.Num = tab[id].Name:match('^(%d+)') or tab[1].Name
			m_simpleTV.User.YT.ChPlst.Header = tab[id].Name:match('^%d+%. (.+)') or tab[1].Name
			m_simpleTV.User.YT.ChPlst.Refresh = false
			isPlst = true
			m_simpleTV.User.YT.ChTitleForSave = chTitle
			local con = tonumber(tab[id].count)
			if con and con < 300 then
				SetBackground()
			end
		end
			if ret == 2 then
				PrevChPlst_YT()
			 return
			end
			if ret == 3 then
				NextChPlst_YT()
			 return
			end
	end
	if isPlst then
		m_simpleTV.User.YT.isVideo = false
		if not m_simpleTV.User.YT.isChPlst then
			m_simpleTV.User.YT.ChTitleForSave = nil
		end
		if inAdr:match('index=') then
			plstIndex = inAdr:match('index=(%d+)') or '1'
			plstIndex = tonumber(plstIndex)
			m_simpleTV.Http.SetCookies(session, inAdr, m_simpleTV.User.YT.cookies, '')
			local rc, answer = m_simpleTV.Http.Request(session, {url = inAdr:gsub('&restart', '')})
				if rc ~= 200 then
					StopOnErr(5)
				 return
				end
			answer = answer:gsub('\\"', '%%22')
			local tab, i = {}, 1
			local name, selected, timer, adr, desc0, panelDescName
				for gg in answer:gmatch('{"playlistPanelVideoRenderer":{"title".-%]}}') do
					name = gg:match('"title".-"simpleText":"([^"]+)')
					selected = gg:match('"selected":(%a+)')
					timer = gg:match('"label".-"simpleText":"(%d+:.-)"') or ''
					adr = gg:match('"videoId":"([^"]+)')
					if name and adr then
						tab[i] = {}
						tab[i].Id = i
						name = clean_title(name)
						if isPanel == false then
							if timer ~= '' then
								timer = ' (' .. timer .. ')'
							end
							tab[i].Name = name .. timer
						else
							tab[i].Name = name
							tab[i].InfoPanelName = name
							tab[i].InfoPanelLogo = 'https://i.ytimg.com/vi/' .. adr .. '/default.jpg'
							tab[i].InfoPanelShowTime = 10000
							panelDescName = nil
							if isWin_epgInfo == true then
								desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
										.. ':\nhttps://www.youtube.com/watch?v=' .. adr
							else
								desc0 = nil
							end
							tab[i].InfoPanelDesc = desc0
							if desc0 and timer ~= '' then
								panelDescName = ' | ' .. timer
							elseif not desc0 and timer ~= '' then
								panelDescName = timer
							end
							tab[i].InfoPanelTitle = panelDescName or ' '
						end
						tab[i].Address = 'https://www.youtube.com/watch?v=' .. adr .. '&isPlst=true'
						if selected and selected == 'true' then
							selectPos = i
						end
						i = i + 1
					end
				end
				if i == 1 then
					for gg in answer:gmatch('{"playlistVideoRenderer":{"videoId".-%]}}') do
						name = gg:match('"title".-"simpleText":"([^"]+)')
						timer = gg:match('"lengthText".-"simpleText":"(%d+:.-)"') or ''
						adr = gg:match('"videoId":"([^"]+)')
						if name and adr then
							name = clean_title(name)
							tab[i] = {}
							tab[i].Id = i
							if isPanel == false then
								if timer ~= '' then
									timer = ' (' .. timer .. ')'
								end
								tab[i].Name = name .. timer
							else
								tab[i].Name = name
								tab[i].InfoPanelName = name
								tab[i].InfoPanelLogo = 'https://i.ytimg.com/vi/' .. adr .. '/default.jpg'
								tab[i].InfoPanelShowTime = 10000
								panelDescName = nil
								if isWin_epgInfo == true then
									desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
											.. ':\nhttps://www.youtube.com/watch?v=' .. adr
								else
									desc0 = nil
								end
								tab[i].InfoPanelDesc = desc0
								if desc0 and timer ~= '' then
									panelDescName = ' | ' .. timer
								elseif not desc0 and timer ~= '' then
									panelDescName = timer
								end
								tab[i].InfoPanelTitle = panelDescName or ' '
							end
							tab[i].Address = 'https://www.youtube.com/watch?v=' .. adr .. '&isPlst=true'
							i = i + 1
						end
					end
				end
				if i == 1 and not urlAdr:match('&restart') then
					m_simpleTV.Http.Close(session)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = inAdr .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				 return
				end
				if i == 1 and not urlAdr:match('&restart&restart') then
					m_simpleTV.Http.Close(session)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = inAdr:gsub('[%?&]list=[%a%d_%-]+', '') .. '&restart&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				 return
				end
				if i == 1 then
					StopOnErr(5.1)
				 return
				end
			local header = answer:match('"playlist":{"title":"([^"]+)')
						or answer:match('"microformat".-"title":"([^"]+)')
						or 'title - not found'
			header = clean_title(header)
			if isPanel == false then
				ShowMessage(header)
			end
			m_simpleTV.User.YT.Plst = tab
			m_simpleTV.User.YT.plstHeader = header
			local pl = 0
			if selectPos and selectPos ~= plstIndex and selectPos ~= 1 then
				pl = 32
			end
			plstIndex = selectPos or 1
			if plstIndex > 1 or inAdr:match('[%?&]t=') or #tab == 1 or inAdr:match('list=RD') then
				pl = 32
			end
			local vId = tab[plstIndex].Address:match('watch%?v=([^&]+)')
			m_simpleTV.User.YT.AddToBaseUrlinAdr = inAdr
			local plstPicId = tab[1].Address:match('watch%?v=([^&]+)')
			m_simpleTV.User.YT.AddToBaseVideoIdPlst = plstPicId
			tab.ExtButton0 = {ButtonEnable = true, ButtonName = '⚙', ButtonScript = 'Qlty_YT()'}
			tab.ExtButton1 = {ButtonEnable = true, ButtonName = '📋', ButtonScript = [[
					m_simpleTV.Control.ExecuteAction(37)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = 'https://www.youtube.com/channel/' .. m_simpleTV.User.YT.chId .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				]]}
			local FilterType, AutoNumberFormat
			if #tab > 1 then
				FilterType = 1
				AutoNumberFormat = '%1. %2'
			else
				FilterType = 2
				AutoNumberFormat = ''
			end
			tab.ExtParams = {FilterType = FilterType, AutoNumberFormat = AutoNumberFormat, LuaOnCancelFunName = 'OnCancel_YT'}
			m_simpleTV.OSD.ShowSelect_UTF8(header, plstIndex - 1, tab, 10000, pl)
			local t, title = GetStreamsTab(vId)
				if not t or type(t) ~= 'table' then
					StopOnErr(6, title)
				 return
				end
			m_simpleTV.User.YT.QltyTab = t
			local index = GetQltyIndex(t)
			local retAdr = t[index].Address
			m_simpleTV.User.YT.QltyIndex = index
			if isPanel == false then
				if m_simpleTV.User.YT.isTrailer == true then
					title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.preview
				end
				local fps = t[index].Name:match('%d+ FPS')
				if fps then
					title = title .. '\n☑ ' .. fps
				end
				ShowMessage('◽️ ' .. header .. '\n' .. title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.plst)
			end
			m_simpleTV.Control.CurrentTitle_UTF8 = header
			if t[index].isCipher then
				retAdr = DeCipherSign(retAdr)
			end
			if m_simpleTV.Control.MainMode == 0 then
				m_simpleTV.Control.ChangeChannelLogo('https://i.ytimg.com/vi/' .. plstPicId .. '/hqdefault.jpg', m_simpleTV.Control.ChannelID)
			end
			if inAdr:match('isLogo=false') then
				retAdr = retAdr .. '$OPT:POSITIONTOCONTINUE=0'
			end
			m_simpleTV.Control.CurrentAddress = retAdr
		 return
		end
		if not inAdr:match('index=') then
			m_simpleTV.Control.ExecuteAction(37)
			if not m_simpleTV.User.YT.apiKey then
				GetApiKey()
			end
			local url = 'https://www.googleapis.com/youtube/v3/playlists?part=snippet&fields=items/snippet/localized/title&id=' .. plstId .. '&hl=' .. m_simpleTV.User.YT.Lng.hl .. '&key=' .. m_simpleTV.User.YT.apiKey
			local rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
			if rc ~= 200 then
				answer = ''
			end
			answer = answer:gsub('\\"', '%%22')
			local header = answer:match('"title": "([^"]+)') or m_simpleTV.User.YT.Lng.plst
			header = clean_title(header)
			m_simpleTV.User.YT.plstHeader = header
			local tab, i = {}, 1
			url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&fields=pageInfo&playlistId=' .. plstId .. '&hl=' .. m_simpleTV.User.YT.Lng.hl .. '&key=' .. m_simpleTV.User.YT.apiKey
			rc, answer = m_simpleTV.Http.Request(session, {url = url, headers = m_simpleTV.User.YT.apiKeyHeader})
			if rc ~= 200 then
				answer = ''
			end
			local plstTotalResults = tonumber(answer:match('"totalResults": (%d+)') or '1')
			local t0 = {}
			t0.url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&fields=nextPageToken,items(snippet/title,snippet/resourceId/videoId,snippet/description)&playlistId=' .. plstId .. '&hl=' .. m_simpleTV.User.YT.Lng.hl .. '&key=' .. m_simpleTV.User.YT.apiKey
			t0.method = 'get'
			t0.headers = m_simpleTV.User.YT.apiKeyHeader
			local params = {}
			params.Message = '⇩ ' .. m_simpleTV.User.YT.Lng.loading
			params.Callback = AsynPlsCallb_Plst_YT
			params.ProgressColor = ARGB(0x80, 255, 0, 0)
			params.User = {}
			params.User.tab = {}
			params.User.rc = nil
			params.User.plstTotalResults = plstTotalResults
			params.ProgressEnabled = true
			if plstTotalResults < 201 then
				params.delayedShow = 1500
			end
			require 'asynPlsLoaderHelper'
			asynPlsLoaderHelper.Work(session, t0, params)
			tab = params.User.tab
			rc = params.User.rc
				if rc == 400 or rc == - 1 then
					StopOnErr(8)
				 return
				end
				if #tab == 0 and rc then
					if rc == 404 and not inAdr:match('&restart') then
						inAdr = inAdr .. '&index=1'
					elseif (rc == 404 or rc == 403) and inAdr:match('&restart') then
						inAdr = inAdr:gsub('[%?&]list=[%w_%-]+', '')
					end
					m_simpleTV.Http.Close(session)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = inAdr .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				 return
				end
				if #tab == 0 and not rc then
					StopOnErr(9, m_simpleTV.User.YT.Lng.videoNotAvail)
					if m_simpleTV.User.YT.isChPlst == true then
						m_simpleTV.Common.Sleep(2000)
						m_simpleTV.Control.ChangeAddress = 'No'
						m_simpleTV.Control.CurrentAddress = m_simpleTV.User.YT.ChPlst.MainUrl .. '&restart'
						dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
					end
				 return
				end
				if not selectPos and videoId and inAdr:match('[%?&]t=') then
					inAdr = inAdr:gsub('[%?&]list=[%w_%-]+', '')
					m_simpleTV.Http.Close(session)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = inAdr .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				 return
				end
			m_simpleTV.User.YT.Plst = tab
			local pl = 0
			if selectPos and selectPos ~= plstIndex and selectPos ~= 1 then
				pl = 32
			end
			plstIndex = selectPos or 1
			if plstIndex > 1 or inAdr:match('[%?&]t=') or #tab == 1 then
				pl = 32
			end
			tab.ExtButton0 = {ButtonEnable = true, ButtonName = '⚙', ButtonScript = 'Qlty_YT()'}
			if m_simpleTV.User.YT.isChPlst then
				tab.ExtButton1 = {ButtonEnable = true, ButtonName = '📋', ButtonScript = 'ChPlst_YT()'}
			else
				tab.ExtButton1 = {ButtonEnable = true, ButtonName = '📋', ButtonScript = [[
					m_simpleTV.Control.ExecuteAction(37)
					m_simpleTV.Control.ChangeAddress = 'No'
					m_simpleTV.Control.CurrentAddress = 'https://www.youtube.com/channel/' .. m_simpleTV.User.YT.chId .. '&restart'
					dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
				]]}
			end
			local FilterType, AutoNumberFormat
			if #tab > 1 then
				FilterType = 1
				AutoNumberFormat = '%1. %2'
			else
				FilterType = 2
				AutoNumberFormat = ''
			end
			tab.ExtParams = {FilterType = FilterType, AutoNumberFormat = AutoNumberFormat, LuaOnCancelFunName = 'OnCancel_YT'}
			local vId
			if plstId:match('^RD') then
					local function shuffle(t)
						for i = #t, 2, -1 do
							local j = math.random(i)
							t[i], t[j] = t[j], t[i]
						end
					end
				shuffle(tab)
				vId = tab[1].Address:match('watch%?v=([^&]+)')
			else
				vId = tab[plstIndex].Address:match('watch%?v=([^&]+)')
			end
			m_simpleTV.OSD.ShowSelect_UTF8(header, plstIndex - 1, tab, 10000, pl)
			local t, title = GetStreamsTab(vId)
				if not t or type(t) ~= 'table' then
					StopOnErr(10, title)
				 return
				end
			m_simpleTV.User.YT.QltyTab = t
			local index = GetQltyIndex(t)
			local retAdr = t[index].Address
			m_simpleTV.User.YT.QltyIndex = index
			if isPanel == false then
				if m_simpleTV.User.YT.isTrailer == true then
					title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.preview
				end
				local fps = t[index].Name:match('%d+ FPS')
				if fps then
					title = title .. '\n☑ ' .. fps
				end
				ShowMessage('◽️ ' .. header .. '\n' .. title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.plst)
			end
			if not m_simpleTV.User.YT.isChPlst then
				m_simpleTV.Control.CurrentTitle_UTF8 = header
			else
				m_simpleTV.Control.CurrentTitle_UTF8 = ''
			end
			if t[index].isCipher then
				retAdr = DeCipherSign(retAdr)
			end
			local plstPicId
			if plstId:match('^RD') then
				local plstPicIdRD = plstId:gsub('^RD', '')
				m_simpleTV.User.YT.AddToBaseUrlinAdr = 'https://www.youtube.com/watch?v=' .. plstPicIdRD .. '&list=' .. plstId
				retAdr = retAdr .. '$OPT:POSITIONTOCONTINUE=0'
				plstPicId = plstPicIdRD
				m_simpleTV.User.YT.AddToBaseVideoIdPlst = plstPicIdRD
			else
				m_simpleTV.User.YT.AddToBaseUrlinAdr = 'https://www.youtube.com/playlist?list=' .. plstId
				plstPicId = tab[1].Address:match('watch%?v=([^&]+)')
				m_simpleTV.User.YT.AddToBaseVideoIdPlst = plstPicId
			end
			m_simpleTV.Control.CurrentAddress = retAdr
			if m_simpleTV.Control.MainMode == 0 then
				m_simpleTV.Control.ChangeChannelLogo('https://i.ytimg.com/vi/' .. plstPicId .. '/hqdefault.jpg', m_simpleTV.Control.ChannelID)
			end
			if m_simpleTV.User.YT.isChPlst then
				m_simpleTV.Control.SetNewAddress(m_simpleTV.Control.CurrentAddress .. '$OPT:POSITIONTOCONTINUE=0')
			end
		 return
		end
	end
	if not isPlst then
		local t, title = GetStreamsTab(videoId)
			if not t then
				StopOnErr(12, title)
			 return
			end
			if type(t) ~= 'table' then
				m_simpleTV.Control.PlayAddress(t)
			 return
			end
		m_simpleTV.User.YT.QltyTab = t
		local index = GetQltyIndex(t)
		local retAdr = t[index].Address
		m_simpleTV.User.YT.QltyIndex = index
		if isVideo then
			if m_simpleTV.Control.MainMode == 0 then
				m_simpleTV.Control.ChangeChannelLogo('https://i.ytimg.com/vi/' .. videoId .. '/hqdefault.jpg', m_simpleTV.Control.ChannelID)
			end
			local name = title:gsub('%c.-$', '')
			m_simpleTV.Control.SetTitle(name)
			m_simpleTV.Control.CurrentTitle_UTF8 = name
			m_simpleTV.User.YT.isVideo = true
			local header, name_header, ap_header, desc, desc0, panelDescName
			local publishedAt = ''
			if m_simpleTV.User.YT.author
				and m_simpleTV.User.YT.isTrailer == false
			then
				name_header = m_simpleTV.User.YT.Lng.upLoadOnCh
						.. ': '
						.. m_simpleTV.User.YT.author
			elseif m_simpleTV.User.YT.isTrailer == true then
				name_header = m_simpleTV.User.YT.Lng.preview
			else
				name_header = ''
			end
			if m_simpleTV.User.YT.isLive == true then
				if isPanel == false then
					ap_header = ' (' .. m_simpleTV.User.YT.Lng.live .. ')'
				else
					if m_simpleTV.User.YT.actualStartTime then
						local timeSt = timeStamp(m_simpleTV.User.YT.actualStartTime)
						timeSt = os.date('%y %d %m %H %M', tonumber(timeSt))
						local year, day, month, hour, min = timeSt:match('(%d+) (%d+) (%d+) (%d+) (%d+)')
						publishedAt = ' | ' .. m_simpleTV.User.YT.Lng.started .. ': '
								.. string.format('%d:%02d (%d/%d/%02d)', hour, min, day, month, year)
					end
				end
			else
				if isPanel == false then
					if m_simpleTV.User.YT.duration ~= '' then
						ap_header = ' (' .. secondsToClock(m_simpleTV.User.YT.duration) .. ')'
					end
				end
			end
			local t1 = {}
			t1[1] = {}
			t1[1].Id = 1
			t1[1].Address = retAdr
			t1[1].Name = name
			if isPanel == false then
				header = name_header .. (ap_header or '')
			else
				if m_simpleTV.User.YT.isTrailer == true then
					ap_header = m_simpleTV.User.YT.Lng.preview
				elseif m_simpleTV.User.YT.isLive == true then
					ap_header = m_simpleTV.User.YT.Lng.live
				else
					ap_header = m_simpleTV.User.YT.Lng.video
				end
				if m_simpleTV.User.YT.isLive == false then
					if m_simpleTV.User.YT.duration ~= '' then
						publishedAt = ' | ' .. secondsToClock(m_simpleTV.User.YT.duration)
					end
				end
				header = 'YouTube - ' .. ap_header
				t1[1].InfoPanelLogo = 'https://i.ytimg.com/vi/' .. videoId .. '/default.jpg'
				t1[1].InfoPanelName = name
				t1[1].InfoPanelShowTime = 8000
				if isWin_epgInfo == true then
					desc0 = '💢 ' .. m_simpleTV.User.YT.Lng.link
							.. ':\nhttps://www.youtube.com/watch?v=' .. videoId
				else
					desc0 = nil
				end
				desc = m_simpleTV.User.YT.desc
				panelDescName = nil
				if desc and desc ~= '' then
					panelDescName = m_simpleTV.User.YT.Lng.desc .. ' | '
					if desc0 then
						desc0 = desc0 .. '\n'
					else
						desc0 = ''
					end
					desc0 = desc0 .. '💢 '
						.. m_simpleTV.User.YT.Lng.desc .. ':\n'
						.. clean_desc(desc)
				elseif desc0 then
					panelDescName = ' | '
				end
				t1[1].InfoPanelDesc = desc0
				t1[1].InfoPanelTitle = (panelDescName or '')
									.. m_simpleTV.User.YT.Lng.channel .. ': '
									.. clean_title(m_simpleTV.User.YT.author)
									.. publishedAt
			end
			if m_simpleTV.User.YT.isLiveContent == false
				and m_simpleTV.User.YT.isTrailer == false
			then
				t1[2] = {}
				t1[2].Id = 2
				t1[2].Name = '🔎 ' .. m_simpleTV.User.YT.Lng.search .. ': ' .. m_simpleTV.User.YT.Lng.relatedVideos
				t1[2].Address = '-related=' .. m_simpleTV.User.YT.vId .. '&isLogo=false'
				if m_simpleTV.User.YT.isMusic == true then
					t1[3] = {}
					t1[3].Id = 3
					t1[3].Name = '🎵 Music-Mix ' .. m_simpleTV.User.YT.Lng.plst
					t1[3].Address = 'https://www.youtube.com'
									.. '/embed?listType=playlist'
							.. '&list=RD'
							.. m_simpleTV.User.YT.vId
							.. '&isLogo=false'
					m_simpleTV.User.YT.ChTitleForSave = nil
				end
			end
			t1.ExtParams = {FilterType = 2, LuaOnCancelFunName = 'OnCancel_YT'}
			t1.ExtButton0 = {ButtonEnable = true, ButtonName = '⚙', ButtonScript = 'Qlty_YT()'}
			t1.ExtButton1 = {ButtonEnable = true, ButtonName = '📋', ButtonScript = [[
						m_simpleTV.Control.ExecuteAction(37)
						m_simpleTV.Control.ChangeAddress = 'No'
						m_simpleTV.Control.CurrentAddress = 'https://www.youtube.com/channel/' .. m_simpleTV.User.YT.chId .. '&restart'
						dofile(m_simpleTV.MainScriptDir .. 'user\\video\\!youtube.lua')
					]]}
			m_simpleTV.OSD.ShowSelect_UTF8(header, 0, t1, 8000, 32 + 64 + 128)
		else
			m_simpleTV.Control.CurrentTitle_UTF8 = ''
		end
		if isPanel == false then
			if m_simpleTV.User.YT.isTrailer == true then
				title = title .. '\n☑ ' .. m_simpleTV.User.YT.Lng.preview
			end
			local fps = t[index].Name:match('%d+ FPS')
			if fps then
				title = title .. '\n☑ ' .. fps
			end
			ShowMessage(title)
		end
		if t[index].isCipher then
			retAdr = DeCipherSign(retAdr)
		end
		m_simpleTV.Control.CurrentAddress = retAdr
		if debug then
			local scr_time = string.format('%.3f', (os.clock() - debug))
			local calc = scr_time - debug_0
			local adr = m_simpleTV.Common.fromPersentEncoding(retAdr)
			debug_in_file(
							title .. '\n\n'
							.. 'https://www.youtube.com/watch?v=' .. videoId .. '\n\n'
							.. 'video itag: ' .. tostring(t[index].itag)
							.. ' | audio itag: ' .. tostring(t[1].audioItag)
							.. ' | isCipher: ' .. tostring(t[index].isCipher) .. '\n\n'
							.. 'time ' .. scr_time .. ' s.'
							.. ' | request ' .. debug_0 .. ' s.'
							.. ' | calc ' .. calc .. ' s.\n'
							.. '-----------------------------------------------------------------------\n\n'
							.. adr:gsub('%$', '\n\n$'):gsub('slave=', 'slave=\n\n'):gsub('%#', '\n\n#\n\n')
							, m_simpleTV.Common.GetMainPath(2) .. 'YouTube_log.txt', true
						)
		end
	 return
	end