Mastodon bookmarks media downloads.

Problem: if you’re hosting your own Mastodon instance and subscribe to some feed with images, your instance footprint would grow like hell. You can purge ‘old media,’ but that’ll ruin your bookmarks. This snippet will get all your media from bookmarks.

Generate a list of S3 URLs for media attached to bookmarked statuses. Output: export/bookmarked_media_s3_urls.txt

# script/list_bookmarked_media_s3.rb
#
# Generate list of S3 URLs for media attached to bookmarked statuses.
# Output: export/bookmarked_media_s3_urls.txt

require "fileutils"
require "uri"

S3_HOST   = "https://s3.andreybondarenko.com"
S3_BUCKET = "mastodon" # path-style bucket

EXPORT_DIR  = Rails.root.join("export")
OUTPUT_PATH = EXPORT_DIR.join("bookmarked_media_s3_urls.txt")

FileUtils.mkdir_p(EXPORT_DIR)

puts "Writing S3 URL list to: #{OUTPUT_PATH}"

File.open(OUTPUT_PATH, "w") do |out|
  Bookmark.includes(status: :media_attachments).find_each do |bookmark|
    status = bookmark.status
    next unless status

    status.media_attachments.each do |media|
      begin
        uploader = media.file if media.respond_to?(:file)
        next unless uploader

        key = nil

        # 1) Try file.path (often S3 key or local path)
        if uploader.respond_to?(:path) && uploader.path.present?
          path = uploader.path.to_s

          # If this is a local path with /system/ in it, strip everything up to /system/
          if path.include?("/system/")
            key = path.split("/system/", 2).last
            key = "system/#{key}"
          else
            # Otherwise assume it's already a key
            key = path.sub(%r{^/}, "")
          end
        end

        # 2) Fallback: parse from file.url if path wasn't usable
        if key.nil? && uploader.respond_to?(:url) && uploader.url.present?
          url = uploader.url.to_s

          if url.start_with?("http")
            uri = URI(url)
            # If path starts with /mastodon/, strip that as bucket prefix
            # /mastodon/system/... -> system/...
            path = uri.path.sub(%r{^/}, "")
            if path.start_with?("#{S3_BUCKET}/")
              key = path.sub(%r{^#{Regexp.escape(S3_BUCKET)}/}, "")
            else
              # If it's already like system/..., accept as key
              key = path
            end
          else
            # relative url like /system/media_attachments/...
            key = url.sub(%r{^/}, "")
          end
        end

        unless key && !key.empty?
          warn "Could not determine key for media #{media.id}, bookmark #{bookmark.id}"
          next
        end

        full_url = "#{S3_HOST}/#{S3_BUCKET}/#{key}"
        out.puts(full_url)
      rescue => e
        warn "Error for bookmark #{bookmark.id}, media #{media.id}: #{e.class} #{e.message}"
      end
    end
  end
end

puts "Done."

Sieve shinannigans

Welp, if you’re using Sieve with the Dovecot (like you should), the client may behave strange, due to Sieve needs some dot-files and they naturally may come in your mailbox/maildir dir. Some clients are aware, some clients not, but the best way is to put dotfiles somewhere else. Like:

  90-sieve: |
    plugin {
      sieve = file:~/sieve;active=/var/mail/sieve/%u/.dovecot.sieve
    }

Otherwise errors/warnings in the logs, like in Mail:app of the Nextcloud.

Yet another cluster update

UniFi: made a deployment and network setup. Does not see AP’s. 10001 UDP seems to be a problem, yet no idea why. Adopted them manually via SSH, now they work. Yes, consumes RAM for no reason. Why? No idea. However, the functionality is limited compared to the Windows app, and it does not provide me with anything valuable. Consume RAM and CPU, so be it, no use.

Mastodon: it wants Elasticsearch that wants RAM. OKAY, I didn’t even want to really use it. Maybe the next more powerful setup will do.

Cluster update

Out of all services, only Mastodon and UniFi Network controller are left, both are technically possible, but require strange things.

Own Bitwarden service works great, however, needs a license to be OTP key. The Minecraft server works great. Done log aggregation, local Docker registry, metrics to have them in Lens, backups of everything. The phone syncs files, contacts and calendar with the Nextcloud no problem.

Wouldn’t do antivirus, greylisting and Plex (maybe Plex will do much later). Firefox instance is useless, since I sync bookmarks only and passwords are in the Bitwarden. Video transcoding works much better with the RTX 4070ti, and I need only one video to transcode per week. LanguageTool instance is useless, since it’s impossible to use it with “premium” plan that really helps me. Done Dockerfile for it anyway.

Maybe I’ll do Clamav instance, though.

OrangePi 5b emmc support

Armbian image from https://www.armbian.com/orangepi-5/ by default thinks that the board is 5, not 5b, so no nice emmc storage for you! I thought that it’s kind of general issue with the kernel, but all it took is to change fdtfile to rockchip/rk3588s-orangepi-5b.dtb in the /boot/armbianEnv.txt. Now my /var with all K3S and rancher stuff is on a fast reliable emmc, not sd-card.

Current state of my K8S

Works:

  • WordPress
  • Tiny-Tiny RSS
  • Matrix
  • Nextcloud with image recognition
  • Dovecot
  • Postfix with DKIM
  • MySQL
  • PostgresQL

To do:

  • Mastodon (dunno why but why not)
  • Greylisintg
  • Antispam
  • Antivirus (clamav)
  • Minecraft server
  • Own Language tool instance
  • Own Firefox sync instance
  • Calendar and contacts sync
  • Ubiquity controller
  • Logs and metrics collection properly
  • Local docker registry
  • Video transcoding
  • Other bits and dimes
  • ????