volkan-k / CANLI TV IZLE

// ==UserScript==
// @name 			CANLI TV IZLE
// @description 	CANLI TV IZLEYIN
// @author 			volkan-k
// @version 		2.9.6
// @date 			12-07-2018
// @namespace 		canliTV
// @include 		*
// @require 		https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js
// @require 		https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js
// @require 		https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.10.0/hls.min.js
// @require-test 		https://github.com/volkirik/hls.js/raw/master/dist/hls.js
// @require-test 		https://cdnjs.cloudflare.com/ajax/libs/jquery-fullscreen-plugin/1.1.4/jquery.fullscreen-min.js
// @license 		MIT
// @run-at 			document-end
// @grant 			GM_xmlhttpRequest
// @grant 			GM_registerMenuCommand
// @connect 		canlitv.me
// @connect 		tvizlehd.com
// @connect 		fmanager.net
// @connect 		harunyahya.tv
// @connect 		98.142.98.125
// @connect 		streamlock.net
// @connect 		akamaihd.net
// @connect 		yayin.com.tr
// @connect 		streamprovider.net
// @connect 		radyotelekomtv.com
// @connect 		62.113.210.6
// @connect 		cdnnew.com
// @connect 		212.174.58.161
// @connect 		rocketcdn.com
// @connect 		dailymotion.com
// @connect 		listenpowerapp.com
// @connect 		dogannet.tv
// @connect 		cloudflare.com
// @connect 		haberturk.com
// @connect 		mncdn.com
// ==/UserScript==

var channels=[];

this.$ = this.jQuery = jQuery.noConflict(true);

var debug_internal = 1; // 1=enable debug , 0 =disable debug
var debug_developer = 0; // do not set this to 1, if you are not developer.
var RANDOM=Math.floor(Math.random()*1234567890);
var CONTAINER_ID='canlitv'+RANDOM;
var VOLUME_ID='volume'+RANDOM;
var cnt_id;
var my_hls;
var timer1;
var volume=100;
var size=90; // dialog size in percentage, must be integer, minimum 25, maximum 100, default 90.
var no_1st_fetch, no_2nd_fetch;

function debugLog(message, do_alert) {
	if (debug_internal==1) {
		console.log("USER-SCRIPT CANLI_TV_IZLE | " + message);
	}
	if (do_alert && typeof alert === "function") {
		alert("USER-SCRIPT CANLI_TV_IZLE | " + message);
	}
}

function convert_relative_urls(css,base){
	return css.replace(/url\s*\(\s*['"]{1}([^'"]+)['"]{1}\s*\)/ig, function (match, capture) { 
		if (/^(https?|file|ftps?|mailto|javascript|data:image\/[^;]{2,9};):/i.test(capture)) {
			return match; // url is already absolute
		}
		return "url('"+new URL(capture,base).href+"')";
	}); 
}

function addStyle_external(css_link, once, xhr) {
	id=btoa(css_link).replace(/[+\/=]+/ig, "");
	if (typeof xhr === "boolean" && xhr === true){
		// xhr it
		GM_xmlhttpRequest({
			method: "GET",
			url: css_link,
			onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
			onload: function(response){
				if (response.readyState !== 4 || response.status !== 200) return;
				addGlobalStyle(convert_relative_urls(response.responseText,css_link), once,id);
				inlineBase64Style(document.getElementById(id));
			}
		});
		return;
	}
	var head, style;
	head = document.getElementsByTagName('head')[0];
	if (!head) {
		return;
	}
	if (once && $("link[href='"+css_link+"']").length>0) {
		return;
	}
	style = document.createElement('link');
	style.setAttribute("rel", "stylesheet");
	style.setAttribute("type", "text/css");
	style.setAttribute("id", id);
	style.setAttribute("href", css_link);
	head.appendChild(style);
}

function addGlobalStyle(css, once,id) {
	var head, style;
	head = document.getElementsByTagName('head')[0];
	if (!head) {
		return;
	}
	if (once && document.getElementById(id)) {
		return;
	}
	style = document.createElement('style');
	style.setAttribute("type", "text/css");
	if (typeof id === "string"){
		style.setAttribute("id", id);
	}
	style.innerHTML = css;
	head.appendChild(style);
}

function get_max_z_index(){
	return Math.max.apply(null, 
		$.map($('body *'), function(e,n) {
			if ($(e).css('position') != 'static')
				return parseInt($(e).css('z-index')) || 1;
			}
		)
	);
}

