Skip to content
Open
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PPVviaVLC
# PV (formerly PPVviaVLC)

PPVviaVLC enables anyone to broadcast pay-per-view (PPV) live streams from the comfort of their own device using VLC. It tokenizes access to your encrypted VLC stream so viewers must purchase a token to watch.
PV enables anyone to broadcast pay-per-view (PPV) live streams from the comfort of their own device using VLC. It tokenizes access to your encrypted VLC stream so viewers must purchase a token to watch.

## Features
- Uses standard VLC tools to stream directly from your machine.
Expand All @@ -13,5 +13,7 @@ PPVviaVLC enables anyone to broadcast pay-per-view (PPV) live streams from the c
3. Launch your broadcast using VLC.
4. Share your tokens so viewers can watch the live PPV stream.
5. Windows users can run ppv_vlc_setup.py to locate or install VLC.
6. Launch the graphical interface with `python pv_gui.py` to select a video,
generate tokens, and manage your VLC stream.

Remember to only stream content you have rights to share and comply with all local laws regarding pay-per-view broadcasts.
94 changes: 94 additions & 0 deletions pv_gui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
import subprocess
import uuid
import tkinter as tk
from tkinter import filedialog, messagebox

DEFAULT_VLC_COMMAND = "vlc"

class PVGUI(tk.Tk):
def __init__(self):
super().__init__()
self.title("PV Broadcaster")
self.geometry("420x320")
self.stream_path = tk.StringVar()
self.token = tk.StringVar(value=self._generate_token())
self.port = tk.StringVar(value="8080")
self.vlc_path = tk.StringVar(value=DEFAULT_VLC_COMMAND)
self.vlc_proc = None
self._create_widgets()
self.protocol("WM_DELETE_WINDOW", self._on_close)

def _create_widgets(self):
tk.Label(self, text="PV Streaming UI", font=("Helvetica", 14)).pack(pady=10)

tk.Button(self, text="Select video file", command=self._select_file).pack(pady=5)
tk.Label(self, textvariable=self.stream_path).pack(pady=5)

path_frame = tk.Frame(self)
path_frame.pack(pady=5)
tk.Label(path_frame, text="VLC path:").pack(side=tk.LEFT)
tk.Entry(path_frame, textvariable=self.vlc_path, width=30).pack(side=tk.LEFT)
tk.Button(path_frame, text="Browse", command=self._select_vlc).pack(side=tk.LEFT, padx=2)

port_frame = tk.Frame(self)
port_frame.pack(pady=5)
tk.Label(port_frame, text="Port:").pack(side=tk.LEFT)
tk.Entry(port_frame, textvariable=self.port, width=6).pack(side=tk.LEFT)

token_frame = tk.Frame(self)
token_frame.pack(pady=5)
tk.Button(token_frame, text="New token", command=self._refresh_token).pack(side=tk.LEFT)
tk.Entry(token_frame, textvariable=self.token, width=20, state="readonly").pack(side=tk.LEFT, padx=2)
tk.Button(token_frame, text="Copy", command=self._copy_token).pack(side=tk.LEFT)

control_frame = tk.Frame(self)
control_frame.pack(pady=10)
tk.Button(control_frame, text="Start stream", command=self._start_stream).pack(side=tk.LEFT, padx=5)
tk.Button(control_frame, text="Stop stream", command=self._stop_stream).pack(side=tk.LEFT, padx=5)

def _select_file(self):
path = filedialog.askopenfilename()
if path:
self.stream_path.set(path)

def _select_vlc(self):
path = filedialog.askopenfilename(title="Select VLC executable")
if path:
self.vlc_path.set(path)

def _refresh_token(self):
self.token.set(self._generate_token())

def _copy_token(self):
self.clipboard_clear()
self.clipboard_append(self.token.get())

def _generate_token(self):
return uuid.uuid4().hex[:8]

def _start_stream(self):
if not self.stream_path.get():
messagebox.showwarning("Missing file", "Please select a video file to stream.")
return
cmd = [self.vlc_path.get(), self.stream_path.get(), "--sout",
f"#duplicate{{dst=display,dst=std{{access=http,mux=ts,dst=:{self.port.get()}}}}}"]
try:
self.vlc_proc = subprocess.Popen(cmd)
messagebox.showinfo("Streaming", f"Stream started on http://localhost:{self.port.get()}\nToken: {self.token.get()}")
except Exception as e:
messagebox.showerror("Error", str(e))

def _stop_stream(self):
if self.vlc_proc:
self.vlc_proc.terminate()
self.vlc_proc = None
messagebox.showinfo("Stopped", "Streaming stopped")

def _on_close(self):
self._stop_stream()
self.destroy()

if __name__ == "__main__":
app = PVGUI()
app.mainloop()