# -*- coding: utf-8 -*-
# script.module.python.koding.aio
# Python Koding AIO (c) by TOTALREVOLUTION LTD (support@trmc.freshdesk.com)
# Python Koding AIO is licensed under a
# Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.
# You should have received a copy of the license along with this
# work. If not, see http://creativecommons.org/licenses/by-nc-nd/4.0.
# Please make sure you've read and understood the license, this code can NOT be used commercially
# and it can NOT be modified and redistributed. If you're found to be in breach of this license
# then any affected add-ons will be blacklisted and will not be able to work on the same system
# as any other add-ons which use this code. Thank you for your cooperation.
import os
import requests
import shutil
import xbmc
import xbmcgui
from guitools import Show_Busy
from systemtools import Last_Error
dp = xbmcgui.DialogProgress()
check_started = xbmc.translatePath('special://profile/addon_data/script.module.python.koding.aio/temp/playback_in_progress')
#----------------------------------------------------------------
# TUTORIAL #
def Check_Playback(ignore_dp=False,timeout=10):
"""
This function will return true or false based on video playback. Simply start a stream
(whether via an add-on, direct link to URL or local storage doesn't matter), the code will
then work out if playback is successful. This uses a number of checks and should take into
account all potential glitches which can occur during playback. The return should happen
within a second or two of playback being successful (or not).
CODE: Check_Playback()
AVAILABLE PARAMS:
ignore_dp - By default this is set to True but if set to False
this will ignore the DialogProgress window. If you use a DP while
waiting for the stream to start then you'll want to set this True.
Please bare in mind the reason this check is in place and enabled
by default is because some streams do bring up a DialogProgress
when initiated (such as f4m proxy links) and disabling this check
in those circumstances can cause false positives.
timeout - This is the amount of time you want to allow for playback
to start before sending back a response of False. Please note if
ignore_dp is set to True then it will also add a potential 10s extra
to this amount if a DialogProgress window is open. The default setting
for this is 10s.
EXAMPLE CODE:
xbmc.Player().play('http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov')
isplaying = koding.Check_Playback()
if isplaying:
dialog.ok('PLAYBACK SUCCESSFUL','Congratulations, playback was successful')
xbmc.Player().stop()
else:
dialog.ok('PLAYBACK FAILED','Sorry, playback failed :(')
~"""
if not ignore_dp:
isdialog = True
counter = 1
# Check if the progress window is active and wait for playback
while isdialog and counter < 60:
if xbmc.getCondVisibility('Window.IsActive(progressdialog)'):
try:
if dp.iscanceled():
dp.close()
break
except:
pass
xbmc.log('### Current Window: %s' % xbmc.getInfoLabel('System.CurrentWindow'))
xbmc.log('### Current XML: %s' % xbmc.getInfoLabel('Window.Property(xmlfile)'))
xbmc.log('### Progress Dialog active, sleeping for %s seconds' % counter)
xbmc.sleep(1000)
if xbmc.getCondVisibility('Window.IsActive(progressdialog)') or (xbmc.getInfoLabel('Window.Property(xmlfile)') == 'DialogProgress.xml'):
isdialog = True
else:
isdialog = False
counter += 1
xbmc.log('counter: %s' % counter)
# Given the DialogProgress 10 seconds to finish and it's still up - time to close it
if counter >= 10:
try:
xbmc.log('attempting to send click to close dp')
xbmc.executebuiltin('SendClick()')
if dp.iscanceled():
dp.close()
try:
dp.close()
except:
pass
except:
xbmc.log('### FAILED TO CLOSE DP')
try:
dp.close()
except:
pass
isplaying = xbmc.Player().isPlaying()
counter = 1
if xbmc.Player().isPlayingAudio():
return True
# If xbmc player is not yet active give it some time to initialise
while not isplaying and counter < timeout:
xbmc.sleep(1000)
isplaying = xbmc.Player().isPlaying()
xbmc.log('### XBMC Player not yet active, sleeping for %s seconds' % counter)
counter += 1
success = 0
counter = 0
# If it's playing give it time to physically start streaming then attempt to pull some info
if isplaying:
xbmc.sleep(1000)
while not success and counter < 5:
try:
if xbmc.Player().isPlayingVideo():
infotag = xbmc.Player().getVideoInfoTag()
vidtime = xbmc.Player().getTime()
if vidtime > 0:
success = 1
# If playback doesn't start automatically (buffering) we force it to play
else:
xbmc.log('### Playback active but time at zero, trying to unpause')
xbmc.executebuiltin('PlayerControl(Play)')
xbmc.sleep(2000)
vidtime = xbmc.Player().getTime()
if vidtime > 0:
success = 1
# If no infotag or time could be pulled then we assume playback failed, try and stop the xbmc.player
except:
counter += 1
xbmc.sleep(1000)
# Check if the busy dialog is still active from previous locked up playback attempt
isbusy = xbmc.getCondVisibility('Window.IsActive(busydialog)')
counter = 1
while isbusy:
xbmc.log('### Busy dialog active, sleeping for %ss' % counter)
xbmc.sleep(1000)
isbusy = xbmc.getCondVisibility('Window.IsActive(busydialog)')
counter += 1
if counter >= 5:
xbmc.executebuiltin('Dialog.Close(busydialog)')
if not success:
xbmc.executebuiltin('PlayerControl(Stop)')
xbmc.log('### Failed playback, stopped stream')
return False
else:
return True
#----------------------------------------------------------------
# TUTORIAL #
def Last_Played():
"""
Return the link of the last played (or currently playing) video.
This differs to the built in getPlayingFile command as that only shows details
of the current playing file, these details can differ to the url which was
originally sent through to initiate the stream. This Last_Played function
directly accesses the database to get the REAL link which was initiated and
will even return the plugin path if it's been played through an external add-on.
CODE: Last_Played()
EXAMPLE CODE:
if koding.Play_Video('http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov'):
xbmc.sleep(3000)
xbmc.Player().stop()
last_vid = Last_Played()
dialog.ok('VIDEO LINK','The link we just played is:\n\n%s'%last_vid)
else:
dialog.ok('PLAYBACK FAILED','Sorry this video is no longer available, please try using a different video link.')
~"""
from database import DB_Query
from filetools import DB_Path_Check
from vartools import Decode_String
db_path = DB_Path_Check('MyVideos')
sql = "SELECT files.strFilename as mystring, path.strPath as mybase FROM files JOIN path ON files.idPath=path.idPath ORDER BY files.lastPlayed DESC LIMIT 1"
results = DB_Query(db_path, sql)
try:
if Decode_String(results[0]['mybase']).startswith('plugin://'):
return Decode_String(results[0]['mystring'])
else:
return Decode_String(results[0]['mybase']+results[0]['mystring'])
except:
return False
#----------------------------------------------------------------
# TUTORIAL #
def Link_Tester(video='', local_check=True, proxy_list=None, proxy_url='https://free-proxy-list.net/', ip_col=0, port_col=1, table=0):
"""
Send through a link and test whether or not it's playable on other devices.
Many links include items in the query string which lock the content down to your
IP only so what may open fine for you may not open for anyone else!
This function will attempt to load the page using a proxy. If when trying to access
the link via a proxy the header size and content-type match then we assume the
link will play on any device. This is not fool proof and could potentially return
false positives depending on the security used on the website being accessed.
The return you'll get is a dictionary of the following items:
'plugin_path' - This will have the path for a plugin, it means the stream was
originally passed through an add-on to get the final link. If this is not set
to None then it "should" work on any device so long as that add-on is installed
(e.g. YouTube).
'url' - This is the final resolved url which Kodi was playing, you need to check
the status though to find out whether or not that link is locked to your IP only.
'status' - This will return one of the following status codes:
good - The link should work on all IPs.
bad_link - The link was not valid, won't even play on your current Kodi setup.
proxy_fail - None of the proxies sent through worked.
locked - The url only works on this device, if this is the case consider using
the plugin_path which should generally work on all devices (although this does
depend on how the developer of that add-on coded up their add-on).
CODE: Link_Tester([proxy_list, url, ip_col, port_col, table])
AVAILABLE PARAMS:
video - This is the url of the video you want to check
local_check - By default this is set to True and this function will first of
all attempt to play the video locally with no proxy just to make sure the
link is valid in the first place. If you want to skip this step then set
this to False.
proxy_list - If you already have a list of proxies you want to test with
send them through in the form of a list of dictionaries. Use the following
format: [{"ip":"0.0.0.0","port":"80"},{"ip":"127.0.0.1","port":"8080"}]
proxy_url - If you want to scrape for online proxies and loop through until a
working one has been found you can set the url here. If using this then
proxy_list can be left as the default (None). If you open this Link_Tester
function with no params the defaults are setup to grab from:
free-proxy-list.net but there is no guarantee this will always
work, the website may well change it's layout/security over time.
ip_col - If you've sent through a proxy_url then you'll need to set a column number
for where in the table the IP address is stored. The default is 0
port_col - If you've sent through a proxy_url then you'll need to set a column number
for where in the table the port details are stored. The default is 1
table - If you've sent through a proxy_url then you'll need to set a table number.
The default is 0 - this presumes we need to use the first html table found on the
page, if you require a different table then alter accordingly - remember zero is the
first instance so if you want the 3rd table on the page you would set to 2.
EXAMPLE CODE:
vid_test = Link_Tester(video='http://totalrevolution.tv/videos/python_koding/Browse_To_Folder.mov')
if vid_test['status'] == 'bad_link':
dialog.ok('BAD LINK','The link you sent through cannot even be played on this device let alone another one!')
elif vid_test['status'] == 'proxy_fail':
dialog.ok('PROXIES EXHAUSTED','It was not possible to get any working proxies as a result it\'s not possible to fully test whether this link will work on other devices.')
elif vid_test['status'] == 'locked':
dialog.ok('NOT PLAYABLE','Although you can play this link locally the tester was unable to play it when using a proxy so this is no good.')
if vid_test['plugin_path']:
dialog.ok('THERE IS SOME GOOD NEWS!','Although the direct link for this video won\'t work on other IPs it "should" be possible to open this using the following path:\n[COLOR dodgerblue]%s[/COLOR]'%vid_test['plugin_path'])
else:
dialog.ok('WORKING!!!','Congratulations this link can be resolved and added to your playlist.')
~"""
import random
import urllib
from guitools import Notify
from vartools import Table_Convert
from systemtools import System
# xbmc.executebuiltin('RunScript(special://home/addons/script.module.python.koding.aio/lib/koding/localproxy.py)')
Notify('PLEASE WAIT','Checking Link - Step 1','5000','Video.png')
isplaying = xbmc.Player().isPlaying()
# If video not yet playing try playing it
if not isplaying:
xbmc.Player().play(video)
if Check_Playback(True):
xbmclink = xbmc.Player().getPlayingFile()
active_plugin = System(command='addonid')
plugin_path = System(command='currentpath')
vid_title = ''
title_count = 0
while vid_title == '' and title_count < 10:
vid_title = xbmc.getInfoLabel('Player.Title')
xbmc.sleep(100)
title_count += 1
xbmc.Player().stop()
video_orig = Last_Played()
xbmc.log('VIDEO: %s'%video_orig,2)
if video_orig.startswith('plugin://'):
video = xbmclink
xbmc.log('NEW VIDEO: %s'%video,2)
else:
video = video_orig
r = requests.head(url=video, timeout=5)
orig_header = r.headers
try:
orig_size = orig_header['Content-Length']
except:
orig_size = 0
try:
orig_type = orig_header['Content-Type']
except:
orig_type = ''
proxies = Table_Convert(url=proxy_url, contents={"ip":ip_col,"port":port_col}, table=table)
myproxies = []
used_proxies = []
for item in proxies:
myproxies.append({'http':'http://%s:%s'%(item['ip'],item['port']),'https':'https://%s:%s'%(item['ip'],item['port'])})
success = False
if video_orig.startswith('plugin://'):
dp.create('[COLOR gold]CHECKING PROXIES[/COLOR]','This video is being parsed through another add-on so using the plugin path should work. Now checking the final resolved link...','')
else:
dp.create('[COLOR gold]CHECKING PROXIES[/COLOR]','Please wait...','')
counter = 1
while (not success) and (len(myproxies) > 0):
dp.update(counter/len(myproxies),'Checking proxy %s'%counter)
counter += 1
proxychoice = random.choice( range(0,len(myproxies)) )
currentproxy = myproxies[proxychoice]
# Find a working proxy and play the video through it
try:
xbmc.log(repr(currentproxy),2)
r = requests.head(url=video, proxies=currentproxy, timeout=5)
headers = r.headers
try:
new_size = headers['Content-Length']
except:
new_size = 0
try:
new_type = headers['Content-Type']
except:
new_type = ''
xbmc.log('orig size: %s'%orig_size,2)
xbmc.log('new size: %s'%new_size,2)
xbmc.log('orig type: %s'%orig_type,2)
xbmc.log('new type: %s'%new_type,2)
xbmc.log('VIDEO: %s'%video,2)
if orig_size != 0 and (orig_size==new_size) and (orig_type==new_type):
dp.close()
success = True
except:
xbmc.log('failed with proxy: %s'%currentproxy,2)
myproxies.pop(proxychoice)
if dp.iscanceled():
dp.close()
break
plugin_path = None
if video_orig.startswith('plugin://'):
plugin_path = video_orig
if len(myproxies)==0 and not success:
return {"plugin_path":plugin_path, "url":video, "status":"proxy_fail"}
elif not success:
return {"plugin_path":plugin_path, "url":video, "status":"locked"}
else:
return {"plugin_path":plugin_path, "url":video, "status":"good"}
else:
return {"plugin_path":None, "url":video, "status":"bad_link"}
#----------------------------------------------------------------
# TUTORIAL #
def M3U_Selector(url,post_type='get',header='Stream Selection'):
"""
Send through an m3u/m3u8 playlist and have the contents displayed via a dialog select.
The return will be a dictionary of 'name' and 'url'. You can send through either
a locally stored filepath or an online URL.
This function will try it's best to pull out the relevant playlist details even if the
web page isn't a correctly formatted m3u playlist (e.g. an m3u playlist embedded into
a blog page).
CODE: M3U_Selector(url, [post_type, header])
AVAILABLE PARAMS:
(*) url - The location of your m3u file, this can be local or online
post_type - If you need to use POST rather than a standard query string
in your url set this to 'post', by default it's set to 'get'.
header - This is the header you want to appear at the top of your dialog
selection window, by default it's set to "Stream Selection"
EXAMPLE CODE:
dialog.ok('M3U SELECTOR','We will now call this function using the following url:','','[COLOR dodgerblue]http://totalrevolution.tv/videos/playlists/youtube.m3u[/COLOR]')
# This example uses YouTube plugin paths but any playable paths will work
vid = koding.M3U_Selector(url='http://totalrevolution.tv/videos/playlists/youtube.m3u')
# Make sure there is a valid link returned
if vid:
playback = koding.Play_Video(video=vid['url'], showbusy=False)
if playback:
dialog.ok('SUCCESS!','Congratulations the playback was successful!')
xbmc.Player().stop()
else:
dialog.ok('OOPS!','Looks like something went wrong there, the playback failed. Check the links are still valid.')
~"""
from web import Open_URL
from vartools import Cleanup_String, Find_In_Text
from filetools import Text_File
success = False
if url.startswith('http'):
content = Open_URL(url=url, post_type=post_type, timeout=10)
else:
try:
url = xbmc.translatePath(url)
except:
pass
content = Text_File(url,'r')
if content:
newcontent = content.splitlines()
name_array = []
url_array = []
name = ''
for line in newcontent:
line = line.strip()
# Grab the name of the stream
if line.startswith('#EXT'):
name = line.split(',')
name.pop(0)
name = ''.join(name)
# Grab the url(s) of the stream
if name != '' and line != '' and not line.startswith('#EXT'):
name_array.append(Cleanup_String(name))
line = line.replace('
','').replace('
','').replace('
','')
line = line.replace('