בירור | הוספת הספרים של אוצריא לאורייתא
-
כידוע שתוכנת אוצריא אינה מאוד מותאמת לנגנים, קשה לקרוא ולחפש ולעיין כי זה בנוי למסך גדול יותר
מנגד יש את אפליקציית אורייתא שמותאמת גם למכשירים קטנים, אבל מלאי הספרים שם מוגבל מאוד,
הכי טוב יהיה אם יעשו פיתוח מיוחד של אוצריא שמותאם למסך קטן
אבל בינתיים, אולי יש דרך להוסיף את הספרים של אוצריא לאפליקציית אורייתא?
אני לא מבין בכל הקידודים למיניהם
יש מישהו שיכול להכין מאגר כזה, או לפחות להדריך מה צריך לעשות? -
כידוע שתוכנת אוצריא אינה מאוד מותאמת לנגנים, קשה לקרוא ולחפש ולעיין כי זה בנוי למסך גדול יותר
מנגד יש את אפליקציית אורייתא שמותאמת גם למכשירים קטנים, אבל מלאי הספרים שם מוגבל מאוד,
הכי טוב יהיה אם יעשו פיתוח מיוחד של אוצריא שמותאם למסך קטן
אבל בינתיים, אולי יש דרך להוסיף את הספרים של אוצריא לאפליקציית אורייתא?
אני לא מבין בכל הקידודים למיניהם
יש מישהו שיכול להכין מאגר כזה, או לפחות להדריך מה צריך לעשות?@משה-ש. תתייג את @האדם-החושב
כאן, יש הוראות ברורות. -
כידוע שתוכנת אוצריא אינה מאוד מותאמת לנגנים, קשה לקרוא ולחפש ולעיין כי זה בנוי למסך גדול יותר
מנגד יש את אפליקציית אורייתא שמותאמת גם למכשירים קטנים, אבל מלאי הספרים שם מוגבל מאוד,
הכי טוב יהיה אם יעשו פיתוח מיוחד של אוצריא שמותאם למסך קטן
אבל בינתיים, אולי יש דרך להוסיף את הספרים של אוצריא לאפליקציית אורייתא?
אני לא מבין בכל הקידודים למיניהם
יש מישהו שיכול להכין מאגר כזה, או לפחות להדריך מה צריך לעשות? -
@משה-ש. שם הקובץ נמצא בהערות ארכיון (באנדרואיד ולינוקס, בווינ' יש קובץ נפרד)
שם התיקייה נמצא בתקיה מעל התיקיה הנוכחית בסיומת .folder
קובץ הספרים הוא בעצם קובץ zip בסיומת obk.@האדם-החושב בוא תסביר ברור, מה הוא עושה כדי להמיר את כל ספרי אוצריא לאורייתא?
-
@האדם-החושב @יום-חדש-מתחיל
למעשה יש איזה דרך להריץ את זה על כל קבצי אוצריא בבת אחת, או שצריך קובץ קובץ -
import zipfile import os from bs4 import BeautifulSoup otzaria_path = r"C:\אוצריא\אוצריא" output_path = "אורייתא" def convert_text(text: str) -> str: 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}") # type: ignore return str(soup) def to_zip(comment: str, file_path: str, file_content: str) -> None: with zipfile.ZipFile(file_path, "w") as zip_file: zip_file.comment = bytes(comment, "utf-8") zip_file.writestr("BookText", file_content) def main() -> None: unique_id = 3000 for root, dirs, files in os.walk(otzaria_path): rel_path = os.path.relpath(root, otzaria_path) for dir in dirs: if not os.listdir(os.path.join(root, dir)): continue os.makedirs(os.path.join(output_path, rel_path, dir), exist_ok=True) with open(os.path.join(output_path, rel_path, f"{dir}.folder"), "w", encoding="utf-8") as f: f.write(f"BranchName={dir}") for file in files: if not file.lower().endswith(".txt"): continue unique_id += 1 file_name = os.path.splitext(file)[0] with open(os.path.join(root, file), "r", encoding="utf-8") as f: text = f.read() converted_text = convert_text(text) comment = f"UniqueId={unique_id}\nDisplayName={file_name}" to_zip(comment, os.path.join(output_path, rel_path, f"{file_name}.obk"), converted_text) if __name__ == "__main__": main()
-
@משה-ש. הנה הסקריפט של האדם החושב עם ממשק גרפי חמוד. בספוילר
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()
-
import zipfile import os from bs4 import BeautifulSoup otzaria_path = r"C:\אוצריא\אוצריא" output_path = "אורייתא" def convert_text(text: str) -> str: 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}") # type: ignore return str(soup) def to_zip(comment: str, file_path: str, file_content: str) -> None: with zipfile.ZipFile(file_path, "w") as zip_file: zip_file.comment = bytes(comment, "utf-8") zip_file.writestr("BookText", file_content) def main() -> None: unique_id = 3000 for root, dirs, files in os.walk(otzaria_path): rel_path = os.path.relpath(root, otzaria_path) for dir in dirs: if not os.listdir(os.path.join(root, dir)): continue os.makedirs(os.path.join(output_path, rel_path, dir), exist_ok=True) with open(os.path.join(output_path, rel_path, f"{dir}.folder"), "w", encoding="utf-8") as f: f.write(f"BranchName={dir}") for file in files: if not file.lower().endswith(".txt"): continue unique_id += 1 file_name = os.path.splitext(file)[0] with open(os.path.join(root, file), "r", encoding="utf-8") as f: text = f.read() converted_text = convert_text(text) comment = f"UniqueId={unique_id}\nDisplayName={file_name}" to_zip(comment, os.path.join(output_path, rel_path, f"{file_name}.obk"), converted_text) if __name__ == "__main__": main()
-
@האדם-החושב איפה להריץ את זה, פשוט להפעיל את זה במחשב?
בתיקיה של אוצריא?
איפה אני ימצא את הקבצים שנוצרואולי כדאי שפשוט תעלה תיקיה מוכנה לבורים כמוני
@משה-ש. זה קוד פייתון. צריך להתקין פייתון בשביל להריץ את זה.
בכל אופן, קימפלתי לך את זה לתוכנה רגילה, לא דורש פייתון ולא שום דבר.
המרה מאוצריא לאורייתא.exe