function SetVolume(val) {
	volume=val;
	debugLog('Volume Now: ' + volume);
	var player = $('video#player_tv'+RANDOM);
	if (player.length>0){
		debugLog('Volume Before: ' + Math.round(player.prop('volume')*100));
		player.prop("volume", val / 100);
	}
	var player_yt = $("iframe#EXT_FRAME"+RANDOM+"[src*='www.youtube.com']");
	if (player_yt.length>0){
		player_yt[0].contentWindow.postMessage('{"event":"command","func":"setVolume","args":['+val+']}', '*');
		/*var iframe = player_yt[0];
		var innerDoc = iframe.contentDocument || iframe.contentWindow.document;
		var ytplayer = innerDoc.querySelector("div.html5-video-player");
		if (ytplayer !== null && typeof ytplayer.setVolume==="function") {
			debugLog('Volume Before: ' + ytplayer.getVolume());
			ytplayer.setVolume(val);
		}*/
	}
}

function open_volume_dialog() {
	if ($('#'+VOLUME_ID).length>0 && $('#'+VOLUME_ID).dialog('isOpen')) {
		$('#'+VOLUME_ID).dialog('close').remove();
		return;
	}
	volume_div = $("<div title='SES AYARI' id='"+VOLUME_ID+"'></div>").dialog({
		width: 300,
		height: 210,
		closeText: "KAPAT",
		buttons: [
			{
				text: "KAPAT",
				click: function () {
					$('#'+VOLUME_ID).dialog('close').remove();
				},
				icon: "ui-icon-closethick"
			}
		],
		close: function( event, ui ) {
			$(this).dialog('destroy').remove();
		}
	});
	$('#'+VOLUME_ID).append('<p class="ui-state-default ui-corner-all ui-helper-clearfix" style="padding:4px;">'
		+'<span class="ui-icon ui-icon-volume-on" style="float:left; margin:-2px 5px 0 0;"></span>'
		+'Ses duzeyi<span id="handle'+RANDOM+'"></span>'
		+'</p>'
		+'<div id="master'+RANDOM+'" style="width:220px; margin:15px;"></div>');
	$( "#master"+RANDOM ).slider({
		value: volume,
		create: function() {
			$( "#handle"+RANDOM ).text( ' = %'+$( this ).slider( "value" ) );
		},
		slide: function( event, ui ) {
			$( "#handle"+RANDOM ).text( ' = %'+ui.value );
			SetVolume(ui.value);
		}
	});
}

function init_dialog() {
	if ($('#'+CONTAINER_ID).length>0 && $('#'+CONTAINER_ID).dialog('isOpen')) {
		$('#'+CONTAINER_ID).dialog('close').remove();
	}
	addStyle_external('https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css', true, true);
	var maxZ = get_max_z_index();
	addGlobalStyle(".ui-dialog { z-index: "+(maxZ+1)+" !important; position: absolute} .ui-dialog .ui-dialog-buttonpane button { font-weight: bolder;} #"+CONTAINER_ID+" {background-color: white; font-size: 14px;}", true,'gm_added_style_watchTV');
	watchtv_div = $("<div title='CANLI TV IZLE' id='"+CONTAINER_ID+"'></div>").dialog({
		width: "auto",
		height: "auto",
		closeText: "KAPAT",
		buttons: [
			{
				text: "SES AYARI",
				click: function () {
					open_volume_dialog();
				},
				icon: "ui-icon-volume-on"
			},
			{
				text: "KANAL LISTESI",
				click: function () {
					show_channels();
				},
				icon: "ui-icon-home"
			},
			{
				text: "YENILE",
				click: function () {
					refresh_dialog(); 
				},
				icon: "ui-icon-refresh"
			}
		],
		close: function( event, ui ) {
			if (typeof my_hls !=="undefined" && typeof my_hls.destroy === "function"){
				debugLog("DEBUG: destroying hls");
				my_hls.destroy();
			}
			$('video#player_tv'+RANDOM).each(function() {
				$(this)[0].pause();
				$(this)[0].currentTime = 0;
				$(this).attr('src', '');
				$(this)[0].load();
				$(this)[0].innerHTML = '';
				$(this)[0].load();
				$(this).removeAttr('src');
				$(this)[0].load();
				//$(this).remove();
				$(this).hide();
			});
			$(this).dialog('destroy').remove();
			$('#'+VOLUME_ID).dialog('close').remove();
			clearTimeout(timer1);
		}
	}); // width & height: auto, set child height.
	//watchtv_div.height($(window).innerHeight()*90/100-$("div.ui-dialog-titlebar").outerHeight()-$("div.ui-dialog-buttonpane").outerHeight());
	//watchtv_div.width($(window).innerWidth()*90/100);
	/*if (watchtv_div.width() < 650) {
		watchtv_div.width(650);
	}*/
	watchtv_div.parent().position({
		my: "center center",
		at: "center center",
		of: window
	});
	return CONTAINER_ID;
}

