import sys
import os
import configparser
import keyboard
from [Link] import (QApplication, QMainWindow, QWidget, QLabel,
QPushButton, QVBoxLayout, QHBoxLayout, QFrame,
QColorDialog, QRadioButton, QButtonGroup,
QScrollArea)
from [Link] import Qt, QTimer
from [Link] import QColor, QFont
import vlc
class OverlayWindow(QWidget):
def __init__(self):
super().__init__()
[Link]([Link] | [Link] |
[Link])
[Link](Qt.WA_TranslucentBackground)
# Inisialisasi Label Cover
self.cover_atas = QLabel(self)
self.cover_bawah = QLabel(self)
self.cover_kiri = QLabel(self)
self.cover_kanan = QLabel(self)
# Guide untuk debugging
self.video_guide = QFrame(self)
self.video_guide.setStyleSheet("border: 2px solid #00FF00;")
self.guide_atas = QFrame(self)
self.guide_atas.setStyleSheet("border: 1px solid #FF0000;")
self.guide_bawah = QFrame(self)
self.guide_bawah.setStyleSheet("border: 1px solid #0000FF;")
self.guide_kiri = QFrame(self)
self.guide_kiri.setStyleSheet("border: 1px solid #FFFF00;")
self.guide_kanan = QFrame(self)
self.guide_kanan.setStyleSheet("border: 1px solid #FF00FF;")
self.text_label = QLabel(self)
self.text_label.hide()
# Sembunyikan semua guide di awal
for g in [self.video_guide, self.guide_atas, self.guide_bawah,
self.guide_kiri, self.guide_kanan]:
[Link]()
[Link]()
class VideoPlayerApp(QMainWindow):
def __init__(self):
super().__init__()
self.config_file = "[Link]"
self.video_dir = [Link]([Link](), "video")
self.video_list = sorted([f for f in [Link](self.video_dir) if
[Link]('.mp4')])
self.current_index = 0
[Link] = 10
self.is_debugging = False
self.load_config()
self.setup_vlc_instance()
[Link]([Link] | [Link])
[Link]("background-color: black;")
self.video_frame = QFrame(self)
if [Link] == "win32":
[Link].set_hwnd(int(self.video_frame.winId()))
[Link] = OverlayWindow()
[Link]()
self.apply_changes()
self.init_nav_ui()
keyboard.add_hotkey('a', lambda: [Link](0,
self.play_custom_next))
keyboard.add_hotkey('b', lambda: [Link](0, self.play_intro))
keyboard.add_hotkey('d', lambda: [Link](0, self.toggle_nav))
self.monitor_timer = QTimer()
self.monitor_timer.[Link](self.update_logic_tick)
self.monitor_timer.start(500)
[Link](500, self.play_intro)
def setup_vlc_instance(self):
args = ["--no-xlib", "--quiet", "--no-video-title-show"]
rotation_map = {90: "90", 180: "180", 270: "270"}
if self.video_rotation in rotation_map:
[Link]("--video-filter=transform")
[Link](f"--transform-type={rotation_map[self.video_rotation]}")
[Link] = [Link](args)
[Link] = [Link].media_player_new()
def load_config(self):
[Link] = [Link](allow_no_value=True)
self.header_text = ";
==========================================================================\n;
DOKUMENTASI KONFIGURASI VIDEO PLAYER\n;
==========================================================================\n"
if not [Link](self.config_file):
with open(self.config_file, 'w') as f:
[Link](self.header_text)
[Link]("[myPlayer]\nx = 0\ny = 0\nwidth = 1920\nrotation =
0\n\n")
[Link]("[cover]\natasHeight = 200\nfadingAtasHeight =
100\ncolorAtas = #550000\nrotAtas = 0\n"
"bawahHeight = 200\nfadingBawahHeight = 100\ncolorBawah
= #00ff00\nrotBawah = 0\n"
"kiriWidth = 200\nfadingKiriWidth = 100\ncolorKiri =
#0000ff\nrotKiri = 0\n"
"kananWidth = 200\nfadingKananWidth = 100\ncolorKanan =
#ff00ff\nrotKanan = 0\n\n")
# Default video data omitted for brevity, logic remains same
[Link](self.config_file)
# Player settings
self.player_x = [Link]('myPlayer', 'x', fallback=0.0)
self.player_y = [Link]('myPlayer', 'y', fallback=0.0)
self.player_w = [Link]('myPlayer', 'width',
fallback=1920.0)
self.video_rotation = [Link]('myPlayer', 'rotation',
fallback=0)
# Cover settings
c = 'cover'
self.cover_atas_height = [Link](c, 'atasHeight',
fallback=200.0)
self.fading_atas_height = [Link](c, 'fadingAtasHeight',
fallback=100.0)
self.color_atas = [Link](c, 'colorAtas', fallback="#550000")
self.rot_atas = [Link](c, 'rotAtas', fallback=0)
self.cover_bawah_height = [Link](c, 'bawahHeight',
fallback=200.0)
self.fading_bawah_height = [Link](c, 'fadingBawahHeight',
fallback=100.0)
self.color_bawah = [Link](c, 'colorBawah', fallback="#00ff00")
self.rot_bawah = [Link](c, 'rotBawah', fallback=0)
self.cover_kiri_width = [Link](c, 'kiriWidth',
fallback=200.0)
self.fading_kiri_width = [Link](c, 'fadingKiriWidth',
fallback=100.0)
self.color_kiri = [Link](c, 'colorKiri', fallback="#0000ff")
self.rot_kiri = [Link](c, 'rotKiri', fallback=0)
self.cover_kanan_width = [Link](c, 'kananWidth',
fallback=200.0)
self.fading_kanan_width = [Link](c, 'fadingKananWidth',
fallback=100.0)
self.color_kanan = [Link](c, 'colorKanan', fallback="#ff00ff")
self.rot_kanan = [Link](c, 'rotKanan', fallback=0)
def save_config(self):
if 'myPlayer' not in [Link]: [Link]['myPlayer'] = {}
[Link]['myPlayer'].update({
'x': str(self.player_x), 'y': str(self.player_y), 'width':
str(self.player_w), 'rotation': str(self.video_rotation)
})
if 'cover' not in [Link]: [Link]['cover'] = {}
[Link]['cover'].update({
'atasHeight': str(self.cover_atas_height), 'fadingAtasHeight':
str(self.fading_atas_height), 'colorAtas': self.color_atas, 'rotAtas':
str(self.rot_atas),
'bawahHeight': str(self.cover_bawah_height), 'fadingBawahHeight':
str(self.fading_bawah_height), 'colorBawah': self.color_bawah, 'rotBawah':
str(self.rot_bawah),
'kiriWidth': str(self.cover_kiri_width), 'fadingKiriWidth':
str(self.fading_kiri_width), 'colorKiri': self.color_kiri, 'rotKiri':
str(self.rot_kiri),
'kananWidth': str(self.cover_kanan_width), 'fadingKananWidth':
str(self.fading_kanan_width), 'colorKanan': self.color_kanan, 'rotKanan':
str(self.rot_kanan)
})
with open(self.config_file, 'w') as f:
[Link](self.header_text)
[Link](f)
def rotate_logic(self, target):
rotations = [0, 90, 180, 270]
if target == 'video':
curr_idx = [Link](self.video_rotation)
self.video_rotation = rotations[(curr_idx + 1) % len(rotations)]
curr_time = [Link].get_time()
[Link]()
self.setup_vlc_instance()
if [Link] == "win32":
[Link].set_hwnd(int(self.video_frame.winId()))
self.play_video(self.current_index)
[Link].set_time(curr_time)
else:
attr = f"rot_{target}"
curr_val = getattr(self, attr)
curr_idx = [Link](curr_val)
setattr(self, attr, rotations[(curr_idx + 1) % len(rotations)])
self.apply_changes()
self.update_nav_labels()
def get_gradient(self, side, color_hex, total, fading, rotation):
c = QColor(color_hex)
r, g, b = [Link](), [Link](), [Link]()
# Perhitungan stop gradient
val = (fading / total) if total > 0 else 0.5
stop = max(0.0, min(1.0, val))
# Mapping arah gradient berdasarkan rotasi visual
# Default (0) Atas: Top->Bottom, Bawah: Bottom->Top, Kiri: Left->Right,
Kanan: Right->Left
dirs = {
'atas': ("x1:0, y1:0, x2:0, y2:1", f"stop:0 {color_hex},
stop:{1.0-stop:.2f} {color_hex}, stop:1 rgba({r},{g},{b},0)"),
'bawah': ("x1:0, y1:0, x2:0, y2:1", f"stop:0 rgba({r},{g},{b},0),
stop:{stop:.2f} {color_hex}, stop:1 {color_hex}"),
'kiri': ("x1:0, y1:0, x2:1, y2:0", f"stop:0 {color_hex},
stop:{1.0-stop:.2f} {color_hex}, stop:1 rgba({r},{g},{b},0)"),
'kanan': ("x1:0, y1:0, x2:1, y2:0", f"stop:0 rgba({r},{g},{b},0),
stop:{stop:.2f} {color_hex}, stop:1 {color_hex}")
}
# Catatan: Rotasi disini merubah arah gradient relatif terhadap box-nya
# Jika rotasi 180, gradient dibalik
style = f"background-color: qlineargradient({dirs[side][0]},
{dirs[side][1]});"
if rotation != 0:
style += f" border: none; transform: rotate({rotation}deg);"
return style
def apply_changes(self):
sw, sh = [Link]().size().width(),
[Link]().size().height()
self.video_frame.setGeometry(int(self.player_x), int(self.player_y),
int(self.player_w), sh)
# Update Geometries
[Link].cover_atas.setGeometry(0, 0, sw,
int(self.cover_atas_height))
[Link].cover_bawah.setGeometry(0, sh -
int(self.cover_bawah_height), sw, int(self.cover_bawah_height))
[Link].cover_kiri.setGeometry(0, 0, int(self.cover_kiri_width),
sh)
[Link].cover_kanan.setGeometry(sw - int(self.cover_kanan_width),
0, int(self.cover_kanan_width), sh)
# Apply Styles & Gradients
[Link].cover_atas.setStyleSheet(self.get_gradient('atas',
self.color_atas, self.cover_atas_height, self.fading_atas_height,
self.rot_atas))
[Link].cover_bawah.setStyleSheet(self.get_gradient('bawah',
self.color_bawah, self.cover_bawah_height, self.fading_bawah_height,
self.rot_bawah))
[Link].cover_kiri.setStyleSheet(self.get_gradient('kiri',
self.color_kiri, self.cover_kiri_width, self.fading_kiri_width, self.rot_kiri))
[Link].cover_kanan.setStyleSheet(self.get_gradient('kanan',
self.color_kanan, self.cover_kanan_width, self.fading_kanan_width,
self.rot_kanan))
if self.is_debugging:
[Link].video_guide.setGeometry(self.video_frame.geometry())
[Link].guide_atas.setGeometry([Link].cover_atas.geometry())
[Link].guide_bawah.setGeometry([Link].cover_bawah.geometry())
[Link].guide_kiri.setGeometry([Link].cover_kiri.geometry())
[Link].guide_kanan.setGeometry([Link].cover_kanan.geometry())
for g in [[Link].video_guide, [Link].guide_atas,
[Link].guide_bawah, [Link].guide_kiri, [Link].guide_kanan]:
[Link]()
else:
for g in [[Link].video_guide, [Link].guide_atas,
[Link].guide_bawah, [Link].guide_kiri, [Link].guide_kanan]:
[Link]()
def init_nav_ui(self):
self.nav_window = QWidget()
self.nav_window.setWindowTitle("Panel Kontrol")
self.nav_window.setWindowFlags([Link] | [Link])
self.nav_window.setFixedSize(500, 850)
main_layout = QVBoxLayout()
scroll = QScrollArea()
[Link](True)
content_widget = QWidget()
layout = QVBoxLayout(content_widget)
# Step Selection
step_layout = QHBoxLayout()
self.radio01, self.radio1, self.radio10 = QRadioButton("0.1 px"),
QRadioButton("1 px"), QRadioButton("10 px")
[Link](True)
self.radio_group = QButtonGroup()
for r in [self.radio01, self.radio1, self.radio10]:
self.radio_group.addButton(r); step_layout.addWidget(r)
[Link](lambda: setattr(self, 'step', 0.1))
[Link](lambda: setattr(self, 'step', 1))
[Link](lambda: setattr(self, 'step', 10))
[Link](step_layout)
# Global Video Controls
v_rot_layout = QHBoxLayout()
btn_v_rot = QPushButton("Rotate Video 90°")
btn_v_rot.[Link](lambda: self.rotate_logic('video'))
self.lbl_v_rot = QLabel("0°")
v_rot_layout.addWidget(btn_v_rot);
v_rot_layout.addWidget(self.lbl_v_rot)
[Link](v_rot_layout)
[Link] = {}
# Param: (Display Name, Attribute Name, Type['color','rot','val'],
TargetKey)
controls = [
("X-Video", "player_x", "val"), ("Y-Video", "player_y", "val"),
("W-Video", "player_w", "val"),
("--- ATAS ---", None, "sep"), ("H-Atas", "cover_atas_height",
"val"), ("F-Atas", "fading_atas_height", "val"), ("Warna Atas", "color_atas",
"color", "atas"), ("Rot Atas", "rot_atas", "rot", "atas"),
("--- BAWAH ---", None, "sep"), ("H-Bawah", "cover_bawah_height",
"val"), ("F-Bawah", "fading_bawah_height", "val"), ("Warna Bawah",
"color_bawah", "color", "bawah"), ("Rot Bawah", "rot_bawah", "rot", "bawah"),
("--- KIRI ---", None, "sep"), ("W-Kiri", "cover_kiri_width",
"val"), ("F-Kiri", "fading_kiri_width", "val"), ("Warna Kiri", "color_kiri",
"color", "kiri"), ("Rot Kiri", "rot_kiri", "rot", "kiri"),
("--- KANAN ---", None, "sep"), ("W-Kanan", "cover_kanan_width",
"val"), ("F-Kanan", "fading_kanan_width", "val"), ("Warna Kanan", "color_kanan",
"color", "kanan"), ("Rot Kanan", "rot_kanan", "rot", "kanan")
]
for name, attr, ctype, *extra in controls:
if ctype == "sep":
lbl = QLabel(name); [Link]("font-weight: bold;
margin-top: 10px; color: yellow;"); [Link](lbl)
continue
row = QHBoxLayout()
[Link](QLabel(f"{name}:"))
if ctype == "val":
m, p = QPushButton("-"), QPushButton("+")
[Link](lambda ch, a=attr: self.mod_val(a, -1));
[Link](lambda ch, a=attr: self.mod_val(a, 1))
disp = QLabel("0"); [Link][attr] = disp
[Link](m); [Link](p); [Link](disp)
elif ctype == "color":
btn = QPushButton("Pilih Warna")
[Link](lambda ch, t=extra[0]: self.pick_color(t))
[Link](btn)
elif ctype == "rot":
btn = QPushButton("Rot 90°")
[Link](lambda ch, t=extra[0]: self.rotate_logic(t))
disp = QLabel("0°"); [Link][attr] = disp
[Link](btn); [Link](disp)
[Link](row)
[Link](content_widget)
main_layout.addWidget(scroll)
self.nav_window.setLayout(main_layout)
def mod_val(self, attr, direction):
val = getattr(self, attr) + (direction * [Link])
if "fading" in attr:
# Clamp fading agar tidak melebihi total height/width
base = [Link]("fading_", "").replace("Height",
"height").replace("Width", "width")
limit = getattr(self, base, 1000)
val = max(0.0, min(val, limit))
elif any(x in attr for x in ["height", "width"]):
val = max(1.0, val)
setattr(self, attr, round(val, 1)); self.apply_changes();
self.update_nav_labels()
def update_nav_labels(self):
for a, l in [Link]():
val = getattr(self, a)
[Link](f"{val}°" if "rot" in a else f"{val}")
self.lbl_v_rot.setText(f"{self.video_rotation}°")
def toggle_nav(self):
if self.nav_window.isVisible(): self.is_debugging = False;
self.nav_window.hide()
else: self.is_debugging = True; self.nav_window.show();
self.update_nav_labels()
self.apply_changes()
def pick_color(self, target):
attr = f"color_{target}"
current = getattr(self, attr)
color = [Link](QColor(current), self, f"Warna {target}")
if [Link]():
setattr(self, attr, [Link]())
self.apply_changes()
def update_logic_tick(self):
if not [Link].is_playing():
if [Link].get_state() == [Link]:
self.handle_navigation_logic()
return
curr_ms, total_ms = [Link].get_time(), [Link].get_length()
if total_ms > 0:
rem_pct = 100 - ((curr_ms / total_ms) * 100)
conf = [Link][f'video_{self.current_index}']
if [Link]('text_enable', fallback=False):
trigger, duration = float([Link]('text_trigger_pct', 10)),
float([Link]('text_duration_pct', 5))
if trigger >= rem_pct >= (trigger - duration):
self.show_overlay_text(conf)
else: [Link].text_label.hide()
def show_overlay_text(self, conf):
lbl = [Link].text_label
[Link]([Link]('text_content').replace('\\n', '\n'))
[Link]([Link])
[Link](f"background-color: {[Link]('text_bg')}; color:
{[Link]('text_color')}; font-size: {[Link]('text_size')}px; padding: 20px;
border-radius: 10px;")
[Link]([Link]('text_x'), [Link]('text_y'))
[Link](); [Link]()
def handle_navigation_logic(self):
conf = [Link][f'video_{self.current_index}']
mode = [Link]('mode', 'stop').lower()
if mode == 'loop': self.play_video(self.current_index)
elif mode == 'autonext':
next_idx = self.current_index + 1
if f'video_{next_idx}' in [Link]: self.play_video(next_idx)
else: [Link]()
else: [Link]()
def play_custom_next(self):
conf = [Link][f'video_{self.current_index}']
target_idx = [Link]('next_index', fallback=0)
if target_idx == 0 and self.current_index != 0: return
if f'video_{target_idx}' in [Link]: self.play_video(target_idx)
def play_video(self, idx):
self.current_index = idx
if f'video_{idx}' not in [Link]: return
path = [Link](self.video_dir, [Link](f"video_{idx}",
'filename'))
if [Link](path):
m = [Link].media_new(path)
[Link].set_media(m); [Link]()
self.apply_changes()
def play_intro(self): self.play_video(0)
def keyPressEvent(self, e):
if [Link]() == Qt.Key_Escape:
self.save_config(); [Link](); keyboard.unhook_all();
[Link]()
if __name__ == '__main__':
app = QApplication([Link])
if not [Link]("video"): [Link]("video")
ex = VideoPlayerApp()
[Link](app.exec_())