Naphula commited on
Commit
5e99d47
·
verified ·
1 Parent(s): 99bd682

Upload md_to_smf.py

Browse files
Files changed (1) hide show
  1. md_to_smf.py +144 -0
md_to_smf.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tkinter as tk
2
+ from tkinter import scrolledtext
3
+ from tkinterdnd2 import DND_FILES, TkinterDnD
4
+ import re
5
+ import os
6
+
7
+ # --- Conversion Logic ---
8
+
9
+ def convert_md_to_smf(markdown_text):
10
+ """
11
+ Converts a Markdown string to an SMF-compatible BBCode string.
12
+
13
+ The order of replacements is important to avoid conflicts.
14
+ """
15
+ smf_text = markdown_text
16
+
17
+ # 1. Block elements (Code blocks, blockquotes)
18
+ # Code blocks (```...```) - DOTALL flag allows `.` to match newlines
19
+ smf_text = re.sub(r'```(.*?)```', r'[code]\1[/code]', smf_text, flags=re.DOTALL)
20
+
21
+ # Blockquotes (>)
22
+ smf_text = re.sub(r'^\> (.*)', r'[quote]\1[/quote]', smf_text, flags=re.MULTILINE)
23
+
24
+ # 2. Headings (must be at the start of a line)
25
+ smf_text = re.sub(r'^# (.*)', r'[size=18pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
26
+ smf_text = re.sub(r'^## (.*)', r'[size=16pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
27
+ smf_text = re.sub(r'^### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
28
+ smf_text = re.sub(r'^#### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
29
+
30
+ # 3. Lists (must be at the start of a line)
31
+ # This is a simplified conversion. It doesn't wrap the whole block in [list] tags,
32
+ # as that requires more complex state management, but it's often sufficient.
33
+ smf_text = re.sub(r'^\* (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
34
+ smf_text = re.sub(r'^- (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
35
+ smf_text = re.sub(r'^\d+\. (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE) # Note: SMF may need [list type=decimal] wrapper
36
+
37
+ # 4. Images and Links
38
+ smf_text = re.sub(r'!\[.*?\]\((.*?)\)', r'[img]\1[/img]', smf_text) # Images
39
+ smf_text = re.sub(r'\[(.*?)\]\((.*?)\)', r'[url=\2]\1[/url]', smf_text) # Links
40
+
41
+ # 5. Inline formatting
42
+ smf_text = re.sub(r'\*\*(.*?)\*\*', r'[b]\1[/b]', smf_text) # Bold (**)
43
+ smf_text = re.sub(r'__(.*?)__', r'[b]\1[/b]', smf_text) # Bold (__)
44
+ smf_text = re.sub(r'\*(.*?)\*', r'[i]\1[/i]', smf_text) # Italic (*)
45
+ smf_text = re.sub(r'_(.*?)_', r'[i]\1[/i]', smf_text) # Italic (_)
46
+ smf_text = re.sub(r'~~(.*?)~~', r'[s]\1[/s]', smf_text) # Strikethrough
47
+ smf_text = re.sub(r'`(.*?)`', r'[code]\1[/code]', smf_text) # Inline code
48
+
49
+ # 6. Horizontal Rule
50
+ smf_text = re.sub(r'^\s*---\s*$', '\n[hr]\n', smf_text, flags=re.MULTILINE)
51
+
52
+ return smf_text
53
+
54
+ # --- GUI Application ---
55
+
56
+ class App(TkinterDnD.Tk):
57
+ def __init__(self):
58
+ super().__init__()
59
+ self.title("Markdown to SMF Converter")
60
+ self.geometry("500x400")
61
+
62
+ # Main frame
63
+ main_frame = tk.Frame(self)
64
+ main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
65
+
66
+ # Label for instructions
67
+ self.drop_label = tk.Label(
68
+ main_frame,
69
+ text="Drag and Drop .md files here",
70
+ font=("Arial", 14),
71
+ bg="lightblue",
72
+ relief="solid",
73
+ bd=2
74
+ )
75
+ self.drop_label.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
76
+
77
+ # Scrolled text for status logs
78
+ self.status_log = scrolledtext.ScrolledText(
79
+ main_frame,
80
+ height=10,
81
+ state='disabled' # Make it read-only
82
+ )
83
+ self.status_log.pack(fill=tk.BOTH, expand=True)
84
+
85
+ # Register the drop zone
86
+ self.drop_target_register(DND_FILES)
87
+ self.dnd_bind('<<Drop>>', self.handle_drop)
88
+
89
+ self.log("Ready to convert files.")
90
+
91
+ def log(self, message):
92
+ """Adds a message to the status log."""
93
+ self.status_log.config(state='normal')
94
+ self.status_log.insert(tk.END, message + "\n")
95
+ self.status_log.see(tk.END) # Auto-scroll to the bottom
96
+ self.status_log.config(state='disabled')
97
+
98
+ def handle_drop(self, event):
99
+ """Handles the file drop event."""
100
+ # The event.data is a string of file paths, sometimes in curly braces
101
+ # We clean it up and split it into a list of individual paths
102
+ file_paths = self.tk.splitlist(event.data)
103
+
104
+ self.log("\n--- New Drop Detected ---")
105
+
106
+ for file_path in file_paths:
107
+ if file_path.lower().endswith('.md'):
108
+ self.process_file(file_path)
109
+ else:
110
+ self.log(f"Skipped: '{os.path.basename(file_path)}' is not a .md file.")
111
+
112
+ self.log("--- All files processed ---")
113
+
114
+ def process_file(self, file_path):
115
+ """Reads, converts, and saves a single markdown file."""
116
+ filename = os.path.basename(file_path)
117
+ self.log(f"Processing: '{filename}'...")
118
+
119
+ try:
120
+ # Read the markdown file
121
+ with open(file_path, 'r', encoding='utf-8') as f:
122
+ markdown_content = f.read()
123
+
124
+ # Convert the content
125
+ smf_content = convert_md_to_smf(markdown_content)
126
+
127
+ # Define the output path
128
+ base_name = os.path.splitext(file_path)[0]
129
+ output_path = f"{base_name}_smf.txt"
130
+
131
+ # Save the converted content
132
+ with open(output_path, 'w', encoding='utf-8') as f:
133
+ f.write(smf_content)
134
+
135
+ self.log(f"Success! Saved to: '{os.path.basename(output_path)}'")
136
+
137
+ except Exception as e:
138
+ self.log(f"Error processing '{filename}': {e}")
139
+
140
+ # --- Main Execution ---
141
+
142
+ if __name__ == "__main__":
143
+ app = App()
144
+ app.mainloop()