Skip to content

Instantly share code, notes, and snippets.

@brunoamaral
Last active September 26, 2025 19:55
Show Gist options
  • Save brunoamaral/4123e22a5ee12362afc0f70882c66540 to your computer and use it in GitHub Desktop.
Save brunoamaral/4123e22a5ee12362afc0f70882c66540 to your computer and use it in GitHub Desktop.
Bash script to export bookmarks from Shiori as obsidian notes.
#!/bin/bash
# Shiori is a good way to bookmark and archive webpage: https://github.com/go-shiori/shiori
# You can self-host it and use this script if you want your bookmarks as obsidian notes.
# This script assumes that shiori is running using docker.
#
# Usage: ./convert_shiori_to_obsidian.sh output_directory
# Example: ./convert_shiori_to_obsidian.sh ./obsidian_notes/
output_dir="$1"
if [ $# -ne 1 ]; then
echo "Usage: $0 <output_directory>"
echo "Example: $0 ./obsidian_notes/"
exit 1
fi
echo "Fetching bookmarks from Shiori..."
# Get bookmarks from Shiori Docker container
if ! docker exec shiori shiori list > /tmp/shiori_bookmarks.txt 2>/dev/null; then
echo "Error: Failed to fetch bookmarks from Shiori container"
echo "Make sure Docker is running and the 'shiori' container exists"
exit 1
fi
input_file="/tmp/shiori_bookmarks.txt"
# Database path
DB_PATH="/home/brunoamaral/containers/Shiori/data/shiori.db"
# Create output directory if it doesn't exist
mkdir -p "$output_dir"
# Function to get existing bookmark IDs from output directory
get_existing_ids() {
if [ -d "$output_dir" ]; then
# Find all .md files and extract id from frontmatter
find "$output_dir" -name "*.md" -type f -exec awk '/^---$/{frontmatter=!frontmatter; next} frontmatter && /^id:/{gsub(/^id:[[:space:]]*/, ""); print; exit}' {} \; 2>/dev/null | grep -v '^$'
fi
}
# Function to check if bookmark ID already exists
id_exists() {
local id_to_check="$1"
if [ -f "$existing_ids_file" ]; then
grep -q "^${id_to_check}$" "$existing_ids_file"
return $?
fi
return 1 # ID doesn't exist if file doesn't exist
}
# Get existing bookmark IDs to avoid duplicates
echo "Scanning existing notes for bookmark IDs..."
existing_ids_file="/tmp/existing_bookmark_ids.txt"
get_existing_ids > "$existing_ids_file"
existing_count=$(wc -l < "$existing_ids_file" 2>/dev/null || echo "0")
echo "Found $existing_count existing bookmark(s)"
# Initialize variables
bookmark_id=""
title=""
url=""
description=""
tags=""
new_bookmarks_count=0
skipped_bookmarks_count=0
# Function to create obsidian note
create_note() {
if [ -n "$bookmark_id" ] && [ -n "$title" ]; then
# Check if this bookmark ID already exists
if id_exists "$bookmark_id"; then
# echo "Skipping bookmark ID $bookmark_id (already exists): $title"
((skipped_bookmarks_count++))
return
fi
# Clean title for filename (remove special characters and truncate)
filename=$(echo "$title" | sed 's/[^a-zA-Z0-9 ]//g' | sed 's/ */ /g' | sed 's/^ *//;s/ *$//')
filename="${filename// /_}"
# Truncate filename to max 120 characters (leaving room for .md extension)
if [ ${#filename} -gt 116 ]; then
filename="${filename:0:116}"
fi
filename="${filename}.md"
# Get content from SQLite database
content=$(sqlite3 "$DB_PATH" "select bc.content from bookmark b left join bookmark_content bc on b.id = bc.docid where b.id=${bookmark_id};" 2>/dev/null)
# Trim leading and trailing whitespace from content
content=$(echo "$content" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
# Create note content
cat > "$output_dir/$filename" << EOF
---
id: $bookmark_id
kind: bookmark
url: $url
tags: [$tags]
created: $(date -Iseconds)
---
# $title
$description
## Link
[$title]($url)
## Tags
$tags
## Content
$content
EOF
echo "Created: $filename (ID: $bookmark_id)"
((new_bookmarks_count++))
fi
}
# Process the input file
while IFS= read -r line; do
if [[ $line =~ ^[0-9]+\. ]]; then
# If we have a previous bookmark, create its note
create_note
# Extract bookmark ID and title
bookmark_id=$(echo "$line" | sed 's/^\([0-9]*\)\..*$/\1/')
title=$(echo "$line" | sed 's/^[0-9]*\. *\(.*\)$/\1/')
# Reset other variables
url=""
description=""
tags=""
elif [[ $line =~ ^[[:space:]]*\> ]]; then
# Extract URL
url=$(echo "$line" | sed 's/^[[:space:]]*> *\(.*\)$/\1/')
elif [[ $line =~ ^[[:space:]]*\+ ]]; then
# Extract description
description=$(echo "$line" | sed 's/^[[:space:]]*+ *\(.*\)$/\1/')
elif [[ $line =~ ^[[:space:]]*# ]]; then
# Extract tags
tags=$(echo "$line" | sed 's/^[[:space:]]*# *\(.*\)$/\1/')
fi
done < "$input_file"
# Don't forget the last bookmark
create_note
# Clean up temporary files
rm -f "/tmp/shiori_bookmarks.txt"
rm -f "$existing_ids_file"
echo "Conversion complete! Notes saved to: $output_dir"
echo "Statistics:"
echo " - Existing bookmarks: $existing_count"
echo " - New bookmarks created: $new_bookmarks_count"
echo " - Bookmarks skipped (already exist): $skipped_bookmarks_count"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment