#!/usr/bin/env python3 # The Spartan Bookmark Launcher (sbl) with embedded bookmarks # License: Blue Oak Model License 1.0.0 # Copyright: https://640kb.neocities.org # 09feb2026: Bookmarks, help section, other code changes # 06oct2025: Additional bookmarks # 15sep2025: Minor changes - help and bookmarks # 07jun2025: Major changes # . embedded the bookmarks directly into the script (portability) # . display only titles from the embedded bookmarks # . added support for comments in the bookmark section # . new '--edit' flag opens script in default editor (configurable) # 09may2025: initial release # Supported Help Flags: -h --help -? /? # --------------------------------------------------------------------------- import sys import os import subprocess DEFAULT_EDITOR = "xed" # Change this to your preferred editor EMBEDDED_BOOKMARKS = """ # ------------------------ ADD / EDIT Bookmarks Here ------------------------ # # The format is: # Title: URL # aligned below for readability but it's not important. # # -------------------------- Great ASCII Art Links -------------------------- Bob Allison's collection: spartan://ascii.mozz.us:7070/mirrors/incredibleart/files/scarecrow_sig_files.txt Six Line Ascii Art Challenge: spartan://ascii.mozz.us:7070/mirrors/vk/files/junkyard/pics/six_line_challenge.txt Rowan Crawford ASCII Art: spartan://ascii.mozz.us:7070/mirrors/afn/artists/crawford.txt The "Spying at the wall" Dictionary: spartan://ascii.mozz.us:7070/mirrors/bowdrie/spying.txt Huge 800k mthw scrollfile [nsfw]: spartan://ascii.mozz.us:7070/mirrors/asciipr0n/pr0n/morepr0n/pr0n91.txt Flump's Favorite Stuff: spartan://ascii.mozz.us:7070/mirrors/flump/data/faves.txt The Funny Bone (1998): spartan://ascii.mozz.us:7070/mirrors/funnybone/1998/funnybone_1998-09-13.txt Alt-ASCII-Art FAQ: spartan://ascii.mozz.us:7070/academy/faqs/faq_randall.txt Anime ASCII Art collection - part 1: spartan://ascii.mozz.us:7070/mirrors/otakuworld/data/anime001.txt ASCII Artist Licenses: spartan://ascii.mozz.us:7070/academy/artists/license.txt Large Archive (mirrors): spartan://ascii.mozz.us:7070/mirrors # --------------------------- Core Spartan Links ---------------------------- Spartan Home Page: spartan://mozz.us Spartan Specifications: spartan://spartan.mozz.us Software for the Spartan Protocol: spartan://mozz.us/software.gmi # ------------------------------ End Bookmarks ------------------------------ """ def show_help(): script_name = os.path.basename(sys.argv[0]) print(f""" .-----------------------------------------------------------------. | SBL: Spartan Bookmark Launcher (with embedded bookmarks) | | | | Displays a numbered list of bookmarks. Select a number to view | | the Title/URL; the bookmarks reappear after viewing the page. | | | | Usage: sbl - Show and launch embedded bookmarks | | sbl --edit - Edit this script | | sbl [-h|--help] - Shows this help message | | | | Edit bookmarks: | | 1. Run: sbl --edit | | 2. Edit the EMBEDDED_BOOKMARKS section | | note: for sudo support, see: def edit_script() | | | | Bookmark format: | | - Page Title: spartan://example.com | | - Lines starting with '#' or blank lines are ignored. | | | | DEFAULT_EDITOR = "xed" | | Update to your preferred editor (used with sbl --edit) | | | | Requirements: | | sv (spartan viewer) and less. Linux/macOS/*BSD | '-----------------------------------------------------------------' """) def parse_embedded_bookmarks(): bookmarks = [] lines = EMBEDDED_BOOKMARKS.splitlines() for line in lines: stripped = line.strip() if not stripped: continue if stripped.startswith('#'): continue if ':' in line: title, url = line.split(':', 1) title = title.strip() url = url.strip() if url.startswith("spartan://"): bookmarks.append((title, url)) return bookmarks def display_bookmarks(bookmarks): # Windows fix (clear vs cls issue) os.system('clear') if not bookmarks: print("No bookmarks available. Please edit the embedded bookmarks section.") return longest = max(len(title) for title, _ in bookmarks) header_width = max(longest + 10, 30) print("=" * header_width) print("SPARTAN BOOKMARK MANAGER".center(header_width)) print("=" * header_width) for idx, (title, _) in enumerate(bookmarks, 1): print(f"[{idx:2}] {title}") print() print("List of bookmarks embedded in this script") print("=" * header_width) def edit_script(): script_path = os.path.abspath(__file__) try: subprocess.run([DEFAULT_EDITOR, script_path], check=True) # If you need sudo: uncomment the line below and comment out the line above. # subprocess.run(["sudo", DEFAULT_EDITOR, script_path], check=True) except subprocess.CalledProcessError: print(f"Failed to open editor. You may need to:") print(f"1. Change DEFAULT_EDITOR in the script") print(f"2. Ensure {DEFAULT_EDITOR} is installed") sys.exit(1) def main(): help_flags = {'-h', '--help', '-?', '/?'} if len(sys.argv) > 1: if sys.argv[1].lower() in help_flags: show_help() sys.exit(0) elif sys.argv[1].lower() == "--edit": edit_script() sys.exit(0) else: show_help() sys.exit(1) bookmarks = parse_embedded_bookmarks() if not bookmarks: print("No valid bookmarks found in embedded section.") sys.exit(1) while True: display_bookmarks(bookmarks) try: choice = input("Enter number to open (0 to quit): ").strip() if choice == '0': break index = int(choice) - 1 if 0 <= index < len(bookmarks): url = bookmarks[index][1] subprocess.run(f"sv {url} | less -R", shell=True) else: print("Invalid choice.") input("Press Enter to continue...") except ValueError: print("Invalid input.") input("Press Enter to continue...") except KeyboardInterrupt: print("\nCanceled.") break if __name__ == "__main__": main()