function refresh_dialog(){
	if ($('#'+CONTAINER_ID+' [current_channel_id]').length>0) {
		// reload channel
		fetch_channel_url($('#'+CONTAINER_ID+' [current_channel_id]').attr("current_channel_id"),false);
	} else {
		// reload channel list
		show_channels();
	}
}

function CreateIframe() {
	iframe = document.createElement('iframe');
	iframe.setAttribute("id", "EXT_FRAME"+RANDOM);
	iframe.setAttribute("width", "100%");
	iframe.setAttribute("height", "100%");
	iframe.setAttribute("border", "0");
	iframe.setAttribute("scrolling", "yes");
	iframe.setAttribute("style", "border: 0 none;");
	// allow fullscreen
	iframe.setAttribute("webkitAllowFullScreen", "webkitAllowFullScreen");
	iframe.setAttribute("mozAllowFullScreen", "mozAllowFullScreen");
	iframe.setAttribute("allowFullScreen", "allowFullScreen");
	// allow autoplay: If this isn't set, the player will not be able to play without a user gesture.
	iframe.setAttribute("allow", "autoplay");
	// enable js api for youtube
	iframe.setAttribute("enablejsapi", "true");
	//iframe.setAttribute("sandbox", "allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation");
	return iframe;
}

function show_channels(append) {
	clearTimeout(timer1);
	if (typeof append === "undefined"){
		var append=false;
	}
	if (!append){
		cnt_id=init_dialog();
	}
	//document.getElementById(cnt_id).style.overflow = "hidden";
	// fill it
	//iframe.onload = iframe_loaded;
	if (append){
		url="http://canlitv.me/?sayfa=2";
	} else {
		url="http://canlitv.me";
	}
	GM_xmlhttpRequest({
		method: "GET",
		url: url,
		onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
		onload: function(response){
			if (response.readyState !== 4 || response.status !== 200) return;
			show_channels_html(response.responseText,append);
			if (!append){
				show_channels(true);
			}
		}
	});

}

function parse_channel_id(img_src){
	var myregexp = /poster\/([\w\-]+)\.png/i;
	var match = myregexp.exec(img_src);
	if (match != null) {
		result = match[1];
	} else {
		result = "";
	}
	return result;
}

const inlineBase64Images = nodes => {

	let imgs = Array.from(nodes).filter(node => node.matches('img'))

	return Promise.all(imgs.map(img => new Promise(res => {

			GM_xmlhttpRequest({
				method: 'GET',
				url: img.src,
				responseType: 'blob',
				onload: function(xhr) {

					if (xhr.status !== 200) return res(null)

					let headers = {}
					for (let header of xhr.responseHeaders.trim().split(/\n/)) {
						let parts = header.split(':')
						if (parts.length === 1) continue
						headers[parts[0].trim()] = parts[1].trim()
					}

					let type = headers['Content-Type']

					if (!/^image\//.test(type)) {
						if (/\.jpe?g$/i.test(img.src)) type = 'image/jpeg'
						if (/\.png$/i.test(img.src)) type = 'image/png'
						if (/\.gif$/i.test(img.src)) type = 'image/png'
					}

					if (!/^image\//.test(type)) {
						console.error(`Can't process "${img.src}" because its type is "${type}"`)
						return res(null)
					}

					let imgEl = new Image()
					imgEl.onload = function() {

						let canvas = document.createElement('canvas');
						canvas.width = imgEl.naturalWidth;
						canvas.height = imgEl.naturalHeight;

						try {
							canvas.getContext('2d').drawImage(imgEl, 0, 0)
							img.src = canvas.toDataURL(type, 0.7)
							res(img)
						} catch (e) {
							console.error(`Can't draw "${img.src}" on a canvas`, e)
							res(null)
						}

					}
					imgEl.onerror = () => res(null)
					imgEl.src = window.URL.createObjectURL(xhr.response)

				}
			})

		})))
		// At this point, imgs is an array of all the <img> tags of the page
		// who had their content base64'd. Some elements might be null if
		// XHR or canvas conversion failed on them. Filter them out.
		.then(imgs => Promise.resolve(imgs.filter(img => !!img)))

}

