Skip to content

Commit

Permalink
export to vlc and fullscreen fix attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
ozankaraali committed May 25, 2024
1 parent 0ad9af6 commit 2ddb2ee
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 49 deletions.
118 changes: 91 additions & 27 deletions channel_list.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import json
import os
import shutil
import string
import random
import re
import subprocess
import platform

from urlobject import URLObject

import requests
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import (
QMainWindow, QFileDialog, QVBoxLayout, QWidget, QPushButton, QListWidget,
QHBoxLayout, QListWidgetItem, QLineEdit, QGridLayout
QMainWindow,
QFileDialog,
QVBoxLayout,
QWidget,
QPushButton,
QListWidget,
QHBoxLayout,
QListWidgetItem,
QLineEdit,
QGridLayout,
)

from options import OptionsDialog
Expand All @@ -20,6 +33,7 @@ class ChannelList(QMainWindow):
def __init__(self, player):
super().__init__()
self.player = player
self.link = None
self.setWindowTitle("QiTV Channel List")

self.container_widget = QWidget(self)
Expand Down Expand Up @@ -53,7 +67,6 @@ def create_upper_panel(self):
ctl_layout.addWidget(self.options_button)
self.grid_layout.addWidget(self.upper_layout, 0, 0)


def create_left_panel(self):
self.left_panel = QWidget(self.container_widget)
left_layout = QVBoxLayout(self.left_panel)
Expand Down Expand Up @@ -82,8 +95,51 @@ def create_media_controls(self):
self.stop_button.clicked.connect(self.player.stop_video)
control_layout.addWidget(self.stop_button)

self.vlc_button = QPushButton("Open in VLC")
self.vlc_button.clicked.connect(self.open_in_vlc)
control_layout.addWidget(self.vlc_button)

self.grid_layout.addWidget(self.media_controls, 2, 0)

def open_in_vlc(self):
# Invoke user's VLC player to open the current stream
if self.link:
try:
if platform.system() == "Windows":
vlc_path = shutil.which("vlc") # Try to find VLC in PATH
if not vlc_path:
program_files = os.environ.get(
"ProgramFiles", "C:\\Program Files"
)
vlc_path = os.path.join(
program_files, "VideoLAN", "VLC", "vlc.exe"
)
subprocess.Popen([vlc_path, self.link])

elif platform.system() == "Darwin": # macOS
vlc_path = shutil.which("vlc") # Try to find VLC in PATH
if not vlc_path:
common_paths = [
"/Applications/VLC.app/Contents/MacOS/VLC",
"~/Applications/VLC.app/Contents/MacOS/VLC",
]
for path in common_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
vlc_path = expanded_path
break
subprocess.Popen([vlc_path, self.link])

else: # Assuming Linux or other Unix-like OS
vlc_path = shutil.which("vlc") # Try to find VLC in PATH
subprocess.Popen([vlc_path, self.link])
# when VLC open, stop running video on self.player
self.player.stop_video()
except FileNotFoundError as fnf_error:
print("VLC not found: ", fnf_error)
except Exception as e:
print(f"Error opening VLC: {e}")

def open_file(self):
file_dialog = QFileDialog(self)
file_path, _ = file_dialog.getOpenFileName()
Expand All @@ -92,30 +148,30 @@ def open_file(self):

def load_config(self):
try:
with open('config.json', 'r') as f:
with open("config.json", "r") as f:
self.config = json.load(f)
if self.config is None:
self.config = self.default_config()
except (FileNotFoundError, json.JSONDecodeError):
self.config = self.default_config()
self.save_config()

selected_config = self.config['data'][self.config['selected']]
if 'options' in selected_config:
self.options = selected_config['options']
self.token = self.options['headers']['Authorization'].split(" ")[1]
selected_config = self.config["data"][self.config["selected"]]
if "options" in selected_config:
self.options = selected_config["options"]
self.token = self.options["headers"]["Authorization"].split(" ")[1]
else:
self.options = {
'headers': {
'User-Agent': 'Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3',
'Accept-Charset': 'UTF-8,*;q=0.8',
'X-User-Agent': 'Model: MAG200; Link: Ethernet',
'Content-Type': 'application/json'
"headers": {
"User-Agent": "Mozilla/5.0 (QtEmbedded; U; Linux; C) AppleWebKit/533.3 (KHTML, like Gecko) MAG200 stbapp ver: 2 rev: 250 Safari/533.3",
"Accept-Charset": "UTF-8,*;q=0.8",
"X-User-Agent": "Model: MAG200; Link: Ethernet",
"Content-Type": "application/json",
}
}

self.url = selected_config.get('url')
self.mac = selected_config.get('mac')
self.url = selected_config.get("url")
self.mac = selected_config.get("mac")

@staticmethod
def default_config():
Expand All @@ -124,17 +180,17 @@ def default_config():
"data": [
{
"type": "M3UPLAYLIST",
"url": "https://iptv-org.github.io/iptv/index.m3u"
"url": "https://iptv-org.github.io/iptv/index.m3u",
}
],
"window_positions": {
"channel_list": {"x": 1250, "y": 100, "width": 400, "height": 800},
"video_player": {"x": 50, "y": 100, "width": 1200, "height": 800}
}
"video_player": {"x": 50, "y": 100, "width": 1200, "height": 800},
},
}

