Moving from Medium
31st Jan '23 • 3 of your Earth minutesI 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