function inlineBase64Style (element) {

	var img_urls = [];
	var myregexp = /url\s*\(\s*['"]?(https?:\/\/[^'")]+)['"]?\s*\)/ig;
	var match = myregexp.exec(element.innerHTML);
	while (match != null) {
		// matched text: match[0]
		// match start: match.index
		// capturing group n: match[n]
		img_urls.push(match[1]);
		match = myregexp.exec(element.innerHTML);
	}
	var img_url="";
	for (var i = 0; i < img_urls.length; i++) {
		img_url=img_urls[i];
		GM_xmlhttpRequest({
			method: 'GET',
			url: img_url,
			responseType: 'blob',
			onload: function(xhr) {

				if (xhr.status !== 200) return null;

				let headers = {}
				for (let header of xhr.responseHeaders.trim().split(/\n/)) {
					let parts = header.split(':')
					if (parts.length === 1) continue
					headers[parts[0].trim()] = parts[1].trim()
				}

				let type = headers['Content-Type']

				if (!/^image\//.test(type)) {
					if (/\.jpe?g$/i.test(img_url)) type = 'image/jpeg'
					if (/\.png$/i.test(img_url)) type = 'image/png'
					if (/\.gif$/i.test(img_url)) type = 'image/png'
				}

				if (!/^image\//.test(type)) {
					console.error(`Can't process "${img_url}" because its type is "${type}"`)
					return null;
				}

				let imgEl = new Image()
				imgEl.onload = function() {

					let canvas = document.createElement('canvas');
					canvas.width = imgEl.naturalWidth;
					canvas.height = imgEl.naturalHeight;

					try {
						canvas.getContext('2d').drawImage(imgEl, 0, 0);
						new_img_url = canvas.toDataURL(type, 0.7);
						//console.log(img_url); console.log(new_img_url); // for debug
						element.innerHTML=element.innerHTML.replace(img_url,new_img_url);
					} catch (e) {
						console.error(`Can't draw "${img_url}" on a canvas`, e)
						return null;
					}

				}
				imgEl.src = window.URL.createObjectURL(xhr.response)
			}
		});
	}
}


