Skip to content

Feat: add gui #275

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

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@ list_proxy.txt
cmd.txt
bot_config.json
scripts.json
active_requests.json
active_requests.json
.idea/
.vscode/
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,25 @@ Install directly from PyPI:
pip install StreamingCommunity
```

### Creating a Run Script
### Running Script

Create `run_streaming.py`:
Run the script:
```bash
python run_streaming.py
```

### Running the gui

```python
from StreamingCommunity.run import main
Install requirements:

if __name__ == "__main__":
main()
```bash
pip install -r requirements.txt
```

Run the script:
Run the gui:

```bash
python run_streaming.py
python streaming_gui.py
```

### Updating via PyPI
Expand Down
Empty file added gui/__init__.py
Empty file.
39 changes: 39 additions & 0 deletions gui/main_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout
from PyQt5.QtCore import QProcess
import sys
from .tabs.run_tab import RunTab
from .utils.stream_redirect import Stream


class StreamingGUI(QMainWindow):
def __init__(self):
super().__init__()
self.process = None
self.init_ui()
self.setup_output_redirect()

def init_ui(self):
self.setWindowTitle("StreamingCommunity GUI")
self.setGeometry(100, 100, 1000, 700)

central_widget = QWidget()
main_layout = QVBoxLayout()

self.run_tab = RunTab(self)
main_layout.addWidget(self.run_tab)

central_widget.setLayout(main_layout)
self.setCentralWidget(central_widget)

def setup_output_redirect(self):
self.stdout_stream = Stream()
self.stdout_stream.newText.connect(self.run_tab.update_output)
sys.stdout = self.stdout_stream

def closeEvent(self, event):
if self.process and self.process.state() == QProcess.Running:
self.process.terminate()
if not self.process.waitForFinished(1000):
self.process.kill()
sys.stdout = sys.__stdout__
event.accept()
Empty file added gui/tabs/__init__.py
Empty file.
281 changes: 281 additions & 0 deletions gui/tabs/run_tab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
from PyQt5.QtWidgets import (
QWidget,
QVBoxLayout,
QHBoxLayout,
QTabWidget,
QGroupBox,
QFormLayout,
QLineEdit,
QComboBox,
QPushButton,
QCheckBox,
QLabel,
QTextEdit,
)
from PyQt5.QtCore import Qt, QProcess
from ..widgets.results_table import ResultsTable
from ..utils.site_manager import sites
import sys