def save_config(self):
with open('config.json', 'w') as f:
with open("config.json", "w") as f:
json.dump(self.config, f)

def save_window_settings(self):
Expand All @@ -144,7 +200,7 @@ def save_window_settings(self):
"x": pos.x(),
"y": pos.y(),
"width": pos.width(),
"height": pos.height()
"height": pos.height(),
}
self.config["window_positions"] = window_positions
self.save_config()
Expand All @@ -156,7 +212,7 @@ def apply_window_settings(self):
channel_list_pos.get("x", 1250),
channel_list_pos.get("y", 100),
channel_list_pos.get("width", 400),
channel_list_pos.get("height", 800)
channel_list_pos.get("height", 800),
)

def load_channels(self):
Expand Down Expand Up @@ -225,7 +281,9 @@ def parse_m3u(data):
tvg_id = tvg_id_match.group(1) if tvg_id_match else None
tvg_logo = tvg_logo_match.group(1) if tvg_logo_match else None
group_title = group_title_match.group(1) if group_title_match else None
channel_name = channel_name_match.group(1) if channel_name_match else None
channel_name = (
channel_name_match.group(1) if channel_name_match else None
)

id += 1
channel = {"id": id, "name": channel_name}
Expand All @@ -235,7 +293,11 @@ def parse_m3u(data):
return result

def do_handshake(self, url, mac, serverload="/server/load.php", load=True):
token = self.config.get("token") if self.config.get("token") else self.random_token(self)
token = (
self.config.get("token")
if self.config.get("token")
else self.random_token(self)
)
options = self.create_options(url, mac, token)
try:
fetchurl = f"{url}{serverload}?type=stb&action=handshake&prehash=0&token={token}&JsHttpRequest=1-xml"
Expand Down Expand Up @@ -280,14 +342,16 @@ def create_link(self, cmd):
fetchurl = f"{url}/server/load.php?type=itv&action=create_link&type=itv&cmd={requests.utils.quote(cmd)}&JsHttpRequest=1-xml"
response = requests.get(fetchurl, headers=options["headers"])
result = response.json()
link = result["js"]["cmd"].split(' ')[-1]
link = result["js"]["cmd"].split(" ")[-1]
self.link = link
return link
except Exception as e:
print(f"Error creating link: {e}")
return None

@staticmethod
def random_token(self):
return ''.join(random.choices(string.ascii_letters + string.digits, k=32))
return "".join(random.choices(string.ascii_letters + string.digits, k=32))

@staticmethod
def create_options(url, mac, token):
Expand All @@ -303,7 +367,7 @@ def create_options(url, mac, token):
"Accept": "*/*",
"Referer": f"{url}/c/" if not url.path else f"{url}/",
"Cookie": f"mac={mac}; stb_lang=en; timezone=Europe/Kiev; PHPSESSID=null;",
"Authorization": f"Bearer {token}"
"Authorization": f"Bearer {token}",
}
}
return options
Expand Down
4 changes: 1 addition & 3 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import sys

from PyQt5.QtWidgets import (
QApplication
)
from PyQt5.QtWidgets import QApplication

from video_player import VideoPlayer
from channel_list import ChannelList
Expand Down
38 changes: 29 additions & 9 deletions options.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
from PyQt5.QtWidgets import (
QFileDialog, QPushButton, QLineEdit, QDialog, QLabel, QFormLayout, QRadioButton, QButtonGroup, QComboBox
QFileDialog,
QPushButton,
QLineEdit,
QDialog,
QLabel,
QFormLayout,
QRadioButton,
QButtonGroup,
QComboBox,
)


Expand Down Expand Up @@ -96,7 +104,9 @@ def update_radio_buttons(self):
def update_inputs(self):
self.mac_label.setVisible(self.type_STB.isChecked())
self.mac_input.setVisible(self.type_STB.isChecked())
self.file_button.setVisible(self.type_M3UPLAYLIST.isChecked() or self.type_M3USTREAM.isChecked())
self.file_button.setVisible(
self.type_M3UPLAYLIST.isChecked() or self.type_M3USTREAM.isChecked()
)
self.url_input.setEnabled(True)