function show_channels_html(responseText,append){
	//append=false; // for debugging.
	if (!append){
		channels=[];
	}
	channels_new=[]
	// parse channels
	var response_parsed=$.parseHTML(responseText);
	var canlitv_list = $('li.canlitvlist', response_parsed);
	canlitv_list.each(function() {
		channel_id=parse_channel_id($( this ).find("img").prop("src"));
		channel_name=$( this ).find("a").attr("title");
		channel_logo=$( this ).find("img").prop("src");
		channel_url=$( this ).find("a").prop("href");
		channels_new.push({channel_id:channel_id, channel_name:channel_name, channel_logo:channel_logo, channel_url:channel_url});
	});
	channels=channels.concat(channels_new);
	//console.log(channels); // for debug
	var css = [
	"ul#icerik"+RANDOM+" {",
	"	display: table;",
	"}",
	"ul#icerik"+RANDOM+", li.canlitvlist"+RANDOM+" {",
	"	list-style: none;",
	"}",
	"ul#icerik"+RANDOM+">li.canlitvlist"+RANDOM+" {",
	"	float: left;",
	"	width: 200px;",
	"	margin: 5px 9px;",
	"}",
	".icerikbaslik"+RANDOM+" {",
	"	background-color: #efefef;",
	"	padding: 5px;",
	"	border-top: 1px solid #abd541;",
	"	border-bottom: 1px solid #abd541;",
	"	color: #606056;",
	"	text-align: center;",
	"	overflow: hidden;",
	"	white-space: nowrap;",
	"	text-overflow: ellipsis;",
	"}",
	".canlitvlist"+RANDOM+" a {",
	"	display: block;",
	"	text-align: center;",
	"}",
	".canlitvlist"+RANDOM+" img {",
	"	width: 82px !important;",
	"	margin: 10px 0px;",
	"	display: inline;",
	"}"
	].join("\n");
	addGlobalStyle(css, true, "canli_tv_style");
	// prepare html for channel list
	if (append){
		var channels_html = "";
	} else {
		var channels_html = "<ul id='icerik"+RANDOM+"'>";
	}
	for (var i = 0; i < channels_new.length; i++) {
		channels_html +='<li class="canlitvlist'+RANDOM+'"><a href="javascript:void(0)" channel_id="'+channels_new[i].channel_id+'" original_href="'+channels_new[i].channel_url+'" title="'+channels_new[i].channel_name+'">'
		+'<img src="'+channels_new[i].channel_logo+'" alt="'+channels_new[i].channel_name+'" class="block">'
		+'<div class="icerikbaslik'+RANDOM+'">'+channels_new[i].channel_name+'</div></a>'
		+'</li>';
	}
	if (append){
		channels_html += "";
	} else {
		channels_html += "</ul>";
	}
	// show channel list html
	if (append) {
		$("#icerik"+RANDOM).append(channels_html);
	} else {
		$("#"+cnt_id).append(channels_html);
	}
	fix_dialog_size();
	if (debug_developer===1) {
		$('li.canlitvlist'+RANDOM+' > a:not([processed])').each(function () {
			fetch_channel_url(this,true);
		});
	}
	$('li.canlitvlist'+RANDOM+' > a:not([processed])').click(function (event) {
		fetch_channel_url(this,false);
	});
	$('li.canlitvlist'+RANDOM+' > a').each(function(){
		$(this).attr("processed", "yes");
	});
	let startTime = +new Date
	inlineBase64Images(document.querySelectorAll('li.canlitvlist'+RANDOM+' img:not([processed])')).then(imgs => {
		console.log(imgs.length + ' images converted in ' + ((+new Date) - startTime) / 1000 + 's')
	})
	$('li.canlitvlist'+RANDOM+' img').each(function(){
		$(this).attr("processed", "yes");
	});
}

function get_channel_data(channel_id, data_name){
	for (var i = 0; i < channels.length; i++) {
		if (channels[i].channel_id == channel_id){
			return channels[i][data_name];
		}
	}
}

function fetch_channel_url(element,debug){
	clearTimeout(timer1);
	if (typeof debug === "undefined"){
		var debug=false;
	}
	// get player url
	if (typeof element === "string"){
		var channel_id = element;
	} else {
		var channel_id = $(element).attr("channel_id");
	}
	debugLog("DEBUG: Channel Selected. ID = "+channel_id);
	var channel_name = get_channel_data(channel_id, "channel_name");
	var channel_logo = get_channel_data(channel_id, "channel_logo");
	no_1st_fetch = false;
	no_2nd_fetch = false;
	if ( channel_id === "haberturk-tv" ) {
		var channel_url = "http://www.haberturk.com/canliyayin";
		fetch_haberturk(channel_id,channel_url,debug);
		no_1st_fetch=true;
		no_2nd_fetch=true;
/*	} else if ( channel_id === "ulusal-kanal" ) {
		var channel_url = "https://www.dailymotion.com/video/x6c7r09";
		//var channel_url = "https://www.dailymotion.com/video/x3b68jn"; // for testing DM API
		fetch_dailymotion(channel_id,channel_url,debug);
		no_1st_fetch = true;
		no_2nd_fetch = true;*/
	} else {
		var channel_url = get_channel_data(channel_id, "channel_url");
	}
	debugLog("DEBUG: Channel Selected. URL = "+channel_url);
	if (!debug){
		$("#"+cnt_id).html("<div id='please_wait_"+RANDOM+"' current_channel_id='"+channel_id+"'>LUTFEN BEKLEYINIZ...</div>");
	}
	//$("#"+cnt_id).append(channel_url); //for debugging.
	if (no_1st_fetch === true){
		if (no_2nd_fetch===false){
			fetch_iframe_url(channel_id, channel_url,debug);
		}
		return;
	}
	GM_xmlhttpRequest({
		method: "GET",
		url: channel_url,
		onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
		onload: function(response){
			if (response.readyState !== 4 || response.status !== 200) return;
			fetch_iframe_url(channel_id, response.responseText,debug);
		}
	});
}