class RunTab(QTabWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
self.process = None
self.current_context = None
self.selected_season = None
self.buffer = ""
self.init_ui()

def init_ui(self):
run_tab = QWidget()
run_layout = QVBoxLayout()

# Add search group
run_layout.addWidget(self.create_search_group())

# Add control buttons
run_layout.addLayout(self.create_control_layout())

# Add status label
self.status_label = QLabel("Richiesta in corso...")
self.status_label.setAlignment(Qt.AlignCenter)
self.status_label.hide()
run_layout.addWidget(self.status_label)

# Add output group
run_layout.addWidget(self.create_output_group())

run_tab.setLayout(run_layout)
self.addTab(run_tab, "Esecuzione")

def create_search_group(self):
search_group = QGroupBox("Parametri di Ricerca")
search_layout = QFormLayout()

self.search_terms = QLineEdit()
search_layout.addRow("Termini di ricerca:", self.search_terms)

self.site_combo = QComboBox()
for site in sites:
self.site_combo.addItem(f"{site['name']}", site["index"])
self.site_combo.setItemData(site["index"], site["flag"], Qt.ToolTipRole)
if self.site_combo.count() > 0:
self.site_combo.setCurrentIndex(0)

search_layout.addRow("Seleziona sito:", self.site_combo)
search_group.setLayout(search_layout)
return search_group

def create_control_layout(self):
control_layout = QHBoxLayout()

self.run_button = QPushButton("Esegui Script")
self.run_button.clicked.connect(self.run_script)
control_layout.addWidget(self.run_button)

self.stop_button = QPushButton("Ferma Script")
self.stop_button.clicked.connect(self.stop_script)
self.stop_button.setEnabled(False)
control_layout.addWidget(self.stop_button)

self.console_checkbox = QCheckBox("Mostra Console")
self.console_checkbox.setChecked(False)
self.console_checkbox.stateChanged.connect(self.toggle_console)
control_layout.addWidget(self.console_checkbox)

return control_layout

def create_output_group(self):
output_group = QGroupBox("Output")
output_layout = QVBoxLayout()

self.results_table = ResultsTable()
output_layout.addWidget(self.results_table)

self.output_text = QTextEdit()
self.output_text.setReadOnly(True)
self.output_text.hide()
output_layout.addWidget(self.output_text)

input_layout = QHBoxLayout()
self.input_field = QLineEdit()
self.input_field.setPlaceholderText("Inserisci l'indice del media...")
self.input_field.returnPressed.connect(self.send_input)
self.send_button = QPushButton("Invia")
self.send_button.clicked.connect(self.send_input)

self.input_field.hide()
self.send_button.hide()

input_layout.addWidget(self.input_field)
input_layout.addWidget(self.send_button)
output_layout.addLayout(input_layout)

output_group.setLayout(output_layout)
return output_group

def toggle_console(self, state):
self.output_text.setVisible(state == Qt.Checked)
self.results_table.setVisible(state == Qt.Checked)

def run_script(self):
if self.process is not None and self.process.state() == QProcess.Running:
print("Script già in esecuzione.")
return

self.current_context = None
self.selected_season = None
self.buffer = ""
self.results_table.setVisible(False)
self.status_label.setText("Richiesta in corso...")
self.status_label.show()

args = []
search_terms = self.search_terms.text()
if search_terms:
args.extend(["-s", search_terms])

site_index = self.site_combo.currentIndex()
if site_index >= 0:
site_text = sites[site_index]["flag"]
site_name = site_text.split()[0].upper()
args.append(f"-{site_name}")

self.output_text.clear()
print(f"Avvio script con argomenti: {' '.join(args)}")

self.process = QProcess()
self.process.readyReadStandardOutput.connect(self.handle_stdout)
self.process.readyReadStandardError.connect(self.handle_stderr)
self.process.finished.connect(self.process_finished)

python_executable = sys.executable
script_path = "run_streaming.py"

self.process.start(python_executable, [script_path] + args)
self.run_button.setEnabled(False)
self.stop_button.setEnabled(True)

def handle_stdout(self):
data = self.process.readAllStandardOutput()
stdout = bytes(data).decode("utf8", errors="replace")
self.update_output(stdout)

self.buffer += stdout

if "Episodes find:" in self.buffer:
self.current_context = "episodes"
self.input_field.setPlaceholderText(
"Inserisci l'indice dell'episodio (es: 1, *, 1-2, 3-*)"
)
elif "Seasons found:" in self.buffer:
self.current_context = "seasons"
self.input_field.setPlaceholderText(
"Inserisci il numero della stagione (es: 1, *, 1-2, 3-*)"
)

if "Episodes find:" in self.buffer:
self.results_table.hide()
self.current_context = "episodes"
text_to_show = f"Trovati {self.buffer.split('Episodes find:')[1].split()[0]} episodi!"
self.status_label.setText(text_to_show)
self.status_label.show()
elif (("┏" in self.buffer or "┌" in self.buffer) and
("┗" in self.buffer or "┛" in self.buffer or "└" in self.buffer)) or "Seasons found:" in self.buffer:
self.parse_and_show_results(self.buffer)

if "Insert" in self.buffer:
self.input_field.show()
self.send_button.show()
self.input_field.setFocus()
self.output_text.verticalScrollBar().setValue(
self.output_text.verticalScrollBar().maximum()
)

def parse_and_show_results(self, text):
if "Seasons found:" in text and not "Insert media index (e.g., 1)" in text:
self.status_label.hide()
num_seasons = int(text.split("Seasons found:")[1].split()[0])
self.results_table.update_with_seasons(num_seasons)
return

if ("┏━━━━━━━┳" in text or "┌───────┬" in text) and "└───────┴" in text:
chars_to_find = []
if "┏" in text:
chars_to_find.append("┏")
chars_to_find.append("┃")
elif "┌" in text:
chars_to_find.append("┌")
chars_to_find.append("│")

if not chars_to_find or len(chars_to_find) == 0:
return
self.status_label.hide()
table_lines = text[text.find(chars_to_find[0]) : text.find("└")].split("\n")
headers = [h.strip() for h in table_lines[1].split(chars_to_find[1])[1:-1]]

rows = []
for line in table_lines[3:]:
if line.strip() and "│" in line:
cells = [cell.strip() for cell in line.split("│")[1:-1]]
rows.append(cells)

self.results_table.update_with_results(headers, rows)

def send_input(self):
if not self.process or self.process.state() != QProcess.Running:
return

user_input = self.input_field.text().strip()

if self.current_context == "seasons":
if "-" in user_input or user_input == "*":
self.results_table.hide()
else:
self.selected_season = user_input

elif self.current_context == "episodes":
if "-" in user_input or user_input == "*":
self.results_table.hide()

self.process.write(f"{user_input}\n".encode())
self.input_field.clear()
self.input_field.hide()
self.send_button.hide()

if self.current_context == "seasons" and not (
"-" in user_input or user_input == "*"
):
self.status_label.setText("Caricamento episodi...")
self.status_label.show()

def handle_stderr(self):
data = self.process.readAllStandardError()
stderr = bytes(data).decode("utf8", errors="replace")
self.update_output(stderr)

def process_finished(self):
self.run_button.setEnabled(True)
self.stop_button.setEnabled(False)
self.input_field.hide()
self.send_button.hide()
self.status_label.hide()
print("Script terminato.")

def update_output(self, text):
cursor = self.output_text.textCursor()
cursor.movePosition(cursor.End)
cursor.insertText(text)
self.output_text.setTextCursor(cursor)
self.output_text.ensureCursorVisible()

def stop_script(self):
if self.process is not None and self.process.state() == QProcess.Running:
self.process.terminate()
if not self.process.waitForFinished(3000):
self.process.kill()
print("Script terminato.")
self.run_button.setEnabled(True)
self.stop_button.setEnabled(False)
Empty file added gui/utils/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions gui/utils/site_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from StreamingCommunity.run import load_search_functions


def get_sites():
search_functions = load_search_functions()
sites = []
for alias, (_, use_for) in search_functions.items():
sites.append(
{
"index": len(sites),
"name": alias.split("_")[0],
"flag": alias[:3].upper(),
}
)
return sites


sites = get_sites()
13 changes: 13 additions & 0 deletions gui/utils/stream_redirect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from PyQt5.QtCore import QObject, pyqtSignal


class Stream(QObject):
"""Redirect script output to GUI"""

newText = pyqtSignal(str)

def write(self, text):
self.newText.emit(str(text))

def flush(self):
pass
Empty file added gui/widgets/__init__.py
Empty file.
Loading