Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relaxing channel name check in Xtream API #178

Merged
merged 27 commits into from
Dec 8, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
293963b
Added Initial XTream
superolmo May 28, 2021
a1dca48
Added XTream Series
superolmo Jun 2, 2021
d0be0a5
Added check for local logo_path
superolmo Jun 4, 2021
a84f7ce
Back to fixed path
superolmo Jun 4, 2021
b97598a
Added pyxtream choice
superolmo Jun 5, 2021
0530fc9
Replaced the test server
superolmo Jun 6, 2021
b56a6fb
Replaced the test server
superolmo Jun 6, 2021
ab11dfd
Fixed cache-path and added regex search
superolmo Jun 7, 2021
5d4a971
Merge branch 'master' of github.com:superolmo/hypnotix
superolmo Jun 7, 2021
2b8d127
Merge branch 'master' of https://github.com/linuxmint/hypnotix into l…
superolmo Jun 7, 2021
b38d61e
Changed osp back to os.path
superolmo Jun 7, 2021
7db1d62
Changed osp back to os.path
superolmo Jun 7, 2021
06517d6
Merge branch 'linuxmin-master'
superolmo Jun 7, 2021
2a45eb1
Fixed bug in the way it reload from cache
superolmo Jun 12, 2021
e79a848
Fixed missing provider when it doesn't load
superolmo Jun 18, 2021
dcbb6a1
Improved handling of missing keys
superolmo Jun 18, 2021
97b9e73
Fixed Categories and cleaned up the code
superolmo Jun 18, 2021
32af21f
Updated function names to follow PEP8
superolmo Jun 28, 2021
51e6d1d
Added check before authorizing
superolmo Sep 27, 2021
e6390d9
Merge remote-tracking branch 'upstream/master'
superolmo Sep 27, 2021
fca44f2
Scale down changes
superolmo Sep 27, 2021
0b41feb
Revert some more changes
superolmo Sep 27, 2021
215e191
Revert last changes
superolmo Sep 27, 2021
76bd0f2
Revert flag name
superolmo Sep 27, 2021
211db79
Discard streams w/o name, change live radio type to live stream
superolmo Nov 5, 2021
c8577a2
Rebase to upstream master
superolmo Nov 28, 2021
a7d447c
Fix subgroup name check
superolmo Dec 1, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added XTream Series
  • Loading branch information
superolmo committed Jun 2, 2021
commit a1dca481c0479e1025055d64e7dc471e4b13943c
16 changes: 10 additions & 6 deletions usr/lib/hypnotix/hypnotix.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,11 @@ def remove_word(self, word, string):
def show_episodes(self, serie):
logos_to_refresh = []
self.active_serie = serie
# If we are using xtream provider
# Load every Episodes of every Season for this Series
if self.active_provider.type_id == "xtream":
self.x.getSeriesInfoByID(self.active_serie)

self.navigate_to("episodes_page")
for child in self.episodes_box.get_children():
self.episodes_box.remove(child)
Expand Down Expand Up @@ -1221,7 +1226,6 @@ def reload(self, page=None, refresh=False):
self.providers.append(provider)
if provider.name == self.settings.get_string("active-provider"):
self.active_provider = provider
print(provider)
self.status(None)
print("Loaded {} channels".format(len(self.providers[0].channels)))
print("Loaded {} groups".format(len(self.providers[0].groups)))
Expand All @@ -1236,13 +1240,13 @@ def reload(self, page=None, refresh=False):
self.status("Failed to Download playlist from {}".format(provider.name),provider)

else:

# Download via Xtream
# Load xtream class
from xtream import XTream
x = XTream(provider.url,provider.username,provider.password)
if x.authData != {}:
# Download via Xtream
self.x = XTream(provider.url,provider.username,provider.password)
if self.x.authData != {}:
print("XTREAM Loading Channels")
x.load_iptv(provider)
self.x.load_iptv(provider)
else:
print("XTREAM Authentication Failed")

Expand Down
241 changes: 165 additions & 76 deletions usr/lib/hypnotix/xtream.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import requests
import datetime
import time
import os.path as osp
from timeit import default_timer as timer, timeit

Expand Down Expand Up @@ -50,82 +50,49 @@ class Channel():
added = ""
epg_channel_id = ""

def __init__(self, group_title, stream_info):
def __init__(self, group_title, stream_info, authorization):
stream_type = stream_info['stream_type']
# Adjust the odd "created_live" type
if stream_type == "created_live":
stream_type = "live"

if stream_type == "live":
if stream_type != "live" and stream_type != "movie":
print("Error the channel has unknown stream type `{}`\n`{}`".format(stream_type,stream_info))
else:

self.info = stream_info
stream_name = stream_info['name']
self.id = stream_info['stream_id']
self.name = stream_name
self.logo = stream_info['stream_icon']
if self.logo != None:
if not validateURL(self.logo):
#print("Bad URL? `{}`".format(self.logo))
self.logo = None
else:
self.logo_path = osp.join(PROVIDERS_PATH, "{}-{}".format(
slugify(PROVIDER_NAME),
slugify(osp.split(self.logo)[-1])
)
)
else:
self.logo_path=None
self.logo_path = getLogoLocalPath(self.logo)