String.prototype.stripSlashes = function(){
    return this.replace(/\\(.)/mg, "$1");
}

function fetch_haberturk(channel_id, channel_url,debug){
	debugLog("DEBUG: fetching URL for "+channel_id+": "+channel_url);
	// haberturk
	GM_xmlhttpRequest({
		method: "GET",
		url: channel_url,
		onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
		onload: function(response){
			if (response.readyState !== 4 || response.status !== 200) return;
			var myregexp_ht = /"ht_stream_m3u8"\s*:\s*"([^"]+)"\s*,/i;
			var match_ht = myregexp_ht.exec(response.responseText);
			if (match_ht != null) {
				result = match_ht[1].stripSlashes();
				get_stream_url(channel_id, result,debug);
				return;
			}
		}
	});
}

function getDailyMotionId(url) {
	//return url.substring(url.lastIndexOf('/') + 1); // old buggy method.
	var m = url.match(/^.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/);
	if (m !== null) {
		if (m[4] !== undefined) {
			return m[4];
		}
		return m[2];
	}
	return null;
}

function fetch_dailymotion(channel_id, channel_url,debug){
	var video_id = getDailyMotionId(channel_url);
	if (video_id === null){
		debugLog("DEBUG: fetch_dailymotion() couldnt find video id");
		return ;
	}
	var meta_url = "https://www.dailymotion.com/player/metadata/video/" + video_id;
	debugLog("DEBUG: fetching URL for "+channel_id+": "+meta_url);
	// dailymotion
	GM_xmlhttpRequest({
		method: "GET",
		url: meta_url,
		onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
		onload: function(response){
			if (response.readyState !== 4 || response.status !== 200) return;
			//var myregexp_dm = /"url"\s*:\s*"([^"]+live[^"]+)"/i;
			//var match_dm = myregexp_dm.exec(response.responseText);
			//if (match_dm != null) {
				//result = match_dm[1].stripSlashes();
				get_stream_url(channel_id, JSON.parse(response.responseText),debug);
				return;
			//}
		}
	});
}

function fetch_iframe_url(channel_id, responseText,debug){
	var myregexp = /src="(http:\/\/(?:www\.)?canlitv\.me\/yayin\.php\?kanal=[\w\-]+&security=[\w]+)"/i;
	var match = myregexp.exec(responseText);
	if (match != null) {
		result = match[1];
	} else if (no_2nd_fetch===true) {
		result = responseText;
		get_stream_url(channel_id, result,debug);
		return;
	} else {
		debugLog("DEBUG: couldnt find iframe URL for "+channel_id+"!");
		return;
	}
	GM_xmlhttpRequest({
		method: "GET",
		url: result,
		headers: { 
			'Referer': 'http://canlitv.me/'
		},
		onerror: function(oEvent){ alert("Error " + oEvent.target.status + " occurred while receiving the document."); },
		onload: function(response){
			if (response.readyState !== 4 || response.status !== 200) return;
			var myregexp = /src=\s*['"]{1}([^'"]+dailymotion[^'"]+)['"]{1}\s+/i;
			var match = myregexp.exec(response.responseText);
			if (match != null) {
				result = match[1];
				fetch_dailymotion(channel_id, result, debug);
			} else {
				get_stream_url(channel_id, response.responseText,debug);
			}
		}
	});
}

function get_stream_url(channel_id, responseText,debug_only) {
	if (typeof responseText === "object" && responseText !== null ) {
		var iframe = false;
		if (typeof responseText.qualities === "object" && typeof responseText.qualities.auto[0] === "object" && typeof responseText.qualities.auto[0].url === "string" ){
			var video_qualities=responseText.qualities;
			var result = video_qualities.auto[0].url;
		} else {
			debugLog("DEBUG: couldnt find stream URL for "+channel_id+"!");
			return;
		}
	} else {
		var myregexp = /file:\s*['"]{1}([^'"]+)['"]{1},/i;
		var match = myregexp.exec(responseText);
		var myregexp2 = /src=\s*['"]{1}([^'"]+)['"]{1}\s+/i;
		var match2 = myregexp2.exec(responseText);
		var result = null;
		var iframe = false;
		if (match != null) {
			result = match[1];
		} else if (match2 !=null) {
			result = match2[1];
			iframe = true;
		} else if (/^(https?|file|ftps?|mailto|javascript|data:image\/[^;]{2,9};):/i.test(responseText)) {
			result = responseText;
			if (/(youtube|vimeo)/i.test(responseText)) {
				iframe = true;
			}
		} else {
			debugLog("DEBUG: couldnt find stream URL for "+channel_id+"!");
			return;
		}
	}
	debugLog("DEBUG: Stream URL = "+result+" ; iframe = "+iframe.toString());
	if (debug_only){
		return;
	}
	show_channel_player(channel_id, result,iframe);
}

