Moving from Medium

31st Jan '233 of your Earth minutes

I finally did it! I took the plunge and am running my own blog.

It's been many years since I ran my own site 'properly'. And now that it's practically free, it was about time.

For the eagle-eyed among you, you'll know that I sporadically published content to Medium. But with the sheer volume of content there—plus their approach to paywalling–I've really lost the desire to publish there.

There was a time when the writing experience on Medium truly was delightful, but I think that has disappeared too.

I'm sure it will do well for a while longer, but it's just not for me.

I haven't shut my account there down completely yet, but I'm sure it won't be long before I do.

But I am (at least to some degree) proud of the few things I wrote on there. So I wanted to make sure I kept those articles around.

I also have a lot of very old articles on Blogger that I've never done anything with. I'm sure there's some cringey nonsense in there.

What this runs on

I decided back in September '22 to pull all of my Medium content down using their export tool.

My site has been a Statamic-based, statically-generated, Netlify-served thing just for the homepage for almost 3 years, so it was about time I gave it something else to do.

I put all of the files from Medium's download into a folder and wrote a simple command:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use League\HTMLToMarkdown\HtmlConverter;
use Statamic\Facades\YAML;

class MediumConverter extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'convert:medium';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $converter = new HtmlConverter(['strip_tags' => true]);

        $files = Storage::files('medium-posts');

        foreach ($files as $file) {
            // Make the filename Statamic friendly
            $filename = Str::beforeLast(Str::replaceFirst('_', '.', Str::lower(File::name($file))), '-').'.md';
            
            $published = true;
            
            if (Str::startsWith($filename, 'draft')) {
                $published = false;
                
                // Strip 'draft' from the filename
                $filename = Str::replaceFirst('draft', date('Y-m-d'), $filename);
            }

            $slug = Str::after($filename, '.');

            // Clear out the Medium CSS and meta
            $html = preg_replace('/\<head\>.*\<\/head\>/ims', '', Storage::get($file));

            $markdown = $converter->convert($html);

            $collection = collect(explode("\n", $markdown));

            $title = $collection->shift();
            $id = (string) Str::uuid();

            $collection = $collection->skipUntil(function ($item) {
               return Str::endsWith($item, '---');
            });

            $subtitle = trim(Str::replace('---', '', $collection->shift()));
            
            // Remove the Medium footer
            $collection->pop(8);

            $markdown = $collection->implode("\n");

            $frontmatter = YAML::dumpFrontMatter(compact('id', 'title', 'subtitle', 'published'));

            File::put(base_path("content/collections/articles/{$filename}"), $frontmatter.$markdown);
        }

        return 0;
    }
}

This did enough to create all of the posts in a format that Statamic would be happy with.

I then needed to go through and do a few chunks of manual cleanup (some of which I could have made a part of this command, but 🤷🏻‍♂️) - this was essentially a bunch of 'find and replace' in my IDE.

I've added a few styles and adjusted things for better reading and that's it. I haven't moved any images anywhere yet.

My cheap and cheerful, self-hosted blog, backed by a fully-fledged CMS and hosted for free.

The best part? I can completely craft the editor experience I want. I write content and save it locally, so I don't need an internet connection just to write. When I'm ready to publish, I just commit and push via Git.

What's more, I can do all of that from the CMS itself thanks to my Statamic Git add-on!

Even better: everyone can read my content without hitting some signup process or banners all over the place.

Beautiful

#notadesigner • Please don't © me

Want to support my work? Please consider sponsoring me on Spred