Skip to content

Commit b6c742b

Browse files
authored
Create Local-URL-Shortener.py
1 parent c5f530f commit b6c742b

1 file changed

Lines changed: 227 additions & 0 deletions

File tree

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import sys
2+
import os
3+
import json
4+
import tkinter as tk
5+
from tkinter import ttk, messagebox
6+
import sv_ttk
7+
import random
8+
9+
# =========================
10+
# Helpers
11+
# =========================
12+
def resource_path(file_name):
13+
base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
14+
return os.path.join(base_path, file_name)
15+
16+
def set_status(msg):
17+
status_var.set(msg)
18+
root.update_idletasks()
19+
20+
def generate_easy_code():
21+
prefixes = ["go", "url", "link", "up", "web"]
22+
prefix = random.choice(prefixes)
23+
number = random.randint(10, 999)
24+
return f"{prefix}{number}"
25+
26+
# =========================
27+
# Storage
28+
# =========================
29+
STORAGE_FILE = resource_path("short_urls.json")
30+
if os.path.exists(STORAGE_FILE):
31+
with open(STORAGE_FILE, "r", encoding="utf-8") as f:
32+
url_mapping = json.load(f)
33+
else:
34+
url_mapping = {}
35+
36+
def save_mapping():
37+
with open(STORAGE_FILE, "w", encoding="utf-8") as f:
38+
json.dump(url_mapping, f, indent=4)
39+
update_url_list()
40+
41+
# =========================
42+
# Core Logic
43+
# =========================
44+
def shorten_url():
45+
original = url_var.get().strip()
46+
if not original:
47+
messagebox.showwarning("Input Required", "Enter a URL to shorten.")
48+
return
49+
50+
for code, url in url_mapping.items():
51+
if url == original:
52+
short_var.set(code)
53+
set_status("URL already shortened.")
54+
return
55+
56+
code = generate_easy_code()
57+
while code in url_mapping:
58+
code = generate_easy_code()
59+
60+
url_mapping[code] = original
61+
save_mapping()
62+
short_var.set(code)
63+
set_status("URL shortened successfully.")
64+
65+
def expand_url():
66+
code = short_var.get().strip()
67+
if not code:
68+
messagebox.showwarning("Input Required", "Enter a short code to expand.")
69+
return
70+
71+
original = url_mapping.get(code)
72+
if original:
73+
url_var.set(original)
74+
set_status("URL expanded successfully.")
75+
else:
76+
messagebox.showerror("Not Found", "Short code does not exist.")
77+
78+
def copy_to_clipboard(entry_var):
79+
root.clipboard_clear()
80+
root.clipboard_append(entry_var.get())
81+
set_status("Copied to clipboard!")
82+
83+
# =========================
84+
# URL List Display
85+
# =========================
86+
def update_url_list():
87+
for widget in list_frame_inner.winfo_children():
88+
widget.destroy()
89+
90+
for i, (code, url) in enumerate(url_mapping.items(), 1):
91+
ttk.Label(
92+
list_frame_inner,
93+
text=f"{i}. {code}{url}",
94+
font=("Segoe UI", 10),
95+
anchor="w"
96+
).pack(fill="x", padx=5, pady=2)
97+
98+
# =========================
99+
# App Setup
100+
# =========================
101+
root = tk.Tk()
102+
root.title("Local URL Shortener")
103+
root.geometry("1100x650")
104+
sv_ttk.set_theme("light")
105+
106+
# =========================
107+
# Globals
108+
# =========================
109+
url_var = tk.StringVar()
110+
short_var = tk.StringVar()
111+
dark_mode_var = tk.BooleanVar(value=False)
112+
113+
# =========================
114+
# Theme Toggle
115+
# =========================
116+
def toggle_theme():
117+
style.theme_use("clam")
118+
bg = "#2E2E2E" if dark_mode_var.get() else "#FFFFFF"
119+
fg = "white" if dark_mode_var.get() else "black"
120+
121+
root.configure(bg=bg)
122+
for w in ["TFrame", "TLabel", "TLabelframe", "TLabelframe.Label", "TCheckbutton"]:
123+
style.configure(w, background=bg, foreground=fg)
124+
125+
for entry in [url_entry, short_entry]:
126+
entry.configure(
127+
bg="#1e1e1e" if dark_mode_var.get() else "white",
128+
fg="white" if dark_mode_var.get() else "black",
129+
insertbackground="white" if dark_mode_var.get() else "black"
130+
)
131+
132+
update_url_list()
133+
set_status(f"Theme switched to {'Dark' if dark_mode_var.get() else 'Light'} mode")
134+
135+
# =========================
136+
# Styles
137+
# =========================
138+
style = ttk.Style()
139+
style.theme_use("clam")
140+
style.configure(
141+
"Action.TButton",
142+
font=("Segoe UI", 11, "bold"),
143+
foreground="white",
144+
background="#4CAF50",
145+
padding=8
146+
)
147+
style.map("Action.TButton", background=[("active", "#45a049")])
148+
149+
# =========================
150+
# Status Bar
151+
# =========================
152+
status_var = tk.StringVar(value="Ready")
153+
ttk.Label(root, textvariable=status_var, anchor="w").pack(side=tk.BOTTOM, fill="x")
154+
155+
# =========================
156+
# Main UI
157+
# =========================
158+
main = ttk.Frame(root, padding=20)
159+
main.pack(expand=True, fill="both")
160+
161+
ttk.Label(main, text="Local URL Shortener", font=("Segoe UI", 22, "bold")).pack()
162+
ttk.Label(main, text="Shorten URLs locally without internet", font=("Segoe UI", 11)).pack(pady=(0, 10))
163+
164+
# =========================
165+
# URL Input
166+
# =========================
167+
url_frame = ttk.LabelFrame(main, text="Original URL", padding=10)
168+
url_frame.pack(fill="x", pady=5)
169+
170+
url_entry = tk.Entry(url_frame, textvariable=url_var, font=("Segoe UI", 12))
171+
url_entry.pack(fill="x", padx=5, pady=5)
172+
173+
# =========================
174+
# Short Code
175+
# =========================
176+
short_frame = ttk.LabelFrame(main, text="Short Code", padding=10)
177+
short_frame.pack(fill="x", pady=5)
178+
179+
short_entry = tk.Entry(short_frame, textvariable=short_var, font=("Segoe UI", 12))
180+
short_entry.pack(fill="x", padx=5, pady=5)
181+
182+
# =========================
183+
# Actions
184+
# =========================
185+
actions = ttk.Frame(main)
186+
actions.pack(pady=10)
187+
188+
ttk.Button(actions, text="🔗 Shorten", command=shorten_url, style="Action.TButton").pack(side="left", padx=6)
189+
ttk.Button(actions, text="🔍 Expand", command=expand_url, style="Action.TButton").pack(side="left", padx=6)
190+
ttk.Button(actions, text="📋 Copy URL", command=lambda: copy_to_clipboard(url_var), style="Action.TButton").pack(side="left", padx=6)
191+
ttk.Button(actions, text="📋 Copy Code", command=lambda: copy_to_clipboard(short_var), style="Action.TButton").pack(side="left", padx=6)
192+
ttk.Checkbutton(actions, text="Dark Mode", variable=dark_mode_var, command=toggle_theme).pack(side="left", padx=14)
193+
194+
# =========================
195+
# Stored URLs List (FIXED)
196+
# =========================
197+
list_frame_outer = ttk.LabelFrame(main, text="Stored URLs", padding=10)
198+
list_frame_outer.pack(fill="both", pady=10, expand=True)
199+
200+
# Canvas (FIX)
201+
list_frame_canvas = tk.Canvas(list_frame_outer)
202+
list_frame_canvas.pack(side="left", fill="both", expand=True)
203+
204+
scrollbar = ttk.Scrollbar(list_frame_outer, orient="vertical", command=list_frame_canvas.yview)
205+
scrollbar.pack(side="right", fill="y")
206+
207+
list_frame_canvas.configure(yscrollcommand=scrollbar.set)
208+
209+
list_frame_inner = ttk.Frame(list_frame_canvas)
210+
list_frame_canvas.create_window((0, 0), window=list_frame_inner, anchor="nw")
211+
212+
def on_frame_configure(event):
213+
list_frame_canvas.configure(scrollregion=list_frame_canvas.bbox("all"))
214+
215+
list_frame_inner.bind("<Configure>", on_frame_configure)
216+
217+
def on_mousewheel(event):
218+
list_frame_canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")
219+
220+
list_frame_canvas.bind_all("<MouseWheel>", on_mousewheel)
221+
222+
update_url_list()
223+
224+
# =========================
225+
# Run App
226+
# =========================
227+
root.mainloop()

0 commit comments

Comments
 (0)