Spaces:
Sleeping
Sleeping
import os | |
import sys | |
import copy | |
import enum | |
import json | |
import time | |
import random | |
import shutil | |
import zipfile | |
from random import random as rand | |
from typing import List, Tuple, Union | |
from PyQt6 import QtGui, QtCore, QtWidgets, QtMultimedia, QtMultimediaWidgets | |
class InteractiveVariable: | |
""" | |
互动节点的变量 | |
""" | |
def __init__( | |
self, | |
name: str, | |
var_id: str, | |
var_value: int, | |
show: bool = False, | |
random: bool = False, | |
): | |
""" | |
Args: | |
name (str) : 变量名 | |
var_id (str) : 变量 id | |
var_value (int) : 变量的值 | |
show (bool) : 是否显示 | |
random (bool) : 是否为随机值(1-100) | |
""" | |
self.__var_id = var_id | |
self.__var_value = var_value | |
self.__name = name | |
self.__is_show = show | |
self.__random = random | |
def get_id(self) -> str: | |
return self.__var_id | |
def refresh_value(self) -> None: | |
""" | |
刷新变量数值 | |
""" | |
if self.is_random(): | |
self.__var_value = int(rand(0, 100)) | |
def get_value(self) -> int: | |
return self.__var_value | |
def is_show(self) -> bool: | |
return self.__is_show | |
def is_random(self) -> bool: | |
return self.__random | |
def get_name(self) -> str: | |
return self.__name | |
def __str__(self): | |
return f"{self.__name} {self.__var_value}" | |
class InteractiveButtonAlign(enum.Enum): | |
""" | |
按钮的文字在按钮中的位置 | |
``` text | |
----- | |
|xxx|----o (TEXT_LEFT) | |
----- | |
----- | |
o----|xxx| (TEXT_RIGHT) | |
----- | |
---------- | |
|XXXXXXXX| (DEFAULT) | |
---------- | |
``` | |
- DEFAULT | |
- TEXT_UP | |
- TEXT_RIGHT | |
- TEXT_DOWN | |
- TEXT_LEFT | |
""" | |
DEFAULT = 0 | |
TEXT_UP = 1 | |
TEXT_RIGHT = 2 | |
TEXT_DOWN = 3 | |
TEXT_LEFT = 4 | |
class InteractiveButton: | |
""" | |
互动视频节点按钮类 | |
""" | |
def __init__( | |
self, | |
text: str, | |
x: int, | |
y: int, | |
align: Union[InteractiveButtonAlign, int] = InteractiveButtonAlign.DEFAULT, | |
): | |
""" | |
Args: | |
text (str) : 文字 | |
x (int) : x 轴 | |
y (int) : y 轴 | |
align (InteractiveButtonAlign | int): 按钮的文字在按钮中的位置 | |
""" | |
self.__text = text | |
self.__pos = (x, y) | |
if isinstance(align, InteractiveButtonAlign): | |
align = align.value | |
self.__align = align | |
def get_text(self) -> str: | |
return self.__text | |
def get_align(self) -> int: | |
return self.__align # type: ignore | |
def get_pos(self) -> Tuple[int, int]: | |
return self.__pos | |
def __str__(self): | |
return f"{self.__text} {self.__pos}" | |
class InteractiveJumpingCondition: | |
""" | |
节点跳转的公式,只有公式成立才会跳转 | |
""" | |
def __init__(self, var: List[InteractiveVariable] = [], condition: str = "True"): | |
""" | |
Args: | |
var (List[InteractiveVariable]): 所有变量 | |
condition (str) : 公式 | |
""" | |
self.__vars = var | |
self.__command = condition | |
def get_result(self) -> bool: | |
""" | |
计算公式获得结果 | |
Returns: | |
bool: 是否成立 | |
""" | |
if self.__command == "": | |
return True | |
command = copy.copy(self.__command) | |
for var in self.__vars: | |
var_name = var.get_id() | |
var_value = var.get_value() | |
command = command.replace(var_name, str(var_value)) | |
command = command.replace("&&", " and ") | |
command = command.replace("||", " or ") | |
command = command.replace("!", " not ") | |
command = command.replace("===", "==") | |
command = command.replace("!==", "!=") | |
command = command.replace("true", "True") | |
command = command.replace("false", "False") | |
return eval(command) | |
def __str__(self): | |
return f"{self.__command}" | |
class InteractiveJumpingCommand: | |
""" | |
节点跳转对变量的操作 | |
""" | |
def __init__(self, var: List[InteractiveVariable] = [], command: str = ""): | |
""" | |
Args: | |
var (List[InteractiveVariable]): 所有变量 | |
condition (str) : 公式 | |
""" | |
self.__vars = var | |
self.__command = command | |
def run_command(self) -> List["InteractiveVariable"]: | |
""" | |
执行操作 | |
Returns: | |
List[InteractiveVariable] | |
""" | |
if self.__command == "": | |
return self.__vars | |
for code in self.__command.split(";"): | |
var_name_ = code.split("=")[0] | |
var_new_value = code.split("=")[1] | |
for var in self.__vars: | |
var_name = var.get_id() | |
var_value = var.get_value() | |
var_new_value = var_new_value.replace(var_name, str(var_value)) | |
var_new_value_calc = eval(var_new_value) | |
for var in self.__vars: | |
if var.get_id() == var_name_: | |
var._InteractiveVariable__var_value = var_new_value_calc # type: ignore | |
return self.__vars | |
class InteractiveNodeJumpingType(enum.Enum): | |
""" | |
对下一节点的跳转的方式 | |
- ASK : 选择 | |
- DEFAULT: 跳转到默认节点 | |
- READY : 选择(只有一个选择) | |
""" | |
READY = 1 | |
DEFAULT = 0 | |
ASK = 2 | |
class Button: | |
def __init__(self, id_, pos, text, condition, command): | |
# A class that provides the button model. | |
self.node_id = id_ | |
self.pos = pos | |
self.text = text | |
self.condition = condition | |
self.command = command | |
# 什么?别问我为什么不用 TypedDict / dataclass | |
def __str__(self) -> str: | |
return f"{self.pos} {self.text} {self.condition} {self.command}" | |
class ButtonLabel(QtWidgets.QLabel): | |
def __init__(self, parent: QtWidgets.QWidget = None): | |
super().__init__(parent=parent) | |
self.setObjectName(str(time.time())) | |
def prep_text(self, text: str, x: int, y: int): | |
self.setText(text) | |
self.setWordWrap(True) | |
self.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) | |
font = QtGui.QFont() | |
font.setPointSize(12) | |
font.setBold(True) | |
self.setFont(font) | |
rect = QtCore.QRect(x, y, 200, 50) | |
self.setGeometry(rect) | |
self.setStyleSheet( | |
"border-width: 5px;\ | |
border-style: solid;\ | |
border-color: rgb(100, 100, 100);\ | |
background-color: rgb(50, 50, 50);\ | |
color: rgb(255, 255, 255);" | |
) | |
self.raise_() | |
return self | |
class MPlayer(object): | |
def setup(self, Form): | |
# UI | |
Form.setObjectName("Form") | |
Form.resize(800, 600) | |
sizePolicy = QtWidgets.QSizePolicy( | |
QtWidgets.QSizePolicy.Policy.Fixed, QtWidgets.QSizePolicy.Policy.Fixed | |
) | |
sizePolicy.setHorizontalStretch(0) | |
sizePolicy.setVerticalStretch(0) | |
sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth()) | |
Form.setSizePolicy(sizePolicy) | |
Form.setMinimumSize(QtCore.QSize(800, 650)) | |
Form.setMaximumSize(QtCore.QSize(800, 650)) | |
Form.setBaseSize(QtCore.QSize(800, 650)) | |
Form.setWindowTitle("MPlayer") | |
self.win: QtWidgets.QWidget = Form | |
self.player = QtMultimediaWidgets.QVideoWidget(Form) | |
self.player.setGeometry(QtCore.QRect(0, 0, 800, 450)) | |
self.player.setObjectName("player") | |
self.mediaplayer = QtMultimedia.QMediaPlayer() | |
self.mediaplayer.setVideoOutput(self.player) | |
self.audio_output = QtMultimedia.QAudioOutput() | |
self.audio_output.setVolume(0.0) | |
self.mediaplayer.setAudioOutput(self.audio_output) | |
self.slider = QtWidgets.QSlider(Form) | |
self.slider.setGeometry(QtCore.QRect(120, 455, 571, 22)) | |
self.slider.setOrientation(QtCore.Qt.Orientation.Horizontal) | |
self.slider.setObjectName("slider") | |
self.slider.setValue(100) | |
self.pp = QtWidgets.QPushButton(Form) | |
self.pp.setGeometry(QtCore.QRect(0, 450, 113, 32)) | |
self.pp.setObjectName("pp") | |
self.pushButton = QtWidgets.QPushButton(Form) | |
self.pushButton.setGeometry(QtCore.QRect(0, 525, 113, 32)) | |
self.pushButton.setObjectName("pushButton") | |
self.node = QtWidgets.QLabel(Form) | |
self.node.setGeometry(QtCore.QRect(120, 530, 191, 16)) | |
self.node.setObjectName("node") | |
self.info = QtWidgets.QLabel(Form) | |
self.info.setGeometry(QtCore.QRect(320, 520, 471, 36)) | |
self.info.setWordWrap(True) | |
font = QtGui.QFont() | |
font.setBold(True) | |
font.setWeight(75) | |
self.info.setFont(font) | |
self.info.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) | |
self.info.setObjectName("info") | |
self.pushButton_2 = QtWidgets.QPushButton(Form) | |
self.pushButton_2.setGeometry(QtCore.QRect(0, 560, 113, 32)) | |
self.pushButton_2.setObjectName("pushButton_2") | |
self.lineEdit = QtWidgets.QLineEdit(Form) | |
self.lineEdit.setEnabled(True) | |
self.lineEdit.setGeometry(QtCore.QRect(120, 565, 561, 21)) | |
self.lineEdit.setText("") | |
self.lineEdit.setObjectName("lineEdit") | |
self.lineEdit.setPlaceholderText("Type in the path to your ivi file. ") | |
self.pushButton_3 = QtWidgets.QPushButton(Form) | |
self.pushButton_3.setGeometry(QtCore.QRect(690, 560, 108, 32)) | |
self.pushButton_3.setObjectName("pushButton_3") | |
self.pushButton_4 = QtWidgets.QPushButton(Form) | |
self.pushButton_4.setGeometry(QtCore.QRect(0, 485, 113, 32)) | |
self.pushButton_4.setObjectName("pushButton_4") | |
self.horizontalSlider = QtWidgets.QSlider(Form) | |
self.horizontalSlider.setGeometry(QtCore.QRect(120, 490, 571, 22)) | |
self.horizontalSlider.setOrientation(QtCore.Qt.Orientation.Horizontal) | |
self.horizontalSlider.setObjectName("horizontalSlider") | |
self.label = QtWidgets.QLabel(Form) | |
self.label.setGeometry(QtCore.QRect(699, 449, 81, 31)) | |
self.label.setObjectName("label") | |
self.label_2 = QtWidgets.QLabel(Form) | |
self.label_2.setGeometry(QtCore.QRect(700, 490, 60, 16)) | |
self.label_2.setObjectName("label_2") | |
# Slot & String | |
self.pp.setEnabled(False) | |
self.pushButton.setEnabled(False) | |
self.retranslateUi(Form) | |
self.win.closeEvent = self.on_close_check | |
self.pushButton_2.clicked.connect(self.open_ivi) | |
self.pushButton_3.clicked.connect(self.close_ivi) | |
self.pushButton_4.clicked.connect(self.sound_on_off_event) | |
self.pp.clicked.connect(self.pp_button) | |
self.horizontalSlider.valueChanged.connect(self.volume_change_event) | |
self.slider.sliderReleased.connect(self.position_change_event) | |
self.slider.sliderPressed.connect(self.position_start_change_event) | |
self.pushButton.clicked.connect(self.back_to_previous) | |
# InteractiveVariables | |
self.current_node = 0 | |
self.variables: List[InteractiveVariable] = [] | |
self.state_log = [] | |
self.graph = None | |
self.choice_buttons: List[Button] = [] | |
self.choice_labels: List[ButtonLabel] = [] | |
# Video Play Variables & Functions | |
self.temp_dir = "" | |
self.is_draging_slider = False | |
self.is_stoping = False | |
self.win.startTimer(100) | |
self.has_end = False | |
self.final_position = -1 | |
# Timer & Refresh | |
def timerEvent(*args, **kwargs): | |
# 创建要跳转的节点 | |
if self.has_end: | |
if len(self.choice_labels) != 0: | |
for lbl in self.choice_labels: | |
lbl.raise_() | |
self.player.lower() | |
else: | |
children = self.graph[str(self.current_node)]["sub"] | |
if len(children) == 0: | |
# 已结束 | |
pass | |
else: | |
# 跳转类型 | |
if ( | |
self.graph[str(children[0])]["jump_type"] | |
== InteractiveNodeJumpingType.DEFAULT.value | |
): | |
# 直接跳转 | |
for node_id in children: | |
btn = Button( | |
node_id, | |
[0, 0], | |
"", | |
self.graph[str(node_id)]["condition"], | |
self.graph[str(node_id)]["command"], | |
) | |
condition = InteractiveJumpingCondition( | |
self.variables, btn.condition | |
) | |
if condition.get_result(): | |
# 可以跳转 | |
native_command = InteractiveJumpingCommand( | |
self.variables, btn.command | |
) | |
self.variables = native_command.run_command() | |
btn_id = btn.node_id | |
self.set_source(self.graph[str(btn_id)]["cid"]) | |
self.current_node = btn.node_id | |
self.volume_change_event() | |
title = self.graph[str(node_id)]["title"] | |
self.node.setText(f"(当前节点: {title})") | |
break | |
else: | |
# 进行选择 | |
def get_info(node_id: int): | |
return self.graph[str(node_id)] | |
cnt = 0 | |
for idx, child in enumerate(children): | |
pos_x = cnt * 200 | |
pos_y = 600 | |
cur_info = get_info(child) | |
# 生成 Button 对象 | |
self.choice_buttons.append( | |
Button( | |
child, | |
[pos_x, pos_y], | |
cur_info["button"]["text"], | |
cur_info["condition"], | |
cur_info["command"], | |
) | |
) | |
# 生成 ButtonLabel 对象 | |
if cur_info["button"]["pos"][0] == None: | |
if idx != 0: | |
previous_info = get_info(children[idx - 1]) | |
curtext, previoustext = ( | |
cur_info["button"]["text"], | |
previous_info["button"]["text"], | |
) | |
if curtext[2:] == previoustext[2:]: | |
# 可确定与上一个按钮同一个位置(即概率按钮) | |
# 不再生成单独的 label | |
self.choice_buttons[-1].pos[0] -= 200 | |
continue | |
cnt += 1 | |
lbl = ButtonLabel(self.win) | |
lbl.prep_text( | |
cur_info["button"]["text"], pos_x, pos_y | |
) | |
lbl.show() | |
self.choice_labels.append(lbl) | |
continue | |
if idx != 0: | |
previous_info = get_info(children[idx - 1]) | |
curpos, previouspos = ( | |
cur_info["button"]["pos"], | |
previous_info["button"]["pos"], | |
) | |
if (abs(curpos[0] - previouspos[0]) <= 5) and ( | |
abs(curpos[1] - previouspos[1]) <= 5 | |
): | |
# 可确定与上一个按钮同一个位置(即概率按钮) | |
# 不再生成单独的 label | |
self.choice_buttons[-1].pos[0] -= 200 | |
else: | |
# 生成 label | |
cnt += 1 | |
lbl = ButtonLabel(self.win) | |
lbl.prep_text( | |
cur_info["button"]["text"], pos_x, pos_y | |
) | |
lbl.show() | |
self.choice_labels.append(lbl) | |
else: | |
# 生成 label | |
cnt += 1 | |
lbl = ButtonLabel(self.win) | |
lbl.prep_text( | |
cur_info["button"]["text"], pos_x, pos_y | |
) | |
lbl.show() | |
self.choice_labels.append(lbl) | |
pass | |
add_space = int((800 - cnt * 200) / 2) | |
for idx, lbl in enumerate(self.choice_labels): | |
lbl.setGeometry( | |
QtCore.QRect( | |
lbl.geometry().left() + add_space, | |
lbl.geometry().top(), | |
lbl.geometry().width(), | |
lbl.geometry().height(), | |
) | |
) | |
for btn in self.choice_buttons: | |
btn.pos[0] += add_space | |
# 处理进度条 | |
if self.is_draging_slider: | |
return | |
if self.mediaplayer.duration() == 0: | |
self.slider.setValue(100) | |
self.label.setText("--:--/--:--") | |
return | |
if ( | |
(self.mediaplayer.duration() // 1000) | |
== ((self.mediaplayer.position() // 1000)) | |
) and (not self.has_end): | |
self.has_end = True | |
self.mediaplayer.pause() | |
self.final_position = self.mediaplayer.position() | |
self.mediaplayer.setAudioOutput( | |
QtMultimedia.QAudioOutput().setVolume(0) | |
) | |
self.slider.setValue(100) | |
duration = self.mediaplayer.duration() // 1000 | |
duration_sec = duration % 60 | |
duration_min = duration // 60 | |
if duration_sec < 10: | |
duration_sec = "0" + str(duration_sec) | |
if duration_min < 10: | |
duration_min = "0" + str(duration_min) | |
self.label.setText( | |
f"{duration_min}:{duration_sec}/{duration_min}:{duration_sec}" | |
) | |
self.player.lower() | |
for lbl in self.choice_labels: | |
lbl.raise_() | |
return | |
elif self.has_end: | |
self.has_end = True | |
self.slider.setValue(100) | |
self.mediaplayer.setPosition(self.final_position) | |
self.mediaplayer.setAudioOutput( | |
QtMultimedia.QAudioOutput().setVolume(0) | |
) | |
duration = self.mediaplayer.duration() // 1000 | |
duration_sec = duration % 60 | |
duration_min = duration // 60 | |
if duration_sec < 10: | |
duration_sec = "0" + str(duration_sec) | |
if duration_min < 10: | |
duration_min = "0" + str(duration_min) | |
self.label.setText( | |
f"{duration_min}:{duration_sec}/{duration_min}:{duration_sec}" | |
) | |
self.player.lower() | |
for lbl in self.choice_labels: | |
lbl.raise_() | |
return | |
else: | |
self.has_end = False | |
self.choice_buttons = [] | |
for lbl in self.choice_labels: | |
lbl.hide() | |
self.choice_labels = [] | |
self.last_position = self.mediaplayer.position() | |
self.slider.setValue( | |
int(self.mediaplayer.position() / self.mediaplayer.duration() * 100) | |
) | |
duration = self.mediaplayer.duration() // 1000 | |
position = self.mediaplayer.position() // 1000 | |
duration_sec = duration % 60 | |
duration_min = duration // 60 | |
position_sec = position % 60 | |
position_min = position // 60 | |
if duration_sec < 10: | |
duration_sec = "0" + str(duration_sec) | |
if duration_min < 10: | |
duration_min = "0" + str(duration_min) | |
if position_sec < 10: | |
position_sec = "0" + str(position_sec) | |
if position_min < 10: | |
position_min = "0" + str(position_min) | |
self.label.setText( | |
f"{position_min}:{position_sec}/{duration_min}:{duration_sec}" | |
) | |
# 将选择的按钮置于最上层 | |
for lbl in self.choice_labels: | |
lbl.raise_() | |
self.win.timerEvent = timerEvent | |
# Click & Jump | |
def mouseReleaseEvent(event: QtGui.QMouseEvent): | |
pos = event.position() | |
pos = [pos.x(), pos.y()] | |
for var in self.variables: | |
if var.is_random(): | |
var._InteractiveVariable__var_value = random.random() * 100 | |
for btn in self.choice_buttons: | |
if ( | |
(pos[0] - btn.pos[0] <= 200) | |
and (pos[0] - btn.pos[0] >= 0) | |
and (pos[1] - btn.pos[1] <= 50) | |
and (pos[1] - btn.pos[1] >= 0) | |
): | |
condition = InteractiveJumpingCondition( | |
self.variables, btn.condition | |
) | |
if condition.get_result(): | |
# 可以跳转 | |
native_command = InteractiveJumpingCommand( | |
self.variables, btn.command | |
) | |
self.variables = native_command.run_command() | |
btn_id = btn.node_id | |
self.set_source(self.graph[str(btn_id)]["cid"]) | |
self.current_node = btn.node_id | |
self.volume_change_event() | |
title = self.graph[str(btn.node_id)]["title"] | |
self.node.setText(f"(当前节点: {title})") | |
break | |
self.win.mouseReleaseEvent = mouseReleaseEvent | |
def start_playing(self): | |
self.mediaplayer.play() | |
self.is_stoping = False | |
def stop_playing(self): | |
self.mediaplayer.stop() | |
self.is_stoping = True | |
def pause_playing(self): | |
self.mediaplayer.pause() | |
self.is_stoping = True | |
def retranslateUi(self, Form): | |
_translate = QtCore.QCoreApplication.translate | |
self.pp.setText(_translate("Form", "Pause")) | |
self.pushButton.setText(_translate("Form", "<- Previous")) | |
self.node.setText(_translate("Form", "(当前节点: 无)")) | |
self.info.setText(_translate("Form", "视频标题(BVID)")) | |
self.pushButton_2.setText(_translate("Form", "Open")) | |
self.pushButton_3.setText(_translate("Form", "Close")) | |
self.pushButton_4.setText(_translate("Form", "Sound: Off")) | |
self.label.setText(_translate("Form", "--:--/--:--")) | |
self.label_2.setText(_translate("Form", "0")) | |
def set_source(self, cid: int): | |
wintitle = "MPlayer" | |
for var in self.variables: | |
if var.is_show(): | |
wintitle += f" - {var.get_name()}: {int(var.get_value())}" | |
for lbl in self.choice_labels: | |
lbl.hide() | |
self.choice_labels = [] | |
self.choice_buttons = [] | |
self.win.setWindowTitle(wintitle) | |
self.state_log.append({"cid": cid, "vars": copy.deepcopy(self.variables)}) | |
self.has_end = False | |
self.mediaplayer.setAudioOutput( | |
QtMultimedia.QAudioOutput().setVolume(self.horizontalSlider.value() / 100) | |
) | |
self.stop_playing() | |
self.pp.setText("Pause") | |
dest = self.temp_dir + str(cid) + ".mp4" | |
self.mediaplayer.setSource(QtCore.QUrl(dest)) | |
self.mediaplayer.setPosition(0) | |
if self.mediaplayer.duration() <= 7: | |
self.mediaplayer.setPosition(self.mediaplayer.duration()) | |
self.start_playing() | |
def extract_ivi(self, path: str): | |
curtime = str(time.time()) | |
try: | |
os.mkdir(".mplayer") | |
except: | |
pass | |
os.mkdir(".mplayer/" + curtime) | |
self.temp_dir = ".mplayer/" + curtime + "/" | |
ivi = zipfile.ZipFile(path) | |
ivi.extractall(self.temp_dir) | |
bilivideo_parser = json.JSONDecoder() | |
self.node.setText("(当前节点: 视频主节点)") | |
self.info.setText( | |
bilivideo_parser.decode( | |
open(self.temp_dir + "bilivideo.json", "r", encoding="utf-8").read() | |
)["title"] | |
+ "(" | |
+ bilivideo_parser.decode( | |
open(self.temp_dir + "bilivideo.json", "r", encoding="utf-8").read() | |
)["bvid"] | |
+ ")" | |
) | |
self.graph = json.load( | |
open(self.temp_dir + "ivideo.json", "r", encoding="utf-8") | |
) | |
self.current_node = 1 | |
variables = self.graph["1"]["vars"] | |
for var in variables: | |
self.variables.append( | |
InteractiveVariable( | |
var["name"], var["id"], var["value"], var["show"], var["random"] | |
) | |
) | |
self.set_source(self.graph["1"]["cid"]) | |
self.volume_change_event() | |
def close_ivi(self): | |
self.player.hide() | |
self.player = QtMultimediaWidgets.QVideoWidget(self.win) | |
self.player.setGeometry(QtCore.QRect(0, 0, 800, 450)) | |
self.player.setObjectName("player") | |
self.player.show() | |
self.current_node = 0 | |
self.variables = [] | |
self.state_log = [] | |
self.choice_buttons = [] | |
for lbl in self.choice_labels: | |
lbl.hide() | |
self.choice_labels = [] | |
self.graph = None | |
self.stop_playing() | |
self.pp.setText("Pause") | |
self.has_end = False | |
self.mediaplayer = QtMultimedia.QMediaPlayer() # Clear the multimedia source | |
self.mediaplayer.setVideoOutput(self.player) | |
self.mediaplayer.setAudioOutput(QtMultimedia.QAudioOutput()) | |
self.volume_change_event() | |
shutil.rmtree(self.temp_dir) | |
while True: | |
if not os.path.exists(self.temp_dir): | |
break | |
try: | |
os.rmdir(".mplayer") | |
except: | |
# Not empty | |
pass | |
self.temp_dir = "" | |
self.node.setText("(当前节点: 无)") | |
self.info.setText("视频标题(BVID)") | |
self.win.setWindowTitle("MPlayer") | |
self.lineEdit.setText("") | |
self.pp.setEnabled(False) | |
self.pushButton.setEnabled(False) | |
def open_ivi(self): | |
if self.current_node != 0: | |
return | |
try: | |
if self.lineEdit.text() != "": | |
self.extract_ivi(self.lineEdit.text()) | |
else: | |
dialog = QtWidgets.QFileDialog() | |
filename, _ = dialog.getOpenFileName( | |
self.win, | |
"Choose an 'ivi' file to open. ", | |
filter="Bilibili Interactive Video (*.ivi)", | |
) | |
self.extract_ivi(filename) | |
self.lineEdit.setText(filename) | |
self.pp.setEnabled(True) | |
self.pushButton.setEnabled(True) | |
except Exception as e: | |
warning = QtWidgets.QMessageBox() | |
warning.warning(self.win, "Oops...", str(e)) | |
def volume_change_event(self): | |
if self.horizontalSlider.value() == 0: | |
self.pushButton_4.setText("Sound: Off") | |
else: | |
self.pushButton_4.setText("Sound: On") | |
position = self.mediaplayer.position() | |
if (not self.has_end) or (not self.is_stoping): | |
pass | |
else: | |
self.pause_playing() | |
volume = self.horizontalSlider.value() | |
self.label_2.setText(str(volume)) | |
self.audio_output.setVolume(float(volume / 100)) | |
self.mediaplayer.setAudioOutput(self.audio_output) | |
if (not self.has_end) or (not self.is_stoping): | |
pass | |
else: | |
self.mediaplayer.setPosition(position) | |
self.start_playing() | |
def position_start_change_event(self): | |
self.mediaplayer.pause() | |
self.is_draging_slider = True | |
def position_change_event(self): | |
volume = self.slider.value() | |
if volume != 100 and self.has_end: | |
self.slider.setValue(100) | |
return | |
self.mediaplayer.setPosition(int(self.mediaplayer.duration() * volume / 100)) | |
if not self.is_stoping: | |
self.start_playing() | |
self.is_draging_slider = False | |
def sound_on_off_event(self): | |
if "on" in self.pushButton_4.text().lower(): | |
self.pushButton_4.setText("Sound: Off") | |
curpos = self.mediaplayer.position() | |
self.mediaplayer.pause() | |
volume = self.horizontalSlider.value() | |
self.label_2.setText(str(volume)) | |
self.audio_output = QtMultimedia.QAudioOutput() | |
self.audio_output.setVolume(0.0) | |
self.mediaplayer.setAudioOutput(self.audio_output) | |
self.mediaplayer.setPosition(curpos) | |
if not self.is_stoping: | |
self.start_playing() | |
self.horizontalSlider.setSliderPosition(0) | |
else: | |
self.pushButton_4.setText("Sound: On") | |
curpos = self.mediaplayer.position() | |
self.mediaplayer.pause() | |
volume = self.horizontalSlider.value() | |
self.label_2.setText(str(volume)) | |
self.audio_output = QtMultimedia.QAudioOutput() | |
self.audio_output.setVolume(1.0) | |
self.mediaplayer.setAudioOutput(self.audio_output) | |
self.mediaplayer.setPosition(curpos) | |
if not self.is_stoping: | |
self.start_playing() | |
self.horizontalSlider.setSliderPosition(100) | |
def pp_button(self): | |
if self.is_stoping: | |
self.start_playing() | |
self.pp.setText("Pause") | |
else: | |
self.pause_playing() | |
self.pp.setText("Play") | |
def on_close_check(self, event): | |
if self.current_node != 0: | |
reply = QtWidgets.QMessageBox.question( | |
self.win, | |
"WARNING", | |
"IVI file is playing. Are you sure want to exit? ", | |
QtWidgets.QMessageBox.StandardButton.Yes | |
| QtWidgets.QMessageBox.StandardButton.No, | |
QtWidgets.QMessageBox.StandardButton.No, | |
) | |
if reply == QtWidgets.QMessageBox.StandardButton.Yes: | |
self.close_ivi() | |
event.accept() | |
else: | |
event.ignore() | |
else: | |
event.accept() | |
def back_to_previous(self): | |
if len(self.state_log) < 2: | |
QtWidgets.QMessageBox.warning( | |
self.win, | |
"WTF???", | |
"MPlayer can't find the previous node. \nMaybe there's not any node or only one node?", | |
) | |
return | |
new_cid = copy.deepcopy(self.state_log[-2]["cid"]) | |
new_vars = copy.deepcopy(self.state_log[-2]["vars"]) | |
self.state_log.pop() | |
for key in self.graph.keys(): | |
if self.graph[key]["cid"] == new_cid: | |
new_node_id = int(key) | |
self.current_node = new_node_id | |
self.variables = new_vars | |
self.set_source(new_cid) | |
self.state_log.pop() | |
title = self.graph[str(new_node_id)]["title"] | |
self.node.setText(f"(当前节点: {title})") | |
self.volume_change_event() | |
return | |
def main(): | |
app = QtWidgets.QApplication(sys.argv) | |
win = QtWidgets.QMainWindow() | |
ui = MPlayer() | |
ui.setup(win) | |
win.show() | |
sys.exit(app.exec()) | |
def prepopen(path: str): | |
app = QtWidgets.QApplication(sys.argv) | |
win = QtWidgets.QMainWindow() | |
ui = MPlayer() | |
ui.setup(win) | |
ui.lineEdit.setText(path) | |
ui.open_ivi() | |
win.show() | |
sys.exit(app.exec()) | |
if __name__ == "__main__": | |
main() | |