def add_new_provider(self):
Expand All @@ -115,14 +125,18 @@ def remove_provider(self):
def save_settings(self):
if self.selected_provider:
self.selected_provider["url"] = self.url_input.text()
self.selected_provider["mac"] = self.mac_input.text() if self.type_STB.isChecked() else ""
self.selected_provider["mac"] = (
self.mac_input.text() if self.type_STB.isChecked() else ""
)
self.selected_provider["type"] = (
"STB" if self.type_STB.isChecked() else
"M3UPLAYLIST" if self.type_M3UPLAYLIST.isChecked() else
"M3USTREAM"
"STB"
if self.type_STB.isChecked()
else "M3UPLAYLIST" if self.type_M3UPLAYLIST.isChecked() else "M3USTREAM"
)
self.parent().save_config()
self.parent().do_handshake(self.url_input.text(), self.mac_input.text(), load=True)
self.parent().do_handshake(
self.url_input.text(), self.mac_input.text(), load=True
)
# self.parent().load_channels()
self.accept()

Expand All @@ -138,8 +152,14 @@ def verify_provider(self):
self.verify_result.repaint()
result = False
if self.type_STB.isChecked():
result = self.parent().do_handshake(self.url_input.text(), self.mac_input.text(), load=False)
result = self.parent().do_handshake(
self.url_input.text(), self.mac_input.text(), load=False
)
elif self.type_M3UPLAYLIST.isChecked() or self.type_M3USTREAM.isChecked():
result = self.parent().verify_url(self.url_input.text())
self.verify_result.setText("Provider verified successfully." if result else "Failed to verify provider.")
self.verify_result.setText(
"Provider verified successfully."
if result
else "Failed to verify provider."
)
self.verify_result.setStyleSheet("color: green;" if result else "color: red;")
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ PyQt5-stubs
python-vlc==3.0.20123
requests==2.31.0
urllib3==1.26.6
URLObject
URLObject
m3u-parser
19 changes: 10 additions & 9 deletions video_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def __init__(self):
print(f"Exception occurred while creating VLC media player: {e}")
raise

self.proxy_server = None
self.fullscreen = False

# Main widget and layout
self.widget = QWidget(self)
Expand All @@ -47,7 +47,7 @@ def __init__(self):
self.apply_window_settings()

def toggle_fullscreen(self):
if self.isFullScreen():
if self.fullscreen:
self.showNormal()
self.setWindowFlags(self.windowFlags() & ~Qt.FramelessWindowHint)
self.show()
Expand Down Expand Up @@ -95,7 +95,7 @@ def toggle_play_pause(self):

def load_config(self):
try:
with open('config.json', 'r') as f:
with open("config.json", "r") as f:
self.config = json.load(f)
if self.config is None:
self.config = self.default_config()
Expand All @@ -110,17 +110,17 @@ def default_config():
"data": [
{
"type": "M3UPLAYLIST",
"url": "https://iptv-org.github.io/iptv/index.m3u"
"url": "https://iptv-org.github.io/iptv/index.m3u",
}
],
"window_positions": {
"channel_list": {"x": 1250, "y": 100, "width": 400, "height": 800},
"video_player": {"x": 50, "y": 100, "width": 1200, "height": 800}
}
"video_player": {"x": 50, "y": 100, "width": 1200, "height": 800},
},
}

def save_config(self):
with open('config.json', 'w') as f:
with open("config.json", "w") as f:
json.dump(self.config, f)

def save_window_settings(self):
Expand All @@ -130,7 +130,7 @@ def save_window_settings(self):
"x": pos.x(),
"y": pos.y(),
"width": pos.width(),
"height": pos.height()
"height": pos.height(),
}
self.config["window_positions"] = window_positions
self.save_config()
Expand All @@ -142,7 +142,7 @@ def apply_window_settings(self):
video_player_pos.get("x", 50),
video_player_pos.get("y", 100),
video_player_pos.get("width", 1200),
video_player_pos.get("height", 800)
video_player_pos.get("height", 800),
)


Expand All @@ -152,4 +152,5 @@ def __init__(self, parent=None):
self.player = parent # Store the VideoPlayer instance

def mouseDoubleClickEvent(self, event):
self.player.fullscreen = not self.player.fullscreen
self.player.toggle_fullscreen() # Call the method on VideoPlayer instance

0 comments on commit 2ddb2ee

Please sign in to comment.