self.group_id = stream_info['category_id']

self.group_title = group_title
self.title = stream_name
self.url = "https://mega.test25.in:80/{}/399898078855714/282869529383630/{}.ts".format(
stream_info['stream_type'],
stream_info['stream_id']
)
if not validateURL(self.url):
print("Bad URL? `{}`".format(self.url))

self.is_adult = stream_info['is_adult']
self.epg_channel_id = stream_info['epg_channel_id']
self.added = stream_info['added']
elif stream_type == "movie":
self.info = stream_info
stream_name = stream_info['name']
self.id = stream_info['stream_id']
self.name = stream_name
self.logo = stream_info['stream_icon']
if self.logo != None:
if not validateURL(self.logo):
#print("Bad URL? `{}`".format(self.logo))
self.logo = None
else:
self.logo_path = osp.join(PROVIDERS_PATH, "{}-{}".format(
slugify(PROVIDER_NAME),
slugify(osp.split(self.logo)[-1])
)
)
else:
self.logo_path=None

self.group_id = stream_info['category_id']
if stream_type == "live":
stream_extension = "ts"

self.is_adult = stream_info['is_adult']
self.epg_channel_id = stream_info['epg_channel_id']
self.added = stream_info['added']

self.group_title = group_title
self.title = stream_name
self.url = "https://mega.test25.in:80/{}/399898078855714/282869529383630/{}.{}".format(
elif stream_type == "movie":
stream_extension = stream_info['container_extension']

self.url = "https://mega.test25.in:80/{}/{}/{}/{}.{}".format(
stream_info['stream_type'],
authorization['username'],
authorization['password'],
stream_info['stream_id'],
stream_info['container_extension']
stream_extension
)

if not validateURL(self.url):
print("{} - Bad URL? `{}`".format(self.name, self.url))

elif stream_type == "series":
pass
else:
print("Error the channel has unknown stream type `{}`\n`{}`".format(stream_type,stream_info))

def show(self):
print("Stream\nname: `{}`\nid: `{}`\nlogo: `{}`\nlogo_path: `{}`\ngroup_id: `{}`\ngroup_title: `{}`\nurl: `{}`\nis_adult: `{}`\nadded: `{}`".format(
self.name,
Expand All @@ -139,6 +106,28 @@ def show(self):
self.added
))

def getLogoLocalPath(logoURL: str):
"""Convert the Logo URL to a local Logo Path

Args:
logoURL (str): The Logo URL

Returns:
[type]: The logo path as a string or None
"""
local_logo_path = None
if logoURL != None:
if not validateURL(logoURL):
#print("Bad URL? `{}`".format(logoURL))
logoURL = None
else:
local_logo_path = osp.join(PROVIDERS_PATH, "{}-{}".format(
slugify(PROVIDER_NAME),
slugify(osp.split(logoURL)[-1])
)
)
return local_logo_path

class Group():
def __init__(self, group_info: dict, stream_type: str):
if "VOD" == stream_type:
Expand All @@ -163,13 +152,42 @@ def show(self):
len(self.series)
))

class Episode():
def __init__(self, series_info, group_title, episode_info, authorization) -> None:
self.info = episode_info
self.title = episode_info['title']
self.name = self.title
self.group_title = group_title
self.id = episode_info['id']
self.container_extension = episode_info['container_extension']
self.episode_number = episode_info['episode_num']
self.av_info = episode_info['info']

self.logo = series_info['cover']
self.logo_path = getLogoLocalPath(self.logo)


self.url = "https://mega.test25.in:80/series/{}/{}/{}.{}".format(
authorization['username'],
authorization['password'],
self.id,
self.container_extension
)

class Serie():
def __init__(self, name):
self.name = name
self.logo = None
self.logo_path = None
def __init__(self, series_info):
self.info = series_info
self.name = series_info['name']
self.series_id = series_info['series_id']
self.logo = series_info['cover']
self.logo_path = getLogoLocalPath(self.logo)

self.seasons = {}
self.episodes = []
self.episodes = {}

self.plot = series_info['plot']
self.youtube_trailer = series_info['youtube_trailer']
self.genre = series_info['genre']

class Season():
def __init__(self, name):
Expand Down Expand Up @@ -201,6 +219,7 @@ class XTream():
seriesType = "Series"

authData = {}
authorization = {}
groups = []
channels = []

Expand All @@ -220,6 +239,39 @@ def __init__(self, server, username, password):
def authenticate(self):
r = requests.get(self.get_authenticate_URL())
self.authData = r.json()
self.authorization = {
"username": self.authData["user_info"]["username"],
"password": self.authData["user_info"]["password"]
}

def loadFromFile(self, filename) -> dict:
#Build the full path
full_filename = osp.join(PROVIDERS_PATH, "{}-{}".format(
slugify(PROVIDER_NAME),
filename
))


