Animeunity: supporto server StreamingCommunityWS
This commit is contained in:
+286
-222
@@ -1,222 +1,286 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Canale per AnimeUnity
|
# Canale per AnimeUnity
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
from lib.requests.sessions import session
|
import cloudscraper, json, copy, inspect
|
||||||
import requests, json, copy, inspect
|
from core import jsontools, support, httptools, filetools
|
||||||
from core import support
|
from platformcode import autorenumber, logger
|
||||||
from platformcode import autorenumber
|
import re
|
||||||
|
import xbmc
|
||||||
host = support.config.get_channel_url()
|
|
||||||
response = support.httptools.downloadpage(host + '/archivio')
|
|
||||||
csrf_token = support.match(response.data, patron='name="csrf-token" content="([^"]+)"').match
|
session = cloudscraper.create_scraper()
|
||||||
headers = {'content-type': 'application/json;charset=UTF-8',
|
|
||||||
'x-csrf-token': csrf_token,
|
host = support.config.get_channel_url()
|
||||||
'Cookie' : '; '.join([x.name + '=' + x.value for x in response.cookies])}
|
response = session.get(host + '/archivio')
|
||||||
|
csrf_token = support.match(response.text, patron='name="csrf-token" content="([^"]+)"').match
|
||||||
|
headers = {'content-type': 'application/json;charset=UTF-8',
|
||||||
@support.menu
|
'x-csrf-token': csrf_token,
|
||||||
def mainlist(item):
|
'Cookie' : '; '.join([x.name + '=' + x.value for x in response.cookies])}
|
||||||
top = [('Ultimi Episodi', ['', 'news'])]
|
|
||||||
|
|
||||||
menu = [('Anime {bullet bold}',['', 'menu', {}, 'tvshow']),
|
@support.menu
|
||||||
('Film {submenu}',['', 'menu', {'type': 'Movie'}]),
|
def mainlist(item):
|
||||||
('TV {submenu}',['', 'menu', {'type': 'TV'}, 'tvshow']),
|
top = [('Ultimi Episodi', ['', 'news'])]
|
||||||
('OVA {submenu} {tv}',['', 'menu', {'type': 'OVA'}, 'tvshow']),
|
|
||||||
('ONA {submenu} {tv}',['', 'menu', {'type': 'ONA'}, 'tvshow']),
|
menu = [('Anime {bullet bold}',['', 'menu', {}, 'tvshow']),
|
||||||
('Special {submenu} {tv}',['', 'menu', {'type': 'Special'}, 'tvshow'])]
|
('Film {submenu}',['', 'menu', {'type': 'Movie'}]),
|
||||||
search =''
|
('TV {submenu}',['', 'menu', {'type': 'TV'}, 'tvshow']),
|
||||||
return locals()
|
('OVA {submenu} {tv}',['', 'menu', {'type': 'OVA'}, 'tvshow']),
|
||||||
|
('ONA {submenu} {tv}',['', 'menu', {'type': 'ONA'}, 'tvshow']),
|
||||||
def menu(item):
|
('Special {submenu} {tv}',['', 'menu', {'type': 'Special'}, 'tvshow'])]
|
||||||
item.action = 'peliculas'
|
search =''
|
||||||
ITA = copy.copy(item.args)
|
return locals()
|
||||||
ITA['title'] = '(ita)'
|
|
||||||
InCorso = copy.copy(item.args)
|
def menu(item):
|
||||||
InCorso['status'] = 'In Corso'
|
item.action = 'peliculas'
|
||||||
Terminato = copy.copy(item.args)
|
ITA = copy.copy(item.args)
|
||||||
Terminato['status'] = 'Terminato'
|
ITA['title'] = '(ita)'
|
||||||
itemlist = [item.clone(title=support.typo('Tutti','bold')),
|
InCorso = copy.copy(item.args)
|
||||||
item.clone(title=support.typo('ITA','bold'), args=ITA),
|
InCorso['status'] = 'In Corso'
|
||||||
item.clone(title=support.typo('Genere','bold'), action='genres'),
|
Terminato = copy.copy(item.args)
|
||||||
item.clone(title=support.typo('Anno','bold'), action='years')]
|
Terminato['status'] = 'Terminato'
|
||||||
if item.contentType == 'tvshow':
|
itemlist = [item.clone(title=support.typo('Tutti','bold')),
|
||||||
itemlist += [item.clone(title=support.typo('In Corso','bold'), args=InCorso),
|
item.clone(title=support.typo('ITA','bold'), args=ITA),
|
||||||
item.clone(title=support.typo('Terminato','bold'), args=Terminato)]
|
item.clone(title=support.typo('Genere','bold'), action='genres'),
|
||||||
itemlist +=[item.clone(title=support.typo('Cerca...','bold'), action='search', thumbnail=support.thumb('search'))]
|
item.clone(title=support.typo('Anno','bold'), action='years')]
|
||||||
return itemlist
|
if item.contentType == 'tvshow':
|
||||||
|
itemlist += [item.clone(title=support.typo('In Corso','bold'), args=InCorso),
|
||||||
|
item.clone(title=support.typo('Terminato','bold'), args=Terminato)]
|
||||||
def genres(item):
|
itemlist +=[item.clone(title=support.typo('Cerca...','bold'), action='search', thumbnail=support.thumb('search'))]
|
||||||
support.info()
|
return itemlist
|
||||||
# support.dbg()
|
|
||||||
itemlist = []
|
|
||||||
|
def genres(item):
|
||||||
genres = json.loads(support.match(response.text, patron='genres="([^"]+)').match.replace('"','"'))
|
support.info()
|
||||||
|
# support.dbg()
|
||||||
for genre in genres:
|
itemlist = []
|
||||||
item.args['genres'] = [genre]
|
|
||||||
itemlist.append(item.clone(title=support.typo(genre['name'],'bold'), action='peliculas'))
|
genres = json.loads(support.match(response.text, patron='genres="([^"]+)').match.replace('"','"'))
|
||||||
return support.thumb(itemlist)
|
|
||||||
|
for genre in genres:
|
||||||
def years(item):
|
item.args['genres'] = [genre]
|
||||||
support.info()
|
itemlist.append(item.clone(title=support.typo(genre['name'],'bold'), action='peliculas'))
|
||||||
itemlist = []
|
return support.thumb(itemlist)
|
||||||
|
|
||||||
from datetime import datetime
|
def years(item):
|
||||||
current_year = datetime.today().year
|
support.info()
|
||||||
oldest_year = int(support.match(response.text, patron='anime_oldest_date="([^"]+)').match)
|
itemlist = []
|
||||||
|
|
||||||
for year in list(reversed(range(oldest_year, current_year + 1))):
|
from datetime import datetime
|
||||||
item.args['year']=year
|
current_year = datetime.today().year
|
||||||
itemlist.append(item.clone(title=support.typo(year,'bold'), action='peliculas'))
|
oldest_year = int(support.match(response.text, patron='anime_oldest_date="([^"]+)').match)
|
||||||
return itemlist
|
|
||||||
|
for year in list(reversed(range(oldest_year, current_year + 1))):
|
||||||
|
item.args['year']=year
|
||||||
def search(item, text):
|
itemlist.append(item.clone(title=support.typo(year,'bold'), action='peliculas'))
|
||||||
support.info('search', item)
|
return itemlist
|
||||||
if not item.args:
|
|
||||||
item.args = {'title':text}
|
|
||||||
else:
|
def search(item, text):
|
||||||
item.args['title'] = text
|
support.info('search', item)
|
||||||
item.search = text
|
if not item.args:
|
||||||
|
item.args = {'title':text}
|
||||||
try:
|
else:
|
||||||
return peliculas(item)
|
item.args['title'] = text
|
||||||
# Continua la ricerca in caso di errore
|
item.search = text
|
||||||
except:
|
|
||||||
import sys
|
try:
|
||||||
for line in sys.exc_info():
|
return peliculas(item)
|
||||||
support.info('search log:', line)
|
# Continua la ricerca in caso di errore
|
||||||
return []
|
except:
|
||||||
|
import sys
|
||||||
|
for line in sys.exc_info():
|
||||||
def newest(categoria):
|
support.info('search log:', line)
|
||||||
support.info(categoria)
|
return []
|
||||||
itemlist = []
|
|
||||||
item = support.Item()
|
|
||||||
item.url = host
|
def newest(categoria):
|
||||||
|
support.info(categoria)
|
||||||
try:
|
itemlist = []
|
||||||
itemlist = news(item)
|
item = support.Item()
|
||||||
|
item.url = host
|
||||||
if itemlist[-1].action == 'news':
|
|
||||||
itemlist.pop()
|
try:
|
||||||
# Continua la ricerca in caso di errore
|
itemlist = news(item)
|
||||||
except:
|
|
||||||
import sys
|
if itemlist[-1].action == 'news':
|
||||||
for line in sys.exc_info():
|
itemlist.pop()
|
||||||
support.info(line)
|
# Continua la ricerca in caso di errore
|
||||||
return []
|
except:
|
||||||
|
import sys
|
||||||
return itemlist
|
for line in sys.exc_info():
|
||||||
|
support.info(line)
|
||||||
def news(item):
|
return []
|
||||||
support.info()
|
|
||||||
item.contentType = 'episode'
|
return itemlist
|
||||||
itemlist = []
|
|
||||||
import cloudscraper
|
def news(item):
|
||||||
session = cloudscraper.create_scraper()
|
support.info()
|
||||||
|
item.contentType = 'episode'
|
||||||
fullJs = json.loads(support.match(session.get(item.url).text, headers=headers, patron=r'items-json="([^"]+)"', debug=True).match.replace('"','"'))
|
itemlist = []
|
||||||
js = fullJs['data']
|
import cloudscraper
|
||||||
|
session = cloudscraper.create_scraper()
|
||||||
for it in js:
|
|
||||||
itemlist.append(
|
fullJs = json.loads(support.match(session.get(item.url).text, headers=headers, patron=r'items-json="([^"]+)"').match.replace('"','"'))
|
||||||
item.clone(title= support.typo(it['anime']['title'] + ' - EP. ' + it['number'], 'bold'),
|
js = fullJs['data']
|
||||||
fulltitle=it['anime']['title'],
|
|
||||||
thumbnail=it['anime']['imageurl'],
|
for it in js:
|
||||||
forcethumb = True,
|
itemlist.append(
|
||||||
video_url=it['link'],
|
item.clone(title= support.typo(it['anime']['title'] + ' - EP. ' + it['number'], 'bold'),
|
||||||
plot=it['anime']['plot'],
|
fulltitle=it['anime']['title'],
|
||||||
action='findvideos')
|
thumbnail=it['anime']['imageurl'],
|
||||||
)
|
forcethumb = True,
|
||||||
if 'next_page_url' in fullJs:
|
video_url=it['scws_id'],
|
||||||
itemlist.append(item.clone(title=support.typo(support.config.get_localized_string(30992), 'color kod bold'),thumbnail=support.thumb(), url=fullJs['next_page_url']))
|
plot=it['anime']['plot'],
|
||||||
return itemlist
|
action='findvideos')
|
||||||
|
)
|
||||||
|
if 'next_page_url' in fullJs:
|
||||||
def peliculas(item):
|
itemlist.append(item.clone(title=support.typo(support.config.get_localized_string(30992), 'color kod bold'),thumbnail=support.thumb(), url=fullJs['next_page_url']))
|
||||||
support.info()
|
return itemlist
|
||||||
itemlist = []
|
|
||||||
|
|
||||||
page = item.page if item.page else 0
|
def peliculas(item):
|
||||||
item.args['offset'] = page * 30
|
support.info()
|
||||||
|
itemlist = []
|
||||||
order = support.config.get_setting('order', item.channel)
|
|
||||||
if order:
|
page = item.page if item.page else 0
|
||||||
order_list = [ "Standard", "Lista A-Z", "Lista Z-A", "Popolarità", "Valutazione" ]
|
item.args['offset'] = page * 30
|
||||||
item.args['order'] = order_list[order]
|
|
||||||
|
order = support.config.get_setting('order', item.channel)
|
||||||
payload = json.dumps(item.args)
|
if order:
|
||||||
records = requests.post(host + '/archivio/get-animes', headers=headers, data=payload).json()['records']
|
order_list = [ "Standard", "Lista A-Z", "Lista Z-A", "Popolarità", "Valutazione" ]
|
||||||
|
item.args['order'] = order_list[order]
|
||||||
for it in records:
|
|
||||||
lang = support.match(it['title'], patron=r'\(([It][Tt][Aa])\)').match
|
payload = json.dumps(item.args)
|
||||||
title = support.re.sub(r'\s*\([^\)]+\)', '', it['title'])
|
records = session.post(host + '/archivio/get-animes', headers=headers, data=payload).json()['records']
|
||||||
|
|
||||||
if 'ita' in lang.lower(): language = 'ITA'
|
for it in records:
|
||||||
else: language = 'Sub-ITA'
|
logger.debug(jsontools.dump(it))
|
||||||
|
lang = support.match(it['title'], patron=r'\(([It][Tt][Aa])\)').match
|
||||||
itm = item.clone(title=support.typo(title,'bold') + support.typo(language,'_ [] color kod') + (support.typo(it['title_eng'],'_ ()') if it['title_eng'] else ''))
|
title = support.re.sub(r'\s*\([^\)]+\)', '', it['title'])
|
||||||
itm.contentLanguage = language
|
|
||||||
itm.type = it['type']
|
if 'ita' in lang.lower(): language = 'ITA'
|
||||||
itm.thumbnail = it['imageurl']
|
else: language = 'Sub-ITA'
|
||||||
itm.plot = it['plot']
|
|
||||||
itm.url = item.url
|
itm = item.clone(title=support.typo(title,'bold') + support.typo(language,'_ [] color kod') + (support.typo(it['title_eng'],'_ ()') if it['title_eng'] else ''))
|
||||||
|
itm.contentLanguage = language
|
||||||
if it['episodes_count'] == 1:
|
itm.type = it['type']
|
||||||
itm.contentType = 'movie'
|
itm.thumbnail = it['imageurl']
|
||||||
itm.fulltitle = itm.show = itm.contentTitle = title
|
itm.plot = it['plot']
|
||||||
itm.contentSerieName = ''
|
itm.url = item.url
|
||||||
itm.action = 'findvideos'
|
|
||||||
itm.video_url = it['episodes'][0]['link']
|
if it['episodes_count'] == 1:
|
||||||
|
itm.contentType = 'movie'
|
||||||
else:
|
itm.fulltitle = itm.show = itm.contentTitle = title
|
||||||
itm.contentType = 'tvshow'
|
itm.contentSerieName = ''
|
||||||
itm.contentTitle = ''
|
itm.action = 'findvideos'
|
||||||
itm.fulltitle = itm.show = itm.contentSerieName = title
|
itm.video_url = it['episodes'][0]['scws_id']
|
||||||
itm.action = 'episodios'
|
|
||||||
itm.episodes = it['episodes'] if 'episodes' in it else it['link']
|
else:
|
||||||
itm.video_url = item.url
|
itm.contentType = 'tvshow'
|
||||||
|
itm.contentTitle = ''
|
||||||
itemlist.append(itm)
|
itm.fulltitle = itm.show = itm.contentSerieName = title
|
||||||
|
itm.action = 'episodios'
|
||||||
autorenumber.start(itemlist)
|
itm.episodes = it['episodes'] if 'episodes' in it else it['scws_id']
|
||||||
if len(itemlist) >= 30:
|
itm.video_url = item.url
|
||||||
itemlist.append(item.clone(title=support.typo(support.config.get_localized_string(30992), 'color kod bold'), thumbnail=support.thumb(), page=page + 1))
|
|
||||||
|
itemlist.append(itm)
|
||||||
return itemlist
|
|
||||||
|
autorenumber.start(itemlist)
|
||||||
def episodios(item):
|
if len(itemlist) >= 30:
|
||||||
support.info()
|
itemlist.append(item.clone(title=support.typo(support.config.get_localized_string(30992), 'color kod bold'), thumbnail=support.thumb(), page=page + 1))
|
||||||
itemlist = []
|
|
||||||
title = 'Parte ' if item.type.lower() == 'movie' else 'Episodio '
|
return itemlist
|
||||||
for it in item.episodes:
|
|
||||||
itemlist.append(
|
def episodios(item):
|
||||||
item.clone(title=support.typo(title + it['number'], 'bold'),
|
support.info()
|
||||||
episode = it['number'],
|
itemlist = []
|
||||||
fulltitle=item.title,
|
title = 'Parte ' if item.type.lower() == 'movie' else 'Episodio '
|
||||||
show=item.title,
|
for it in item.episodes:
|
||||||
contentTitle='',
|
itemlist.append(
|
||||||
contentSerieName=item.contentSerieName,
|
item.clone(title=support.typo(title + it['number'], 'bold'),
|
||||||
thumbnail=item.thumbnail,
|
episode = it['number'],
|
||||||
plot=item.plot,
|
fulltitle=item.title,
|
||||||
action='findvideos',
|
show=item.title,
|
||||||
contentType='episode',
|
contentTitle='',
|
||||||
video_url=it['link']))
|
contentSerieName=item.contentSerieName,
|
||||||
|
thumbnail=item.thumbnail,
|
||||||
if inspect.stack()[1][3] not in ['find_episodes']:
|
plot=item.plot,
|
||||||
autorenumber.start(itemlist, item)
|
action='findvideos',
|
||||||
support.videolibrary(itemlist, item)
|
contentType='episode',
|
||||||
support.download(itemlist, item)
|
video_url=it['scws_id']))
|
||||||
return itemlist
|
|
||||||
|
if inspect.stack()[1][3] not in ['find_episodes']:
|
||||||
|
autorenumber.start(itemlist, item)
|
||||||
def findvideos(item):
|
support.videolibrary(itemlist, item)
|
||||||
support.info()
|
support.download(itemlist, item)
|
||||||
if not 'vvvvid' in item.video_url:
|
return itemlist
|
||||||
return support.server(item,itemlist=[item.clone(title=support.config.get_localized_string(30137), url=item.video_url, server='directo', action='play')])
|
|
||||||
else:
|
|
||||||
return support.server(item, item.video_url)
|
def findvideos(item):
|
||||||
|
# def calculateToken():
|
||||||
|
# from time import time
|
||||||
|
# from base64 import b64encode as b64
|
||||||
|
# import hashlib
|
||||||
|
# o = 48
|
||||||
|
# n = support.match('https://au-1.scws-content.net/get-ip').data
|
||||||
|
# i = 'Yc8U6r8KjAKAepEA'
|
||||||
|
# t = int(time() + (3600 * o))
|
||||||
|
# l = '{}{} {}'.format(t, n, i)
|
||||||
|
# md5 = hashlib.md5(l.encode())
|
||||||
|
# s = '?token={}&expires={}'.format(b64(md5.digest()).decode().replace('=', '').replace('+', "-").replace('\\', "_"), t)
|
||||||
|
# return s
|
||||||
|
# token = calculateToken()
|
||||||
|
|
||||||
|
# url = 'https://streamingcommunityws.com/master/{}{}'.format(item.video_url, token)
|
||||||
|
|
||||||
|
# # support.dbg()
|
||||||
|
|
||||||
|
# m3u8_original = httptools.downloadpage(url, CF=False).data
|
||||||
|
|
||||||
|
# m_video = re.search(r'\.\/video\/(\d+p)\/playlist.m3u8', m3u8_original)
|
||||||
|
# video_res = m_video.group(1)
|
||||||
|
# m_audio = re.search(r'\.\/audio\/(\d+k)\/playlist.m3u8', m3u8_original)
|
||||||
|
# audio_res = m_audio.group(1)
|
||||||
|
|
||||||
|
# # https://streamingcommunityws.com/master/5957?type=video&rendition=480p&token=wQLowWskEnbLfOfXXWWPGA&expires=1623437317
|
||||||
|
# video_url = 'https://streamingcommunityws.com/master/{}{}&type=video&rendition={}'.format(item.video_url, token, video_res)
|
||||||
|
# audio_url = 'https://streamingcommunityws.com/master/{}{}&type=audio&rendition={}'.format(item.video_url, token, audio_res)
|
||||||
|
|
||||||
|
# m3u8_original = m3u8_original.replace( m_video.group(0), video_url )
|
||||||
|
# m3u8_original = m3u8_original.replace( m_audio.group(0), audio_url )
|
||||||
|
|
||||||
|
# file_path = 'special://temp/animeunity.m3u8'
|
||||||
|
|
||||||
|
# filetools.write(xbmc.translatePath(file_path), m3u8_original, 'w')
|
||||||
|
|
||||||
|
# return support.server(item, itemlist=[item.clone(title=support.config.get_localized_string(30137), url=file_path, manifest = 'hls', server='directo', action='play')])
|
||||||
|
# item.url=item.video_url
|
||||||
|
|
||||||
|
directLink = False
|
||||||
|
if item.video_url == None:
|
||||||
|
if item.extra == "tvshow":
|
||||||
|
epnum = item.episode
|
||||||
|
logger.info('it is a episode', epnum)
|
||||||
|
episode = None
|
||||||
|
for ep in item.episodes:
|
||||||
|
if ep["number"] == epnum:
|
||||||
|
episode = ep
|
||||||
|
break
|
||||||
|
if episode == None:
|
||||||
|
logger.warn('cannot found episode')
|
||||||
|
else:
|
||||||
|
item.url = episode["link"]
|
||||||
|
directLink = True
|
||||||
|
|
||||||
|
if directLink:
|
||||||
|
logger.info('try direct link')
|
||||||
|
return support.server(item, itemlist=[item.clone(title=support.config.get_localized_string(30137), url=item.url, server='directo', action='play')])
|
||||||
|
else:
|
||||||
|
return support.server(item, itemlist=[item.clone(title="StreamingCommunityWS", url=str(item.video_url), manifest = 'hls', server='streamingcommunityws', action='play')])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ import re
|
|||||||
|
|
||||||
from core import filetools
|
from core import filetools
|
||||||
from core import httptools
|
from core import httptools
|
||||||
from core import jsontools
|
from core import jsontools, support
|
||||||
from core.item import Item
|
from core.item import Item
|
||||||
from platformcode import config, logger
|
from platformcode import config, logger
|
||||||
from platformcode import platformtools
|
from platformcode import platformtools
|
||||||
@@ -784,4 +784,4 @@ def translate_server_name(name):
|
|||||||
if '@' in name:
|
if '@' in name:
|
||||||
return config.get_localized_string(int(name.replace('@','')))
|
return config.get_localized_string(int(name.replace('@','')))
|
||||||
else:
|
else:
|
||||||
return name
|
return name
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from lib.streamingcommunity.client import Client
|
||||||
|
from lib.streamingcommunity.server import Server
|
||||||
|
__all__ = ['Client', 'Server']
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
import base64, json, random, struct, time, sys, traceback
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
PY3 = True
|
||||||
|
import urllib.request as urllib
|
||||||
|
xrange = range
|
||||||
|
else:
|
||||||
|
PY3 = False
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from core import httptools, jsontools, support
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from lib.streamingcommunity.handler import Handler
|
||||||
|
from platformcode import logger
|
||||||
|
from lib.streamingcommunity.server import Server
|
||||||
|
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
|
||||||
|
def __init__(self, url, port=None, ip=None, auto_shutdown=True, wait_time=20, timeout=5, is_playing_fnc=None, video_id=None):
|
||||||
|
|
||||||
|
self.port = port if port else random.randint(8000,8099)
|
||||||
|
self.ip = ip if ip else "127.0.0.1"
|
||||||
|
self.connected = False
|
||||||
|
self.start_time = None
|
||||||
|
self.last_connect = None
|
||||||
|
self.is_playing_fnc = is_playing_fnc
|
||||||
|
self.auto_shutdown = auto_shutdown
|
||||||
|
self.wait_time = wait_time
|
||||||
|
self.timeout = timeout
|
||||||
|
self.running = False
|
||||||
|
self.file = None
|
||||||
|
self.files = []
|
||||||
|
|
||||||
|
|
||||||
|
# video_id is the ID in the webpage path
|
||||||
|
self._video_id = video_id
|
||||||
|
|
||||||
|
# Get json_data for entire details from video page
|
||||||
|
jsonDataStr = httptools.downloadpage('https://streamingcommunityws.com/videos/1/{}'.format(self._video_id), CF=False ).data
|
||||||
|
logger.debug( jsonDataStr )
|
||||||
|
self._jsonData = jsontools.load( jsonDataStr )
|
||||||
|
|
||||||
|
# going to calculate token and expiration time
|
||||||
|
# These values will be used for manifests request
|
||||||
|
self._token, self._expires = self.calculateToken( self._jsonData['client_ip'] )
|
||||||
|
|
||||||
|
# Starting web server
|
||||||
|
self._server = Server((self.ip, self.port), Handler, client=self)
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
" Starting client and server in a separated thread
|
||||||
|
"""
|
||||||
|
self.start_time = time.time()
|
||||||
|
self.running = True
|
||||||
|
self._server.run()
|
||||||
|
t= Thread(target=self._auto_shutdown)
|
||||||
|
t.setDaemon(True)
|
||||||
|
t.start()
|
||||||
|
logger.info("SC Server Started", (self.ip, self.port))
|
||||||
|
|
||||||
|
def _auto_shutdown(self):
|
||||||
|
while self.running:
|
||||||
|
time.sleep(1)
|
||||||
|
if self.file and self.file.cursor:
|
||||||
|
self.last_connect = time.time()
|
||||||
|
|
||||||
|
if self.is_playing_fnc and self.is_playing_fnc():
|
||||||
|
self.last_connect = time.time()
|
||||||
|
|
||||||
|
if self.auto_shutdown:
|
||||||
|
#shudown por haber cerrado el reproductor
|
||||||
|
if self.connected and self.last_connect and self.is_playing_fnc and not self.is_playing_fnc():
|
||||||
|
if time.time() - self.last_connect - 1 > self.timeout:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
#shutdown por no realizar ninguna conexion
|
||||||
|
if (not self.file or not self.file.cursor) and self.start_time and self.wait_time and not self.connected:
|
||||||
|
if time.time() - self.start_time - 1 > self.wait_time:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
#shutdown tras la ultima conexion
|
||||||
|
if (not self.file or not self.file.cursor) and self.timeout and self.connected and self.last_connect and not self.is_playing_fnc:
|
||||||
|
if time.time() - self.last_connect - 1 > self.timeout:
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running = False
|
||||||
|
self._server.stop()
|
||||||
|
logger.info("SC Server Stopped")
|
||||||
|
|
||||||
|
|
||||||
|
def get_manifest_url(self):
|
||||||
|
# remap request path for main manifest
|
||||||
|
# it must point to local server ip:port
|
||||||
|
return "http://" + self.ip + ":" + str(self.port) + "/manifest.m3u8"
|
||||||
|
|
||||||
|
|
||||||
|
def get_main_manifest_content(self):
|
||||||
|
# get the manifest file for entire video/audio chunks
|
||||||
|
# it must remap each urls in order to catch all chunks
|
||||||
|
|
||||||
|
url = 'https://streamingcommunityws.com/master/{}?token={}&expires={}'.format(self._video_id, self._token, self._expires)
|
||||||
|
|
||||||
|
m3u8_original = httptools.downloadpage(url, CF=False).data
|
||||||
|
|
||||||
|
logger.debug('CLIENT: m3u8:', m3u8_original);
|
||||||
|
|
||||||
|
# remap video/audio manifests url
|
||||||
|
# they must point to local server:
|
||||||
|
# /video/RES.m3u8
|
||||||
|
# /audio/RES.m3u8
|
||||||
|
|
||||||
|
r_video = re.compile(r'(\.\/video\/(\d+p)\/playlist.m3u8)', re.MULTILINE)
|
||||||
|
r_audio = re.compile(r'(\.\/audio\/(\d+k)\/playlist.m3u8)', re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
for match in r_video.finditer(m3u8_original):
|
||||||
|
line = match.groups()[0]
|
||||||
|
res = match.groups()[1]
|
||||||
|
video_url = "/video/" + res + ".m3u8"
|
||||||
|
|
||||||
|
# logger.info('replace', match.groups(), line, res, video_url)
|
||||||
|
|
||||||
|
m3u8_original = m3u8_original.replace( line, video_url )
|
||||||
|
|
||||||
|
|
||||||
|
for match in r_audio.finditer(m3u8_original):
|
||||||
|
line = match.groups()[0]
|
||||||
|
res = match.groups()[1]
|
||||||
|
audio_url = "/audio/" + res + ".m3u8"
|
||||||
|
|
||||||
|
# logger.info('replace', match.groups(), line, res, audio_url)
|
||||||
|
|
||||||
|
m3u8_original = m3u8_original.replace( line, audio_url )
|
||||||
|
|
||||||
|
|
||||||
|
# m_video = re.search(, m3u8_original)
|
||||||
|
# self._video_res = m_video.group(1)
|
||||||
|
# m_audio = re.search(r'\.\/audio\/(\d+k)\/playlist.m3u8', m3u8_original)
|
||||||
|
# self._audio_res = m_audio.group(1)
|
||||||
|
|
||||||
|
# video_url = "/video/" + self._video_res + ".m3u8"
|
||||||
|
# audio_url = "/audio/" + self._audio_res + ".m3u8"
|
||||||
|
|
||||||
|
# m3u8_original = m3u8_original.replace( m_video.group(0), video_url )
|
||||||
|
# m3u8_original = m3u8_original.replace( m_audio.group(0), audio_url )
|
||||||
|
|
||||||
|
return m3u8_original
|
||||||
|
|
||||||
|
|
||||||
|
def get_video_manifest_content(self, url):
|
||||||
|
"""
|
||||||
|
" Based on `default_start`, `default_count` and `default_domain`
|
||||||
|
" this method remap each video chunks url in order to make them point to
|
||||||
|
" the remote domain switching from `default_start` to `default_count` values
|
||||||
|
"""
|
||||||
|
|
||||||
|
m_video = re.search( r'\/video\/(\d+p)\.m3u8', url)
|
||||||
|
video_res = m_video.groups()[0]
|
||||||
|
|
||||||
|
logger.info('Video res: ', video_res)
|
||||||
|
|
||||||
|
# get the original manifest file for video chunks
|
||||||
|
url = 'https://streamingcommunityws.com/master/{}?token={}&expires={}&type=video&rendition={}'.format(self._video_id, self._token, self._expires, video_res)
|
||||||
|
original_manifest = httptools.downloadpage(url, CF=False).data
|
||||||
|
|
||||||
|
manifest_to_parse = original_manifest
|
||||||
|
|
||||||
|
# remap each chunks
|
||||||
|
r = re.compile(r'^(\w+\.ts)$', re.MULTILINE)
|
||||||
|
|
||||||
|
default_start = self._jsonData[ "proxies" ]["default_start"]
|
||||||
|
default_count = self._jsonData[ "proxies" ]["default_count"]
|
||||||
|
default_domain = self._jsonData[ "proxies" ]["default_domain"]
|
||||||
|
storage_id = self._jsonData[ "storage_id" ]
|
||||||
|
folder_id = self._jsonData[ "folder_id" ]
|
||||||
|
|
||||||
|
for match in r.finditer(manifest_to_parse):
|
||||||
|
# getting all single chunks and replace in the original manifest file content
|
||||||
|
ts = match.groups()[0]
|
||||||
|
|
||||||
|
# compute final url pointing to given domain
|
||||||
|
url = 'https://au-{default_start}.{default_domain}/hls/{storage_id}/{folder_id}/video/{video_res}/{ts}'.format(
|
||||||
|
default_start = default_start,
|
||||||
|
default_domain = default_domain,
|
||||||
|
storage_id = storage_id,
|
||||||
|
folder_id = folder_id,
|
||||||
|
video_res = video_res,
|
||||||
|
ts = ts
|
||||||
|
)
|
||||||
|
|
||||||
|
original_manifest = original_manifest.replace( ts, url )
|
||||||
|
|
||||||
|
default_start = default_start + 1
|
||||||
|
if default_start > default_count:
|
||||||
|
default_start = 1
|
||||||
|
|
||||||
|
# replace the encryption file url pointing to remote streamingcommunity server
|
||||||
|
original_manifest = re.sub(r'"(\/.*[enc]?\.key)"', '"https://streamingcommunityws.com\\1"', original_manifest)
|
||||||
|
|
||||||
|
return original_manifest
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_audio_manifest_content(self, url):
|
||||||
|
"""
|
||||||
|
" Based on `default_start`, `default_count` and `default_domain`
|
||||||
|
" this method remap each video chunks url in order to make them point to
|
||||||
|
" the remote domain switching from `default_start` to `default_count` values
|
||||||
|
"""
|
||||||
|
m_audio = re.search( r'\/audio\/(\d+k)\.m3u8', url)
|
||||||
|
audio_res = m_audio.groups()[0]
|
||||||
|
|
||||||
|
logger.info('Audio res: ', audio_res)
|
||||||
|
|
||||||
|
# get the original manifest file for video chunks
|
||||||
|
url = 'https://streamingcommunityws.com/master/{}?token={}&expires={}&type=audio&rendition={}'.format(self._video_id, self._token, self._expires, audio_res)
|
||||||
|
original_manifest = httptools.downloadpage(url, CF=False).data
|
||||||
|
|
||||||
|
manifest_to_parse = original_manifest
|
||||||
|
|
||||||
|
# remap each chunks
|
||||||
|
r = re.compile(r'^(\w+\.ts)$', re.MULTILINE)
|
||||||
|
|
||||||
|
default_start = self._jsonData[ "proxies" ]["default_start"]
|
||||||
|
default_count = self._jsonData[ "proxies" ]["default_count"]
|
||||||
|
default_domain = self._jsonData[ "proxies" ]["default_domain"]
|
||||||
|
storage_id = self._jsonData[ "storage_id" ]
|
||||||
|
folder_id = self._jsonData[ "folder_id" ]
|
||||||
|
|
||||||
|
for match in r.finditer(manifest_to_parse):
|
||||||
|
# getting all single chunks and replace in the original manifest file content
|
||||||
|
ts = match.groups()[0]
|
||||||
|
|
||||||
|
# compute final url pointing to given domain
|
||||||
|
url = 'https://au-{default_start}.{default_domain}/hls/{storage_id}/{folder_id}/audio/{audio_res}/{ts}'.format(
|
||||||
|
default_start = default_start,
|
||||||
|
default_domain = default_domain,
|
||||||
|
storage_id = storage_id,
|
||||||
|
folder_id = folder_id,
|
||||||
|
audio_res = audio_res,
|
||||||
|
ts = ts
|
||||||
|
)
|
||||||
|
|
||||||
|
original_manifest = original_manifest.replace( ts, url )
|
||||||
|
|
||||||
|
default_start = default_start + 1
|
||||||
|
if default_start > default_count:
|
||||||
|
default_start = 1
|
||||||
|
|
||||||
|
|
||||||
|
# replace the encryption file url pointing to remote streamingcommunity server
|
||||||
|
original_manifest = re.sub(r'"(\/.*[enc]?\.key)"', '"https://streamingcommunityws.com\\1"', original_manifest)
|
||||||
|
|
||||||
|
return original_manifest
|
||||||
|
|
||||||
|
|
||||||
|
def calculateToken(self, ip):
|
||||||
|
"""
|
||||||
|
" Compute the `token` and the `expires` values in order to perform each next requests
|
||||||
|
"""
|
||||||
|
|
||||||
|
from time import time
|
||||||
|
from base64 import b64encode as b64
|
||||||
|
import hashlib
|
||||||
|
o = 48
|
||||||
|
|
||||||
|
# NOT USED: it has been computed by `jsondata` in the constructor method
|
||||||
|
# n = support.match('https://au-1.scws-content.net/get-ip').data
|
||||||
|
|
||||||
|
i = 'Yc8U6r8KjAKAepEA'
|
||||||
|
t = int(time() + (3600 * o))
|
||||||
|
l = '{}{} {}'.format(t, ip, i)
|
||||||
|
md5 = hashlib.md5(l.encode())
|
||||||
|
#s = '?token={}&expires={}'.format(, t)
|
||||||
|
token = b64( md5.digest() ).decode().replace( '=', '' ).replace( '+', "-" ).replace( '\\', "_" )
|
||||||
|
expires = t
|
||||||
|
return token, expires
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import time, os, re, sys
|
||||||
|
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
PY3 = True
|
||||||
|
from http.server import BaseHTTPRequestHandler
|
||||||
|
import urllib.request as urllib
|
||||||
|
import urllib.parse as urlparse
|
||||||
|
else:
|
||||||
|
PY3 = False
|
||||||
|
from BaseHTTPServer import BaseHTTPRequestHandler
|
||||||
|
import urlparse
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from platformcode import logger
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
protocol_version = 'HTTP/1.1'
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
"""
|
||||||
|
" Got request
|
||||||
|
" We are going to handle the request path in order to proxy each manifest
|
||||||
|
"""
|
||||||
|
url = urlparse.urlparse(self.path).path
|
||||||
|
|
||||||
|
logger.debug('HANDLER:', url)
|
||||||
|
|
||||||
|
response = None
|
||||||
|
|
||||||
|
# Default content-type for each manifest
|
||||||
|
cType = "application/vnd.apple.mpegurl"
|
||||||
|
|
||||||
|
if url == "/manifest.m3u8":
|
||||||
|
response = self.server._client.get_main_manifest_content()
|
||||||
|
|
||||||
|
elif url.startswith('/video/'):
|
||||||
|
response = self.server._client.get_video_manifest_content(url)
|
||||||
|
|
||||||
|
elif url.startswith('/audio/'):
|
||||||
|
response = self.server._client.get_audio_manifest_content(url)
|
||||||
|
|
||||||
|
elif url.endswith('enc.key'):
|
||||||
|
# This path should NOT be used, see get_video_manifest_content function
|
||||||
|
response = self.server._client.get_enc_key( url )
|
||||||
|
cType = "application/octet-stream"
|
||||||
|
|
||||||
|
|
||||||
|
if response == None:
|
||||||
|
# Default 404 response
|
||||||
|
self.send_error(404, 'Not Found')
|
||||||
|
logger.warn('Responding 404 for url', url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# catch OK response and send it to client
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-Type", cType )
|
||||||
|
self.send_header("Content-Length", str( len(response.encode('utf-8')) ) )
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
self.wfile.write( response.encode() )
|
||||||
|
|
||||||
|
# force flush just to be sure
|
||||||
|
self.wfile.flush()
|
||||||
|
|
||||||
|
logger.info('HANDLER flushed:', cType , str( len(response.encode('utf-8')) ) )
|
||||||
|
logger.debug( response.encode('utf-8') )
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import sys, traceback
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
from http.server import HTTPServer
|
||||||
|
from socketserver import ThreadingMixIn
|
||||||
|
else:
|
||||||
|
from BaseHTTPServer import HTTPServer
|
||||||
|
from SocketServer import ThreadingMixIn
|
||||||
|
|
||||||
|
from threading import Thread
|
||||||
|
from platformcode import logger
|
||||||
|
|
||||||
|
|
||||||
|
class Server(ThreadingMixIn, HTTPServer):
|
||||||
|
daemon_threads = True
|
||||||
|
timeout = 1
|
||||||
|
def __init__(self, address, handler, client):
|
||||||
|
HTTPServer.__init__(self,address,handler)
|
||||||
|
self._client = client
|
||||||
|
self.running=True
|
||||||
|
self.request = None
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running=False
|
||||||
|
|
||||||
|
# def serve(self):
|
||||||
|
# while self.running:
|
||||||
|
# try:
|
||||||
|
# self.handle_request()
|
||||||
|
# except:
|
||||||
|
# logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
t=Thread(target=self.serve_forever, name='HTTP Server')
|
||||||
|
t.daemon=self.daemon_threads
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
def handle_error(self, request, client_address):
|
||||||
|
if not "socket.py" in traceback.format_exc():
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"active": true,
|
||||||
|
"find_videos": {
|
||||||
|
"ignore_urls": [],
|
||||||
|
"patterns": [
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"free": true,
|
||||||
|
"id": "streamingcommunityws",
|
||||||
|
"name": "StreamingCommunityWS",
|
||||||
|
"premium": [
|
||||||
|
],
|
||||||
|
"settings": [
|
||||||
|
]
|
||||||
|
}
|
||||||
Executable
+30
@@ -0,0 +1,30 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
PY3 = False
|
||||||
|
if sys.version_info[0] >= 3: PY3 = True; unicode = str; unichr = chr; long = int
|
||||||
|
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
from core import httptools, support, scrapertools
|
||||||
|
from platformcode import platformtools, logger
|
||||||
|
from lib.streamingcommunity import Client as SCClient
|
||||||
|
|
||||||
|
files = None
|
||||||
|
|
||||||
|
def test_video_exists(page_url):
|
||||||
|
|
||||||
|
# page_url is the {VIDEO_ID}. Es: 5957
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
def get_video_url(page_url, premium=False, user="", password="", video_password=""):
|
||||||
|
|
||||||
|
video_urls = []
|
||||||
|
|
||||||
|
global c
|
||||||
|
c = SCClient("",video_id=page_url, is_playing_fnc=platformtools.is_playing)
|
||||||
|
|
||||||
|
media_url = c.get_manifest_url()
|
||||||
|
|
||||||
|
video_urls.append([scrapertools.get_filename_from_url(media_url)[-4:] + " [Streaming Community]", media_url])
|
||||||
|
|
||||||
|
return video_urls
|
||||||
+4
-3
@@ -311,8 +311,9 @@ def servers_favorites(item):
|
|||||||
|
|
||||||
orden = config.get_setting("favorites_servers_list", server=server)
|
orden = config.get_setting("favorites_servers_list", server=server)
|
||||||
|
|
||||||
if orden > 0:
|
if not orden == None:
|
||||||
dict_values[orden] = len(server_names) - 1
|
if orden > 0:
|
||||||
|
dict_values[orden] = len(server_names) - 1
|
||||||
|
|
||||||
for x in range(1, 12):
|
for x in range(1, 12):
|
||||||
control = {'id': x,
|
control = {'id': x,
|
||||||
@@ -1214,4 +1215,4 @@ def call_browser(item):
|
|||||||
short = urllib.urlopen(
|
short = urllib.urlopen(
|
||||||
'https://u.nu/api.php?action=shorturl&format=simple&url=' + item.url).read()
|
'https://u.nu/api.php?action=shorturl&format=simple&url=' + item.url).read()
|
||||||
platformtools.dialog_ok(config.get_localized_string(20000),
|
platformtools.dialog_ok(config.get_localized_string(20000),
|
||||||
config.get_localized_string(70740) % short)
|
config.get_localized_string(70740) % short)
|
||||||
|
|||||||
Reference in New Issue
Block a user