model_tools / md_to_smf.py
Naphula's picture
Upload md_to_smf.py
5e99d47 verified
import tkinter as tk
from tkinter import scrolledtext
from tkinterdnd2 import DND_FILES, TkinterDnD
import re
import os
# --- Conversion Logic ---
def convert_md_to_smf(markdown_text):
"""
Converts a Markdown string to an SMF-compatible BBCode string.
The order of replacements is important to avoid conflicts.
"""
smf_text = markdown_text
# 1. Block elements (Code blocks, blockquotes)
# Code blocks (```...```) - DOTALL flag allows `.` to match newlines
smf_text = re.sub(r'```(.*?)```', r'[code]\1[/code]', smf_text, flags=re.DOTALL)
# Blockquotes (>)
smf_text = re.sub(r'^\> (.*)', r'[quote]\1[/quote]', smf_text, flags=re.MULTILINE)
# 2. Headings (must be at the start of a line)
smf_text = re.sub(r'^# (.*)', r'[size=18pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^## (.*)', r'[size=16pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^#### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
# 3. Lists (must be at the start of a line)
# This is a simplified conversion. It doesn't wrap the whole block in [list] tags,
# as that requires more complex state management, but it's often sufficient.
smf_text = re.sub(r'^\* (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^- (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^\d+\. (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE) # Note: SMF may need [list type=decimal] wrapper
# 4. Images and Links
smf_text = re.sub(r'!\[.*?\]\((.*?)\)', r'[img]\1[/img]', smf_text) # Images
smf_text = re.sub(r'\[(.*?)\]\((.*?)\)', r'[url=\2]\1[/url]', smf_text) # Links
# 5. Inline formatting
smf_text = re.sub(r'\*\*(.*?)\*\*', r'[b]\1[/b]', smf_text) # Bold (**)
smf_text = re.sub(r'__(.*?)__', r'[b]\1[/b]', smf_text) # Bold (__)
smf_text = re.sub(r'\*(.*?)\*', r'[i]\1[/i]', smf_text) # Italic (*)
smf_text = re.sub(r'_(.*?)_', r'[i]\1[/i]', smf_text) # Italic (_)
smf_text = re.sub(r'~~(.*?)~~', r'[s]\1[/s]', smf_text) # Strikethrough
smf_text = re.sub(r'`(.*?)`', r'[code]\1[/code]', smf_text) # Inline code
# 6. Horizontal Rule
smf_text = re.sub(r'^\s*---\s*$', '\n[hr]\n', smf_text, flags=re.MULTILINE)
return smf_text
# --- GUI Application ---
class App(TkinterDnD.Tk):
def __init__(self):
super().__init__()
self.title("Markdown to SMF Converter")
self.geometry("500x400")
# Main frame
main_frame = tk.Frame(self)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Label for instructions
self.drop_label = tk.Label(
main_frame,
text="Drag and Drop .md files here",
font=("Arial", 14),
bg="lightblue",
relief="solid",
bd=2
)
self.drop_label.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
# Scrolled text for status logs
self.status_log = scrolledtext.ScrolledText(
main_frame,
height=10,
state='disabled' # Make it read-only
)
self.status_log.pack(fill=tk.BOTH, expand=True)
# Register the drop zone
self.drop_target_register(DND_FILES)
self.dnd_bind('<<Drop>>', self.handle_drop)
self.log("Ready to convert files.")
def log(self, message):
"""Adds a message to the status log."""
self.status_log.config(state='normal')
self.status_log.insert(tk.END, message + "\n")
self.status_log.see(tk.END) # Auto-scroll to the bottom
self.status_log.config(state='disabled')
def handle_drop(self, event):
"""Handles the file drop event."""
# The event.data is a string of file paths, sometimes in curly braces
# We clean it up and split it into a list of individual paths
file_paths = self.tk.splitlist(event.data)
self.log("\n--- New Drop Detected ---")
for file_path in file_paths:
if file_path.lower().endswith('.md'):
self.process_file(file_path)
else:
self.log(f"Skipped: '{os.path.basename(file_path)}' is not a .md file.")
self.log("--- All files processed ---")
def process_file(self, file_path):
"""Reads, converts, and saves a single markdown file."""
filename = os.path.basename(file_path)
self.log(f"Processing: '{filename}'...")
try:
# Read the markdown file
with open(file_path, 'r', encoding='utf-8') as f:
markdown_content = f.read()
# Convert the content
smf_content = convert_md_to_smf(markdown_content)
# Define the output path
base_name = os.path.splitext(file_path)[0]
output_path = f"{base_name}_smf.txt"
# Save the converted content
with open(output_path, 'w', encoding='utf-8') as f:
f.write(smf_content)
self.log(f"Success! Saved to: '{os.path.basename(output_path)}'")
except Exception as e:
self.log(f"Error processing '{filename}': {e}")
# --- Main Execution ---
if __name__ == "__main__":
app = App()
app.mainloop()