my_data = None
#threshold_time = time.mktime(time.gmtime(60*60*8)) # 8 hours
threshold_time = 60*60*8

# Get the enlapsed seconds since last file update
diff_time = time.time() - osp.getmtime(full_filename)
# If the file was updated less than the threshold time,
# it means that the file is still fresh, we can load it.
# Otherwise skip and return None to force a re-download
if threshold_time > diff_time:
# Load the JSON data
try:
with open(full_filename,mode='r',encoding='utf-8') as myfile:
#my_data = myfile.read()
my_data = json.load(myfile)
except Exception as e:
print("Could not save to file `{}`: e=`{}`".format(full_filename, e))

return my_data


def saveToFile(self, data_list: dict, filename: str) -> bool:
"""Save a dictionary to file
Expand All @@ -243,9 +295,6 @@ def saveToFile(self, data_list: dict, filename: str) -> bool:
try:
with open(full_filename, mode='wt', encoding='utf-8') as myfile:
myfile.write(json_data)
#myfile.write("[")
#myfile.write('\n'.join(str(line) for line in data_list))
#myfile.write("]")
except Exception as e:
print("Could not save to file `{}`: e=`{}`".format(full_filename, e))
return False
Expand All @@ -255,11 +304,16 @@ def saveToFile(self, data_list: dict, filename: str) -> bool:
def load_iptv(self, provider):
#loading_stream_type = self.liveType
for loading_stream_type in (self.liveType, self.vodType, self.seriesType):
# Load all Groups and save file locally
start = timer()
all_cat = self.categories(loading_stream_type)
self.saveToFile(all_cat,"all_groups_{}.json".format(loading_stream_type))
dt = timer()-start
# Try loading local file
dt = 0
all_cat = self.loadFromFile("all_groups_{}.json".format(loading_stream_type))
# If none, download it from remote
if all_cat == None:
# Load all Groups and save file locally
start = timer()
all_cat = self.categories(loading_stream_type)
self.saveToFile(all_cat,"all_groups_{}.json".format(loading_stream_type))
dt = timer()-start

if all_cat == None:
return None
Expand All @@ -275,11 +329,16 @@ def load_iptv(self, provider):
self.groups.append(new_group)
provider.groups.append(new_group)

# Load all Streams and save file locally
start = timer()
all_streams = self.streams(loading_stream_type)
self.saveToFile(all_streams,"all_stream_{}.json".format(loading_stream_type))
dt = timer()-start
# Try loading local file
dt = 0
all_streams = self.loadFromFile("all_stream_{}.json".format(loading_stream_type))
# If none, download it from remote
if all_streams == None:
# Load all Streams and save file locally
start = timer()
all_streams = self.streams(loading_stream_type)
self.saveToFile(all_streams,"all_stream_{}.json".format(loading_stream_type))
dt = timer()-start

if all_streams == None:
return None
Expand All @@ -296,9 +355,15 @@ def load_iptv(self, provider):
stream_channel['category_id'] = '9999'

if loading_stream_type == self.seriesType:
# Load all Series
new_series = Serie(stream_channel)
# To get all the Episodes for every Season or each Series is
# very time consuming, we will only populate the Series
# Once the user click on the Series, the Seasons and Episodes will
# be loaded using x.getSeriesInfoByID() function

else:
new_channel = Channel(group_title, stream_channel)
new_channel = Channel(group_title, stream_channel,self.authorization)

# Find the first occurence of the group that the Channel or Stream is pointing to
the_group = next((x for x in self.groups if x.group_id == stream_channel['category_id']), None)
Expand All @@ -311,11 +376,35 @@ def load_iptv(self, provider):
else:
provider.series.append(new_series)

self.channels.append(new_channel)
if the_group != None:
the_group.channels.append(new_channel)
if loading_stream_type != self.seriesType:
#self.channels.append(new_channel)
if the_group != None:
the_group.channels.append(new_channel)
else:
print("Group not found `{}`".format(stream_channel['name']))
else:
print("Group not found `{}`".format(stream_channel['name']))
if the_group != None:
the_group.series.append(new_series)
else:
print("Group not found `{}`".format(stream_channel['name']))

def getSeriesInfoByID(self, get_series):
start = timer()
series_seasons = self.seriesInfoByID(get_series.series_id)
dt = timer()-start
#print("Loaded in {:.3f} sec".format(dt))
for series_info in series_seasons["seasons"]:
season_name = series_info["name"]
season_key = series_info['season_number']
season = Season(season_name)
get_series.seasons[season_name] = season
#print("{} with {} episodes".format(season_name,len(series_seasons['episodes'][str(season_key)])))
if "episodes" in series_seasons.keys():
for series_season in series_seasons["episodes"].keys():
#print("{} with {} episodes".format(season_name,len(series_seasons['episodes'][str(series_season)])))
for episode_info in series_seasons["episodes"][str(series_season)]:
new_episode_channel = Episode(series_info,"Testing",episode_info,self.authorization)
season.episodes[episode_info['title']] = new_episode_channel

# GET Stream Categories
def categories(self, streamType: str):
Expand Down