function show_channel_player(channel_id, stream_url, iframe){
	// show iframe
	if (typeof iframe === "boolean" && iframe===true){
		new_iframe = CreateIframe();
		document.getElementById(cnt_id).appendChild(new_iframe);
		if (stream_url.includes('www.youtube.com')){
			new_iframe.setAttribute("src", stream_url+'&embedjsapi=1');
		} else {
			new_iframe.setAttribute("src", stream_url);
		}
		new_iframe.setAttribute("current_channel_id", channel_id);
		$("#please_wait_"+RANDOM).remove();
		return;
	}
	// show the player
	//addStyle_external("https://cdnjs.cloudflare.com/ajax/libs/mediaelement/4.2.9/mediaelementplayer.min.css", true);
	$("#"+cnt_id).append('<video current_channel_id="'+channel_id+'" class="mejs__player" id="player_tv'+RANDOM+'" preload="none" controls="controls" style="width: 100%; height:100%;">' +
	'<source src="' + stream_url + '" type="application/x-mpegURL">' +
	'</video>');
	$("#please_wait_"+RANDOM).remove();
	refresh_video($('video#player_tv'+RANDOM)[0],stream_url);
	SetVolume(volume);
	fix_dialog_size();
}

function fix_dialog_size(){
	if (typeof size === "number" && size >= 25 && size <= 100){
		set_size=size;
	} else {
		set_size=90;
	}
	$( "#"+cnt_id ).dialog( "option", "height", $(window).innerHeight()*size/100 );
	$( "#"+cnt_id ).dialog( "option", "width", $(window).innerWidth()*size/100 );
}

function iframe_loaded() {
	$('#EXT_FRAME')[0].contentWindow.postMessage("abuzitdin","https://www.canlitv.plus");
}

function decide_referer_from_url(stream_url){
	if (stream_url.includes("ulusaltv")){
		return "http://www.ulusal.com.tr/canliyayin/canliyayin.php";
	}
	if (stream_url.includes("tvizlehd.com")){
		return "http://canlitv.me/";
	}
	return stream_url;
}

