Skip to content

Instantly share code, notes, and snippets.

@alastori
Created July 30, 2024 18:54
Show Gist options
  • Save alastori/11afec679a520d3f968501867b96e080 to your computer and use it in GitHub Desktop.
Save alastori/11afec679a520d3f968501867b96e080 to your computer and use it in GitHub Desktop.
This Python script processes referenced screenshots in Obsidian Markdown files, renaming them to include the Markdown file's name and a unique timestamp. It updates the Markdown files with the new filenames and generates a processing report. Create a config.ini file with your notes directory and maximum filename length, then run the script.
# org-obsidian-screenshots.py
"""
Script to Organize and Rename Screenshot Files in Obsidian Markdown Notes
Purpose:
This script processes referenced screenshots in Obsidian Markdown (`.md`) files. It renames the screenshot files to include the name of the Markdown file and a unique timestamp. Additionally, it updates the Markdown files to reference the new screenshot filenames and generates a processing report.
Usage:
1. Create a `config.ini` file in the same directory as the script with the following content:
[DEFAULT]
notes_dir = /path/to/your/obsidian/notes
max_filename_length = 255
2. Run the script:
python org-obsidian-screenshots.py
Dependencies:
- Python 3.x
- No external dependencies are required (only Python standard library is used).
The script will:
- Generate a report listing files with no screenshot images.
- Ask for confirmation to rename screenshots for each `.md` file individually.
- Save the processing report to a text file with a timestamped filename.
"""
import os
import re
import shutil
from datetime import datetime, timedelta
import configparser
# Load configuration from config.ini file
config = configparser.ConfigParser()
config.read('config.ini')
notes_dir = config['DEFAULT']['notes_dir']
MAX_FILENAME_LENGTH = int(config['DEFAULT']['max_filename_length'])
def list_screenshot_files_to_rename(notes_dir):
"""
Identify Markdown files and list the screenshots to be renamed.
Args:
notes_dir (str): The directory containing the Markdown files and screenshots.
Returns:
list: A list of tuples containing the Markdown file, original screenshot file, and new screenshot file.
list: A list of tuples indicating whether screenshots were found in each Markdown file.
"""
screenshots_to_rename = []
markdown_files = []
analyzed_files = []
# Get all .md files and sort them by modification time (most recent first)
files = [f for f in os.listdir(notes_dir) if f.endswith('.md')]
files.sort(key=lambda f: os.path.getmtime(os.path.join(notes_dir, f)), reverse=True)
for file in files:
found_screenshot = False
base_name = os.path.splitext(file)[0]
analyzed_files.append((file, found_screenshot))
with open(os.path.join(notes_dir, file), 'r') as md_file:
lines = md_file.readlines()
for line in lines:
match = re.search(r'!\[.*?\]\((Screenshot.*?|Pasted image.*?)\)|!\[\[.*?(Screenshot.*?|Pasted image.*?)\]\]', line)
if match:
found_screenshot = True
screenshot_file = match.group(1) or match.group(2)
if os.path.exists(os.path.join(notes_dir, screenshot_file)):
screenshot_path = os.path.join(notes_dir, screenshot_file)
timestamp = datetime.fromtimestamp(os.path.getmtime(screenshot_path))
new_screenshot_file = f"{base_name}-Screenshot-{timestamp.strftime('%Y-%m-%d-%H%M%S')}{os.path.splitext(screenshot_file)[1]}"
# Ensure the new filename is unique by incrementing the timestamp if needed
while os.path.exists(os.path.join(notes_dir, new_screenshot_file)):
timestamp += timedelta(seconds=1)
new_screenshot_file = f"{base_name}-Screenshot-{timestamp.strftime('%Y-%m-%d-%H%M%S')}{os.path.splitext(screenshot_file)[1]}"
# Shorten the base name if necessary
if len(new_screenshot_file) > MAX_FILENAME_LENGTH:
excess_length = len(new_screenshot_file) - MAX_FILENAME_LENGTH
base_name = base_name[:-excess_length]
new_screenshot_file = f"{base_name}-Screenshot-{timestamp.strftime('%Y-%m-%d-%H%M%S')}{os.path.splitext(screenshot_file)[1]}"
screenshots_to_rename.append((file, screenshot_file, new_screenshot_file))
analyzed_files[-1] = (file, found_screenshot)
return screenshots_to_rename, analyzed_files
def rename_screenshot_files(notes_dir, screenshots_to_rename, report_file):
"""
Rename screenshot files and update references in Markdown files.
Args:
notes_dir (str): The directory containing the Markdown files and screenshots.
screenshots_to_rename (dict): A dictionary of Markdown files and their associated screenshots to rename.
report_file (file): The file object to write the report to.
Returns:
set: A set of processed Markdown files.
"""
processed_files = set()
for md_file, screenshot_group in screenshots_to_rename.items():
for original_screenshot, new_screenshot in screenshot_group:
original_screenshot_path = os.path.join(notes_dir, original_screenshot)
new_screenshot_path = os.path.join(notes_dir, new_screenshot)
if not os.path.exists(original_screenshot_path):
report_file.write(f"File {original_screenshot} does not exist. Skipping renaming.\n")
print(f"File {original_screenshot} does not exist. Skipping renaming.")
continue
if os.path.exists(new_screenshot_path):
report_file.write(f"File {new_screenshot} already exists. Skipping renaming for {original_screenshot}.\n")
print(f"File {new_screenshot} already exists. Skipping renaming for {original_screenshot}.")
continue
shutil.move(original_screenshot_path, new_screenshot_path)
update_markdown_references(notes_dir, md_file, original_screenshot, new_screenshot, report_file)
processed_files.add(md_file)
return processed_files
def update_markdown_references(notes_dir, md_file, original_screenshot, new_screenshot, report_file):
"""
Update screenshot references in Markdown files.
Args:
notes_dir (str): The directory containing the Markdown files and screenshots.
md_file (str): The Markdown file to update.
original_screenshot (str): The original screenshot file name.
new_screenshot (str): The new screenshot file name.
report_file (file): The file object to write the report to.
"""
report_file.write(f"**Updating:** {md_file}... references to {original_screenshot} --> {new_screenshot}\n")
print(f"**Updating:** {md_file}... references to {original_screenshot} --> {new_screenshot}")
md_file_path = os.path.join(notes_dir, md_file)
with open(md_file_path, 'r') as file:
content = file.read()
updated_content = content.replace(original_screenshot, new_screenshot)
with open(md_file_path, 'w') as file:
file.write(updated_content)
report_file.write(f"Updated references in {md_file}\n")
print(f"Updated references in {md_file}")
def organize_and_rename_screenshots(notes_dir, report_file):
"""
Main function to organize and rename screenshots in notes.
Args:
notes_dir (str): The directory containing the Markdown files and screenshots.
report_file (file): The file object to write the report to.
"""
screenshots_to_rename, analyzed_files = list_screenshot_files_to_rename(notes_dir)
report_file.write("\n**No screenshot found**\n")
print("\n**No screenshot found**")
for file, found_screenshot in analyzed_files:
if not found_screenshot:
report_file.write(f"- {file}\n")
print(f"- {file}")
if not screenshots_to_rename:
report_file.write("No screenshots to rename.\n")
print("No screenshots to rename.")
return
# Group screenshots by their markdown file
screenshots_by_md_file = {}
for md_file, original_screenshot, new_screenshot in screenshots_to_rename:
if md_file not in screenshots_by_md_file:
screenshots_by_md_file[md_file] = []
screenshots_by_md_file[md_file].append((original_screenshot, new_screenshot))
for md_file, screenshot_group in screenshots_by_md_file.items():
report_file.write("\n**Renaming:**\n")
print("\n**Renaming:**")
# Reset the timestamp for each md_file to ensure unique filenames within the same md_file group
timestamp_increment = 0
unique_new_screenshot_files = []
for original_screenshot, new_screenshot in screenshot_group:
while any(new_screenshot == u for _, u in unique_new_screenshot_files):
timestamp_increment += 1
base_name, rest = new_screenshot.rsplit('-Screenshot-', 1)
timestamp = datetime.strptime(rest.split('.')[0], '%Y-%m-%d-%H%M%S')
timestamp += timedelta(seconds=timestamp_increment)
new_screenshot = f"{base_name}-Screenshot-{timestamp.strftime('%Y-%m-%d-%H%M%S')}{os.path.splitext(original_screenshot)[1]}"
unique_new_screenshot_files.append((original_screenshot, new_screenshot))
report_file.write(f"{original_screenshot} --> {new_screenshot}\n")
print(f"{original_screenshot} --> {new_screenshot}")
report_file.flush()
confirmation = input(f"Do you want to rename the screenshots in {md_file}? (yes/no): ").strip().lower()
if confirmation == 'yes':
rename_screenshot_files(notes_dir, {md_file: unique_new_screenshot_files}, report_file)
else:
report_file.write(f"Skipping renaming for {md_file}.\n")
print(f"Skipping renaming for {md_file}.")
report_file.flush()
# Prepare the report file
timestamp = datetime.now().strftime('%Y-%m-%d-%H%M%S')
report_filename = f"org-obsidian-screenshots-{timestamp}.txt"
report_file_path = os.path.join(notes_dir, report_filename)
report_file = open(report_file_path, 'w')
# Call the main function
organize_and_rename_screenshots(notes_dir, report_file)
# Close the report file
report_file.close()
print(f"\nProcessing report saved as {report_filename}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment