@משה-ש. זה קוד פייתון. צריך להתקין פייתון בשביל להריץ את זה.
בכל אופן, קימפלתי לך את זה לתוכנה רגילה, לא דורש פייתון ולא שום דבר.
המרה מאוצריא לאורייתא.exe
@משה-ש. זה קוד פייתון. צריך להתקין פייתון בשביל להריץ את זה.
בכל אופן, קימפלתי לך את זה לתוכנה רגילה, לא דורש פייתון ולא שום דבר.
המרה מאוצריא לאורייתא.exe
@משה-ש. הנה הסקריפט של האדם החושב עם ממשק גרפי חמוד. בספוילר
import zipfile
import os
import sys
import logging
from datetime import datetime
from threading import Thread
from bs4 import BeautifulSoup
from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QPushButton, QLabel, QLineEdit, QTextEdit, QProgressBar,
QFileDialog, QMessageBox, QFrame
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from PyQt5.QtGui import QFont
class ConversionWorker(QThread):
"""Thread נפרד לביצוע ההמרה כדי שלא לקפיא את הממשק"""
progress_updated = pyqtSignal(int, int) # current, total
status_updated = pyqtSignal(str)
finished = pyqtSignal(bool, str) # success, message
def __init__(self, otzaria_path, output_path):
super().__init__()
self.otzaria_path = otzaria_path
self.output_path = output_path
self.should_stop = False
def stop(self):
self.should_stop = True
def convert_text(self, text: str) -> str:
"""ממיר טקסט HTML לפורמט אורייתא"""
soup = BeautifulSoup(text, "html.parser")
conversion_dict = {
"h1": "$",
"h2": "#",
"h3": "@",
"h4": "~",
"h5": "!",
"h6": "!",
}
for tag, replacement in conversion_dict.items():
for element in soup.find_all(tag):
element.replace_with(f"{replacement} {element.text}")
return str(soup)
def to_zip(self, comments: str, file_path: str, file_content: str) -> None:
"""יוצר קובץ ZIP עם התוכן הממופר"""
with zipfile.ZipFile(file_path, "w") as zip_file:
zip_file.comment = bytes(comments, "utf-8")
zip_file.writestr("BookText", file_content)
def count_txt_files(self):
"""סופר כמה קבצי TXT יש לעיבוד"""
total = 0
if os.path.isfile(self.otzaria_path):
return 1 if self.otzaria_path.lower().endswith('.txt') else 0
for root, dirs, files in os.walk(self.otzaria_path):
for file in files:
if file.lower().endswith(".txt"):
total += 1
return total
def run(self):
try:
self.status_updated.emit("מתחיל תהליך המרה...")
# בדיקת תקינות נתיבים
if not os.path.exists(self.otzaria_path):
self.finished.emit(False, "נתיב קבצי אוצריא לא קיים")
return
if not os.path.exists(self.output_path):
try:
os.makedirs(self.output_path, exist_ok=True)
except Exception as e:
self.finished.emit(False, f"לא ניתן ליצור תיקיית פלט: {str(e)}")
return
# ספירת קבצים לעיבוד
total_files = self.count_txt_files()
if total_files == 0:
self.finished.emit(False, "לא נמצאו קבצי TXT לעיבוד")
return
current_file = 0
unique_id = 3000
# אם זה קובץ יחיד
if os.path.isfile(self.otzaria_path):
if self.should_stop:
return
self.status_updated.emit(f"מעבד קובץ: {os.path.basename(self.otzaria_path)}")
unique_id += 1
file_name = os.path.splitext(os.path.basename(self.otzaria_path))[0]
with open(self.otzaria_path, "r", encoding="utf-8") as f:
text = f.read()
converted_text = self.convert_text(text)
comment = f"UniqueId={unique_id}\nDisplayName={file_name}"
output_file = os.path.join(self.output_path, f"{file_name}.obk")
self.to_zip(comment, output_file, converted_text)
current_file += 1
self.progress_updated.emit(current_file, total_files)
else:
# עיבוד תיקייה
for root, dirs, files in os.walk(self.otzaria_path):
if self.should_stop:
break
rel_path = os.path.relpath(root, self.otzaria_path)
# יצירת תיקיות - רק אם יש בהן תוכן
for dir_name in dirs:
dir_full_path = os.path.join(root, dir_name)
if not os.listdir(dir_full_path): # בדיקה אם התיקייה ריקה
continue
output_dir = os.path.join(self.output_path, rel_path, dir_name)
os.makedirs(output_dir, exist_ok=True)
folder_file = os.path.join(self.output_path, rel_path, f"{dir_name}.folder")
with open(folder_file, "w", encoding="utf-8") as f:
f.write(f"BranchName={dir_name}")
# עיבוד קבצים
for file in files:
if self.should_stop:
break
if not file.lower().endswith(".txt"): # בדיקה מעודכנת
continue
current_file += 1
self.status_updated.emit(f"מעבד קובץ {current_file}/{total_files}: {file}")
unique_id += 1
file_name = os.path.splitext(file)[0]
try:
with open(os.path.join(root, file), "r", encoding="utf-8") as f:
text = f.read()
converted_text = self.convert_text(text)
comment = f"UniqueId={unique_id}\nDisplayName={file_name}"
output_file = os.path.join(self.output_path, rel_path, f"{file_name}.obk")
# וודא שהתיקייה קיימת
os.makedirs(os.path.dirname(output_file), exist_ok=True)
self.to_zip(comment, output_file, converted_text)
self.progress_updated.emit(current_file, total_files)
except Exception as e:
logging.error(f"שגיאה בעיבוד קובץ {file}: {str(e)}")
self.status_updated.emit(f"שגיאה בקובץ {file}: {str(e)}")
if not self.should_stop:
self.status_updated.emit("ההמרה הושלמה בהצלחה!")
self.finished.emit(True, f"הומרו {current_file} קבצים בהצלחה")
else:
self.status_updated.emit("ההמרה הופסקה על ידי המשתמש")
self.finished.emit(False, "התהליך הופסק")
except Exception as e:
logging.error(f"שגיאה כללית: {str(e)}")
self.finished.emit(False, f"שגיאה: {str(e)}")
class OtzariaConverterGUI(QMainWindow):
def __init__(self):
super().__init__()
self.worker = None
self.setup_logging()
self.init_ui()
def setup_logging(self):
"""הגדרת מערכת הרישום"""
log_filename = f"otzaria_converter_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_filename, encoding='utf-8'),
logging.StreamHandler()
]
)
logging.info("יישום ממיר אוצריא לאורייתא התחיל")
def init_ui(self):
"""אתחול ממשק המשתמש"""
self.setWindowTitle("ממיר אוצריא לאורייתא")
self.setMinimumSize(600, 500)
self.setLayoutDirection(Qt.RightToLeft) # תמיכה ב-RTL
# וידג'ט מרכזי
central_widget = QWidget()
self.setCentralWidget(central_widget)
# פריסה ראשית
main_layout = QVBoxLayout(central_widget)
main_layout.setSpacing(15)
main_layout.setContentsMargins(20, 20, 20, 20)
# כותרת
title_label = QLabel("ממיר אוצריא לאורייתא")
title_font = QFont()
title_font.setPointSize(16)
title_font.setBold(True)
title_label.setFont(title_font)
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("""
QLabel{
font-size: 30px;
font-weight: bold;
}
""")
main_layout.addWidget(title_label)
# קו מפריד
line = QFrame()
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
main_layout.addWidget(line)
# בחירת נתיב אוצריא
otzaria_layout = QHBoxLayout()
self.otzaria_label = QLabel("נתיב קבצי אוצריא:")
self.otzaria_label.setMinimumWidth(100)
self.otzaria_path_edit = QLineEdit()
self.otzaria_path_edit.setPlaceholderText("בחר תיקייה או קובץ...")
self.otzaria_browse_btn = QPushButton("עיון...")
self.otzaria_browse_btn.clicked.connect(self.browse_otzaria_path)
otzaria_layout.addWidget(self.otzaria_label)
otzaria_layout.addWidget(self.otzaria_path_edit)
otzaria_layout.addWidget(self.otzaria_browse_btn)
main_layout.addLayout(otzaria_layout)
# בחירת נתיב פלט
output_layout = QHBoxLayout()
self.output_label = QLabel("תיקיית פלט:")
self.output_label.setMinimumWidth(100)
self.output_path_edit = QLineEdit()
self.output_path_edit.setPlaceholderText("בחר תיקיית פלט...")
self.output_browse_btn = QPushButton("עיון...")
self.output_browse_btn.clicked.connect(self.browse_output_path)
output_layout.addWidget(self.output_label)
output_layout.addWidget(self.output_path_edit)
output_layout.addWidget(self.output_browse_btn)
main_layout.addLayout(output_layout)
# כפתורי פעולה
buttons_layout = QHBoxLayout()
self.convert_btn = QPushButton("בצע המרה לאורייתא")
self.convert_btn.setStyleSheet("""
QPushButton {
background-color: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #2980b9;
}
QPushButton:disabled {
background-color: #bdc3c7;
}
""")
self.convert_btn.clicked.connect(self.start_conversion)
self.stop_btn = QPushButton("עצור")
self.stop_btn.setStyleSheet("""
QPushButton {
background-color: #e74c3c;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #c0392b;
}
QPushButton:disabled {
background-color: #bdc3c7;
}
""")
self.stop_btn.clicked.connect(self.stop_conversion)
self.stop_btn.setEnabled(False)
buttons_layout.addWidget(self.convert_btn)
buttons_layout.addWidget(self.stop_btn)
buttons_layout.addStretch()
main_layout.addLayout(buttons_layout)
# פס התקדמות
self.progress_bar = QProgressBar()
self.progress_bar.setVisible(False)
main_layout.addWidget(self.progress_bar)
# תיבת סטטוס
self.status_label = QLabel("מוכן להמרה")
self.status_label.setStyleSheet("color: #27ae60; font-weight: bold;")
main_layout.addWidget(self.status_label)
# לוג
log_label = QLabel("יומן פעילות:")
main_layout.addWidget(log_label)
self.log_text = QTextEdit()
self.log_text.setMaximumHeight(150)
self.log_text.setReadOnly(True)
self.log_text.setStyleSheet("""
QTextEdit {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
font-family: 'sans-serif', monospace;
font-size: 14px;
}
""")
main_layout.addWidget(self.log_text)
def browse_otzaria_path(self):
"""בחירת נתיב אוצריא (קובץ או תיקייה)"""
dialog = QFileDialog()
dialog.setLayoutDirection(Qt.RightToLeft)
# תחילה נבחר אם רוצים קובץ או תיקייה
msg_box = QMessageBox()
msg_box.setWindowTitle('בחירת סוג')
msg_box.setText('מה תרצה לבחור?')
msg_box.setLayoutDirection(Qt.RightToLeft)
folder_btn = msg_box.addButton('תיקייה', QMessageBox.YesRole)
file_btn = msg_box.addButton('קובץ', QMessageBox.NoRole)
cancel_btn = msg_box.addButton('ביטול', QMessageBox.RejectRole)
msg_box.setDefaultButton(folder_btn)
reply = msg_box.exec_()
if msg_box.clickedButton() == cancel_btn:
return
elif msg_box.clickedButton() == file_btn:
# בחירת קובץ
file_path, _ = QFileDialog.getOpenFileName(
self,
"בחר קובץ TXT",
"",
"קבצי טקסט (*.txt);;כל הקבצים (*)"
)
if file_path:
self.otzaria_path_edit.setText(file_path)
self.log_message(f"נבחר קובץ: {file_path}")
elif msg_box.clickedButton() == folder_btn:
# בחירת תיקייה
dir_path = QFileDialog.getExistingDirectory(
self,
"בחר תיקיית אוצריא"
)
if dir_path:
self.otzaria_path_edit.setText(dir_path)
self.log_message(f"נבחרה תיקייה: {dir_path}")
def browse_output_path(self):
"""בחירת תיקיית פלט"""
dir_path = QFileDialog.getExistingDirectory(
self,
"בחר תיקיית פלט"
)
if dir_path:
self.output_path_edit.setText(dir_path)
self.log_message(f"נבחרה תיקיית פלט: {dir_path}")
def validate_inputs(self):
"""בדיקת תקינות הקלטים"""
otzaria_path = self.otzaria_path_edit.text().strip()
output_path = self.output_path_edit.text().strip()
if not otzaria_path:
QMessageBox.warning(self, "שגיאה", "יש לבחור נתיב קבצי אוצריא")
return False
if not output_path:
QMessageBox.warning(self, "שגיאה", "יש לבחור תיקיית פלט")
return False
if not os.path.exists(otzaria_path):
QMessageBox.warning(self, "שגיאה", "נתיב קבצי אוצריא לא קיים")
return False
# בדיקת הרשאות כתיבה בתיקיית הפלט
try:
if not os.path.exists(output_path):
os.makedirs(output_path, exist_ok=True)
# בדיקת כתיבה
test_file = os.path.join(output_path, "test_write.tmp")
with open(test_file, "w") as f:
f.write("test")
os.remove(test_file)
except Exception as e:
QMessageBox.warning(self, "שגיאה", f"אין הרשאות כתיבה בתיקיית הפלט:\n{str(e)}")
return False
return True
def start_conversion(self):
"""התחלת תהליך ההמרה"""
if not self.validate_inputs():
return
otzaria_path = self.otzaria_path_edit.text().strip()
output_path = self.output_path_edit.text().strip()
# אתחול ממשק
self.convert_btn.setEnabled(False)
self.stop_btn.setEnabled(True)
self.progress_bar.setVisible(True)
self.progress_bar.setValue(0)
self.status_label.setText("מתחיל המרה...")
self.status_label.setStyleSheet("color: #f39c12; font-weight: bold;")
# יצירת worker thread
self.worker = ConversionWorker(otzaria_path, output_path)
self.worker.progress_updated.connect(self.update_progress)
self.worker.status_updated.connect(self.update_status)
self.worker.finished.connect(self.conversion_finished)
self.worker.start()
self.log_message("תהליך ההמרה החל")
logging.info(f"התחלת המרה: {otzaria_path} -> {output_path}")
def stop_conversion(self):
"""עצירת תהליך ההמרה"""
if self.worker and self.worker.isRunning():
self.worker.stop()
self.worker.wait(3000) # חכה עד 3 שניות
if self.worker.isRunning():
self.worker.terminate()
self.worker.wait()
self.log_message("תהליך ההמרה הופסק על ידי המשתמש")
logging.info("המרה הופסקה על ידי המשתמש")
def update_progress(self, current, total):
"""עדכון פס ההתקדמות"""
if total > 0:
percentage = int((current / total) * 100)
self.progress_bar.setValue(percentage)
self.progress_bar.setFormat(f"{current}/{total} ({percentage}%)")
def update_status(self, message):
"""עדכון הודעת הסטטוס"""
self.status_label.setText(message)
self.log_message(message)
def conversion_finished(self, success, message):
"""סיום תהליך ההמרה"""
# איפוס ממשק
self.convert_btn.setEnabled(True)
self.stop_btn.setEnabled(False)
if success:
self.status_label.setText("ההמרה הושלמה בהצלחה!")
self.status_label.setStyleSheet("color: #27ae60; font-weight: bold;")
self.progress_bar.setValue(100)
QMessageBox.information(self, "הצלחה", message)
logging.info(f"המרה הושלמה: {message}")
else:
self.status_label.setText("שגיאה בהמרה")
self.status_label.setStyleSheet("color: #e74c3c; font-weight: bold;")
QMessageBox.critical(self, "שגיאה", message)
logging.error(f"שגיאה בהמרה: {message}")
self.log_message(f"תהליך הסתיים: {message}")
def log_message(self, message):
"""הוספת הודעה ליומן"""
timestamp = datetime.now().strftime("%H:%M:%S")
formatted_message = f"[{timestamp}] {message}"
self.log_text.append(formatted_message)
# גלילה אוטומטית לתחתית
scrollbar = self.log_text.verticalScrollBar()
scrollbar.setValue(scrollbar.maximum())
def closeEvent(self, event):
"""טיפול בסגירת האפליקציה"""
if self.worker and self.worker.isRunning():
reply = QMessageBox.question(
self, 'סגירת האפליקציה',
'תהליך המרה פועל כעת. האם אתה בטוח שברצונך לסגור?',
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
self.worker.stop()
self.worker.wait(3000)
if self.worker.isRunning():
self.worker.terminate()
event.accept()
else:
event.ignore()
else:
event.accept()
logging.info("האפליקציה נסגרה")
def main():
app = QApplication(sys.argv)
# הגדרת עברית
app.setLayoutDirection(Qt.RightToLeft)
app.setStyleSheet("""
QPushButton {
font-size: 14px;
font-weight: bold;
}
QLabel {
font-size: 14px;
}
QLineEdit, QTextEdit {
font-size: 14px;
padding: 5px;
}
QProgressBar {
font-size: 14px;
text-align: center;
}
QMainWindow {
font-family: 'sans-serif', Arial, Helvetica;
font-size: 14px;
}
QFrame {
border-radius: 4px;
}
""")
# יצירת חלון ראשי
window = OtzariaConverterGUI()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
@מנצפכ מה המייל שלך?
@מנצפכ הוא כל הזמן שולח לי כמויות של מיילים של שגיאות.
פרטי השגיאה בספוילר
You are receiving this error either because your input OAuth2 scope name is invalid or it refers to a newer scope that is outside the domain of this legacy API.
This API was built at a time when the scope name format was not yet standardized. This is no longer the case and all valid scope names (both old and new) are catalogued at https://developers.google.com/identity/protocols/oauth2/scopes. Use that webpage to lookup (manually) the scope name associated with the API you are trying to call and use it to craft your OAuth2 request.
שאלתי את קלוד, והתשובה שלו בספוילר
השגיאה שאתה מקבל נובעת מכך שהסקריפט שלך משתמש ב-Gmail API הישן ולא בעדכון הנכון של ה-scopes. הבעיה העיקרית היא ששימוש ב-Gmail.Users.Messages.batchDelete דורש הרשאות מתקדמות שלא מוגדרות כראוי.
כמו כן, לא הצלחתי להריץ את הסקריפט החדש שלך, השגיאה שאני מקבל היא:
GoogleJsonResponseException: API call to gmail.users.settings.filters.delete failed with error: Requested entity was not found.
אשמח בעזרה. אני לא מבין בזה כל כך.
@משחזר-מידע בערך כמה זמן? כלומר מתי לבדוק שוב?
אני יודע שא"א לדעת, אבל בכל אופן.
@טכנולוגי-גו-ניור לא קשור.
מדובר על תוכנה להצגת וחיפוש בקבצי פידיאף של ספרים תורניים.
@משחזר-מידע איך אפשר לדעת? קניתי מיד שני'.
אשמח שתסביר לי איך בודקים את זה.
@אלף-שין כתב בשיתוף | נפתחה הזמנה מרוכזת של כונני איחסון!:
לחכות אני מנסה לזרז את העדכון לתוכנה
לא הבנתי, אתה מחכה לעדכון לתוכנה, או לדיסקים?
@אלף-שין גא"מ ורוצה להזמין.
דיברתי איתם, ויש להם כרגע רק כונן של סאנדיסק קצת יותר יקר, וטיפה פחות מהיר.
מה למעשה??
@האדם-החושב בוא תסביר ברור, מה הוא עושה כדי להמיר את כל ספרי אוצריא לאורייתא?
@משה-ש. תתייג את @האדם-החושב
כאן, יש הוראות ברורות.
@משחזר-מידע אשמח מאוד אם תפענח לי את הדוח, כי יש לי לאחרונה הרבה שגיאות. תודה רבה!!
@מיאני-ומהשמי גם בתוספים אישיים, ניתן להתקין דרך התקנה מקובץ VSIX,
בדיוק כמו שהתקנת את זה ב VSCODE
כאן הבאתי תוכנה שמיועדת להפוך את כל המאגר שהובא בשרשור הזה, לספרים המותאמים לתוכנת אוצריא בלחיצת כפתור אחת.
כולל כותרות!! [כמו שהיו קיימים בקבצים האלו שהיו מיועדים במקור לתורת אמת]
@לאצי ברגע שיהי' ל vscde, יהי' גם ל Windsurf, כי הוא מבוסס עליו, אני כמעט בטוח.
בכל אופן לגבי התוספים, כל התוספים ל vscode עובדים גם על Windsurf
@מיאני-ומהשמי כתב בבקשת מידע | האם יש Windsurf בעברית?:
האיקס זז מעצמו לצד שמאל כשהתוכנה זיהתה שפה תומכת (אולי הבינה שעזרה לי שינתה את זה בלא ששמתי לב אבל אני לי באמת הבנה בזה ולכן אני לא יודע לבדוק את זה)
זה קורה גם בתוסף של VSCODE לערבית. אז זה לא קשור לתרגום שלך.
https://marketplace.visualstudio.com/items?itemName=Arabic-language.vscode-ar
התרגום שלך מיוחד במינו!!
@מיאני-ומהשמי תשלח לי למיל, מעניין אותי לראות.
@מיאני-ומהשמי כתב בבקשת מידע | האם יש Windsurf בעברית?:
ניסיתי לאחרנוה לתרגם קובץ שפה לעברית
לא הבנתי, אתה תרגמת את הקובץ שפה של VSCODE לעברית?
אולי תעלה את זה כאן?
ולמה היית צריך להפוך את האיקס לצד שמאל?
אל תהפוך אותו והכל יסתדר.