-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 2a66720
Showing
16 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
name: Build | ||
|
||
on: [push, repository_dispatch, workflow_dispatch] | ||
|
||
jobs: | ||
build: | ||
runs-on: windows-latest | ||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
- name: Setup Visual Studio | ||
uses: ilammy/msvc-dev-cmd@v1 | ||
- name: Setup Python | ||
uses: actions/setup-python@v3 | ||
with: | ||
python-version: "3.11" | ||
architecture: "x64" | ||
- name: Install Python requirements | ||
shell: bash | ||
run: > | ||
python -OO -m pip install --disable-pip-version-check --upgrade nuitka zstandard && | ||
python -OO -m pip install --disable-pip-version-check -r requirements.txt | ||
- name: Build executable | ||
shell: powershell | ||
run: > | ||
python -OO -m nuitka --standalone --onefile --python-flag=-OO --assume-yes-for-downloads --static-libpython=auto --windows-disable-console --windows-icon-from-ico=resources/subassistant.ico --windows-product-name=SubAssistant --windows-company-name=naghim --windows-file-version=1.0.0.0 --windows-file-description=SubAssistant --enable-plugin=pyside6 -o SubAssistant.exe subassistant/__main__.py | ||
- uses: actions/upload-artifact@v3 | ||
with: | ||
name: SubAssistant | ||
path: SubAssistant.exe |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.py[c|o] | ||
__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pyside6 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#FileChooserButton { | ||
background-color: #555555; | ||
border: none; | ||
color: white; | ||
padding: 10px 20px; | ||
text-align: center; | ||
text-decoration: none; | ||
font-size: 16px; | ||
margin: 4px 2px; | ||
border-radius: 8px; | ||
} | ||
|
||
#Action_btn { | ||
background-color: #d9073f; | ||
border: none; | ||
font-weight: bold; | ||
color: #f5f5ff; | ||
padding: 10px 20px; | ||
text-align: center; | ||
text-decoration: none; | ||
font-size: 16px; | ||
margin: 4px 2px; | ||
border-radius: 8px; | ||
} | ||
|
||
#Label_txt { | ||
color: #333; | ||
font-size: 16px; | ||
} | ||
|
||
#Label_txt_bold { | ||
color: #333; | ||
font-size: 18px; | ||
font-weight: bold; | ||
} | ||
|
||
#Label_txt_bold_mini { | ||
color: #333; | ||
font-size: 16px; | ||
font-weight: bold; | ||
} | ||
|
||
#LineEdit { | ||
border: 2px solid #e9e9e9; | ||
border-radius: 8px; | ||
padding: 5px; | ||
font-size: 16px; | ||
background-color: #ffffff; | ||
} | ||
|
||
#About_txt { | ||
color: #333; | ||
font-size: 16px; | ||
text-align: justify; | ||
} |
Binary file not shown.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import sys | ||
from PySide6.QtWidgets import QApplication | ||
from subassistant.gui.window import SubAssistantWindow | ||
|
||
if __name__ == "__main__": | ||
app = QApplication(sys.argv) | ||
|
||
with open("./resources/styles.css", "r") as f: | ||
app.setStyleSheet(f.read()) | ||
|
||
window = SubAssistantWindow() | ||
window.show() | ||
sys.exit(app.exec()) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QFileDialog, QHBoxLayout, QLineEdit, QScrollArea, QMessageBox | ||
from subassistant.logic.logic import RemoveComments, CommentDialogue | ||
import os | ||
|
||
class BaseSubtitleUI(QWidget): | ||
def __init__(self): | ||
super().__init__() | ||
|
||
self.layout = QVBoxLayout() | ||
self.window_title = QLabel(self.TAB_TITLE) | ||
self.window_title.setObjectName("Label_txt_bold") | ||
self.layout.addWidget(self.window_title) | ||
|
||
self.input_file_label = QLabel("Select Input File:") | ||
self.input_file_label.setObjectName("Label_txt") | ||
self.layout.addWidget(self.input_file_label) | ||
|
||
self.input_file_line_edit = QLineEdit() | ||
self.input_file_line_edit.setReadOnly(True) | ||
self.input_file_line_edit.setObjectName("LineEdit") | ||
|
||
self.input_btn = QPushButton("Browse", clicked=self.get_file) | ||
self.input_btn.setObjectName("FileChooserButton") | ||
|
||
input_file_layout = QHBoxLayout() | ||
input_file_layout.addWidget(self.input_file_line_edit) | ||
input_file_layout.addWidget(self.input_btn) | ||
|
||
self.layout.addLayout(input_file_layout) | ||
|
||
self.output_label = QLabel("Output Folder:") | ||
self.output_label.setObjectName("Label_txt") | ||
self.layout.addWidget(self.output_label) | ||
|
||
self.output_file_line_edit = QLineEdit() | ||
self.output_file_line_edit.setObjectName("LineEdit") | ||
output_file_layout = QHBoxLayout() | ||
output_file_layout.addWidget(self.output_file_line_edit) | ||
|
||
self.output_btn = QPushButton("Browse", clicked=self.get_output_folder) | ||
self.output_btn.setObjectName("FileChooserButton") | ||
output_file_layout.addWidget(self.output_btn) | ||
|
||
self.layout.addLayout(output_file_layout) | ||
|
||
self.action_button = QPushButton(self.ACTION) | ||
self.action_button.setObjectName("Action_btn") | ||
self.action_button.clicked.connect(lambda: self.process_file(self.ACTION_CLASS)) | ||
|
||
self.layout.addWidget(self.action_button) | ||
self.setLayout(self.layout) | ||
|
||
def get_file(self): | ||
filename, _ = QFileDialog.getOpenFileName(self, "Select File", filter="Subtitles (*.ass)") | ||
if filename: | ||
self.input_file_line_edit.setText(filename) | ||
self.output_file_line_edit.setText(self.get_output_file(self.output_file_line_edit.text())) | ||
|
||
def get_output_folder(self): | ||
output_folder = QFileDialog.getExistingDirectory(self, "Select Folder") | ||
if output_folder: | ||
self.output_file_line_edit.setText(self.get_output_file(output_folder)) | ||
|
||
def get_output_file(self, folder): | ||
input_file = self.input_file_line_edit.text() | ||
|
||
if input_file: | ||
input_filename, input_ext = os.path.splitext(os.path.basename(input_file)) | ||
|
||
if not folder: | ||
folder = os.path.dirname(input_file) | ||
if folder.endswith(".ass"): | ||
folder = os.path.dirname(folder) | ||
output_filename = f'{input_filename}_modified{input_ext}' | ||
output_file = os.path.join(folder, output_filename) | ||
else: | ||
output_file = folder | ||
|
||
output_file = output_file.replace(os.sep, '/') | ||
return output_file | ||
|
||
def process_file(self, action_class): | ||
input_file = self.input_file_line_edit.text() | ||
output_folder = self.output_file_line_edit.text() | ||
|
||
if not input_file: | ||
QMessageBox.warning(self, "Warning", "Please select a file to process.") | ||
return | ||
|
||
if not output_folder: | ||
QMessageBox.warning(self, "Warning", "Please select an output folder.") | ||
return | ||
|
||
output_file = self.get_output_file(output_folder) | ||
|
||
try: | ||
action_class(input_file, output_file).process_file() | ||
QMessageBox.information(self, "Success", f'File processed successfully.\nOutput file: {output_file}') | ||
except Exception as e: | ||
QMessageBox.critical(self, "Error", f'An error occurred while processing the file.\nError: {e}') | ||
|
||
class CommentTab(BaseSubtitleUI): | ||
ACTION = "Comment Dialogue" | ||
TAB_TITLE = "Comment Subtitle Text" | ||
ACTION_CLASS = CommentDialogue | ||
|
||
|
||
class RemoveCommentTab(BaseSubtitleUI): | ||
ACTION = "Delete Comments" | ||
TAB_TITLE = "Delete Commented Dialogue" | ||
ACTION_CLASS = RemoveComments | ||
|
||
|
||
class AboutTab(QWidget): | ||
def __init__(self): | ||
super().__init__() | ||
self.initUI() | ||
|
||
def initUI(self): | ||
self.layout = QVBoxLayout() | ||
|
||
self.content_widget = QWidget() | ||
self.content_layout = QVBoxLayout() | ||
|
||
scroll_area = QScrollArea() | ||
scroll_area.setWidgetResizable(True) | ||
scroll_area.setWidget(self.content_widget) | ||
|
||
self.layout.addWidget(scroll_area) | ||
|
||
|
||
self.text_logo = QLabel("<html><head/><body><p align='center'>Sub<span style=\"color: #d9073f\">Assistant</span></p></body></html>" | ||
) | ||
|
||
self.text = QLabel("<html><head/><body><p align='justify'><b> 👀 What is this?</b> This is a specialized desktop application designed to simplify the translation process for subtitle files, specifically .ass (Advanced SubStation Alpha) format. Tailored for translators, CommentOut facilitates seamless collaboration by allowing users to comment out the original English dialogue text, write their translations alongside it, and enable proofreaders or quality checkers to review both versions within the same file. By providing a unified platform for bilingual text management, this application enhances the efficiency and accuracy of subtitle translation workflows.</p></body></html>" | ||
) | ||
|
||
self.text_how_to = QLabel("<html><head/><body><p align='justify'><b> 👀 How to use?</b> Choose the action you wish to take from the side menu—whether to comment out text or delete comments. Use the \"Select File\" button to open the .ass file. The program will automatically propose an output filename within the same folder. If you prefer a different folder or wish to rename the output, utilize the \"Browse\" button or directly edit the output path. Upon clicking the button, the selected operation will be executed.</p></body></html>" | ||
) | ||
|
||
self.text_slogan = QLabel("<html><head/><body><p align='center'>Made with 50% 💕 and 50% ☕ <br/> by <a style=\"color: #d9073f\" href=\"https://github.com/naghim\">naghim @ GitHub</a></p></body></html>" | ||
) | ||
|
||
self.text_slogan.setOpenExternalLinks(True) | ||
self.text.setWordWrap(True) | ||
self.text_how_to.setWordWrap(True) | ||
self.text.setObjectName("About_txt") | ||
self.text_how_to.setObjectName("About_txt") | ||
self.text_slogan.setObjectName("Label_txt_bold_mini") | ||
self.text_logo.setObjectName("Label_txt_bold") | ||
self.content_layout.addWidget(self.text_logo) | ||
self.content_layout.addWidget(self.text_how_to) | ||
self.content_layout.addWidget(self.text) | ||
self.content_layout.addWidget(self.text_slogan) | ||
self.content_widget.setLayout(self.content_layout) | ||
|
||
self.setLayout(self.layout) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import sys | ||
from PySide6.QtGui import QIcon | ||
from PySide6.QtWidgets import QTabWidget, QWidget, QVBoxLayout | ||
from subassistant.gui.tab import CommentTab, RemoveCommentTab, AboutTab | ||
|
||
class SubAssistantWindow(QWidget): | ||
def __init__(self): | ||
super().__init__() | ||
self.setWindowTitle("SubAssistant") | ||
self.setWindowIcon(QIcon("./resources/curly_braces_icon.png")) | ||
self.setFixedSize(550, 350) | ||
self.initUI() | ||
|
||
def initUI(self): | ||
layout = QVBoxLayout() | ||
|
||
self.tab_widget = QTabWidget() | ||
self.tab_widget.setTabPosition(QTabWidget.West) | ||
|
||
self.tab_widget.addTab(CommentTab(), QIcon("./resources/curly_braces.png"), "") | ||
self.tab_widget.addTab(RemoveCommentTab(), QIcon("./resources/no_curly_braces.png"), "") | ||
self.tab_widget.addTab(AboutTab(), QIcon("./resources/about.png"), "") | ||
|
||
|
||
self.tab_widget.setStyleSheet("QTabBar::tab { height: 60px; width: 100px;}") | ||
layout.addWidget(self.tab_widget) | ||
|
||
self.setLayout(layout) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import sys, os | ||
|
||
class ProcessSubtitles(object): | ||
def __init__(self, input_file: str, output_file: str) -> None: | ||
self.input_file = input_file | ||
self.output_file = output_file | ||
|
||
def process_file(self) -> None: | ||
with open(self.input_file, 'r', encoding='utf-8') as infile, open(self.output_file, 'w', encoding='utf-8') as outfile: | ||
for line in infile: | ||
modified_line = self.process_line(line) | ||
outfile.write(modified_line) | ||
|
||
def process_line(self, line: str) -> str: | ||
raise NotImplementedError | ||
|
||
|
||
|
||
class RemoveComments(ProcessSubtitles): | ||
def process_line(self, line: str) -> str: | ||
modified_line = '' | ||
inside_braces = False | ||
|
||
for i, char in enumerate(line): | ||
if char == '{' and line[i + 1] != '\\': | ||
inside_braces = True | ||
elif char == '}' and inside_braces: | ||
inside_braces = False | ||
elif not inside_braces: | ||
modified_line += char | ||
|
||
return modified_line | ||
|
||
def process_file(self) -> None: | ||
super().process_file() | ||
print("Text within {} removed and written to", self.output_file) | ||
|
||
class CommentDialogue(ProcessSubtitles): | ||
def process_line(self, line: str) -> str: | ||
if not line.startswith("Dialogue"): | ||
return line | ||
|
||
modified_line = '' | ||
inside_braces = False | ||
comma_count = 0 | ||
str_buffer = '' | ||
|
||
for i, char in enumerate(line.strip()): | ||
if char == ',': | ||
comma_count += 1 | ||
if comma_count == 9: | ||
modified_line += char | ||
continue | ||
|
||
# Check if the text part has started | ||
if comma_count >= 9: | ||
# Don't comment out the text if it's a formatting tag | ||
if char == '{': | ||
# But if the opening brace was preceeded by dialog, we want to comment that part out | ||
inside_braces = True | ||
if str_buffer != '': | ||
modified_line += f"{{{str_buffer}}}" | ||
modified_line += char | ||
elif char == '}': | ||
inside_braces = False | ||
modified_line += char | ||
|
||
# Buffer the text outside of the braces to comment out later | ||
elif not inside_braces: | ||
str_buffer += char | ||
|
||
# We don't want to modify the line until the actual text part | ||
else: | ||
modified_line += char | ||
|
||
else: | ||
modified_line += char | ||
|
||
if str_buffer != '': | ||
modified_line += f"{{{str_buffer}}}" | ||
|
||
return f"{modified_line}\n" | ||
|
||
def process_file(self) -> None: | ||
super().process_file() | ||
print("Subtitle successfully commented and saved to ", self.output_file) | ||
|