function refresh_video(element,stream_url)
{
    if(Hls.isSupported()) {
		//console.log(Hls.DefaultConfig);
		//return;
        var volkanHlsConfig = Hls.DefaultConfig;
		volkanHlsConfig.liveSyncDurationCount= 3; // edge of live delay, expressed in multiple of EXT-X-TARGETDURATION.
		volkanHlsConfig.liveMaxLatencyDurationCount = 10; // maximum delay allowed from edge of live, expressed in multiple of EXT-X-TARGETDURATION.
		volkanHlsConfig.fragLoadingMaxRetry = 6;
		volkanHlsConfig.manifestLoadingMaxRetry = 6;
		volkanHlsConfig.levelLoadingMaxRetry = 6;
		volkanHlsConfig.maxBufferLength = 30; // guaranteed buffer length in seconds.
		volkanHlsConfig.maxMaxBufferLength = 600; // maximum buffer length in seconds.
        volkanHlsConfig.loader.prototype.loadInternal = function () {
			//console.log(this);
			//return; // for debug only.
			var xhr = void 0,
				context = this.context;
			var stats = this.stats;
			stats.tfirst = 0;
			stats.loaded = 0;
			this.requestTimeout = window.setTimeout(this.loadtimeout_c.bind(this), this.config.timeout);
            var extraheaders = {};
			extraheaders["Referer"] = decide_referer_from_url(this.context.url);
            if (this.context.rangeEnd) {
                extraheaders["Range"] = "bytes=" + this.context.rangeStart + "-" + (this.context.rangeEnd - 1);
            }
            xhr=this.loader = GM_xmlhttpRequest({
                method: "GET",
                url: this.context.url,
                headers: extraheaders,
                responseType: this.context.responseType,
                onreadystatechange: this.readystatechange.bind(this),
                onprogress: this.loadprogress_c.bind(this),
				onerror: this.loaderror_c.bind(this),
            });
			//console.log(this.loader); // for debug only.
        };
		//console.log(readystatechangeHook.toString());
		//console.log(volkanHlsConfig.loader.prototype.loadtimeout.toString()); // for debug.
        volkanHlsConfig.loader.prototype.loadprogress_c = function (t) {
			debugLog("XHR load progress called..");
            arguments[0].responseURL = arguments[0].finalUrl;
            var getResponseHeader = function(header) {
				var value = null;
				if (this.responseHeaders) {
					var regex = new RegExp('^'+header+": (.*)$","igm");
					var match = regex.exec(this.responseHeaders);
					var result = [];
					while (match != null) {
						// matched text: match[i]
						result.push(match[1]);
						match = regex.exec(this.responseHeaders);
					}
					if (result.length>0){
						//console.log(result); // for debug
						value=result.join(", ");
					}
				}
				// Return the value
				return value;
			};
            arguments[0].getResponseHeader = getResponseHeader.bind(arguments[0]);
            arguments[0].currentTarget = arguments[0];
			this.loadprogress.apply(this,arguments);
		};
        volkanHlsConfig.loader.prototype.loaderror_c = function () {
			debugLog("XHR load error called..");
		};
        var loadtimeoutHook = volkanHlsConfig.loader.prototype.loadtimeout;
        volkanHlsConfig.loader.prototype.loadtimeout_c = function () {
			debugLog("XHR load timeout called..");
			return loadtimeoutHook.apply(this);
		};
        var readystatechangeHook = volkanHlsConfig.loader.prototype.readystatechange;
		volkanHlsConfig.loader.prototype.readystatechange = function () {
			debugLog("XHR ready state change called..");
			//console.log(this); // for debug only.
			//console.log(arguments); // for debug only.
			//return ; // for debug only.
            arguments[0].responseURL = arguments[0].finalUrl;
            var getResponseHeader = function(header) {
				var value = null;
				if (this.responseHeaders) {
					var regex = new RegExp('^'+header+": (.*)$","igm");
					var match = regex.exec(this.responseHeaders);
					var result = [];
					while (match != null) {
						// matched text: match[i]
						result.push(match[1]);
						match = regex.exec(this.responseHeaders);
					}
					if (result.length>0){
						//console.log(result); // for debug
						value=result.join(", ");
					}
				}
				// Return the value
				return value;
			};
            arguments[0].getResponseHeader = getResponseHeader.bind(arguments[0]);
            arguments[0].currentTarget = arguments[0];
            return readystatechangeHook.apply(this, arguments);
        };
        my_hls = new Hls(volkanHlsConfig);
		// bind them together
		my_hls.attachMedia(element);
		my_hls.on(Hls.Events.MEDIA_ATTACHED, function() {
			debugLog("video and hls.js are now bound together !");
			my_hls.loadSource(stream_url);
			my_hls.on(Hls.Events.MANIFEST_PARSED, function(event, data) {
				debugLog("manifest loaded, found " + data.levels.length + " quality level");
				element.play();
			});
			my_hls.on(Hls.Events.ERROR, function(event, data) {
				if (data.fatal) {
					debugLog("fatal error encountered, refresh dialog");
					timer1 = setTimeout(refresh_dialog.bind(this),3000);
					return; // other recovery methods are mostly useless
					switch (data.type) {
						case Hls.ErrorTypes.NETWORK_ERROR:
							// try to recover network error
							debugLog("fatal network error encountered, try to recover");
							my_hls.startLoad();
							break;
						case Hls.ErrorTypes.MEDIA_ERROR:
							debugLog("fatal media error encountered, try to recover");
							my_hls.recoverMediaError();
							break;
						default:
							// cannot recover
							debugLog("fatal other error encountered, can not recover");
							my_hls.destroy();
							break;
					}
				}
			});
		});
		element.onpause = function() {
			debugLog("The video has been paused");
			//my_hls.stopLoad();
		}
		element.onplay = function() {
			debugLog("The video has started to play");
			//my_hls.startLoad();
		}
	}
}
function inside_frame(event) {
	$("a").attr("target","_top");
	$("header, div.ustrenk, footer").hide();
}

GM_registerMenuCommand("CANLI TV IZLE", show_channels);

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
	if (event.data==="abuzitdin"){
		inside_frame();
	}
}