Bluesky

Aug. 20th, 2023 09:51 am
bryant: (Panda)

I’m pretty much hanging out over there now. Find me here if you have an account. I have spare invites from time to time so if I know you feel free to ask.

It’s imperfect. In particular the company leans more rationalist than I’d like, although I think a bunch of trans people and sex workers successfully nudged them towards the left in the first six months after it launched — thanks, y’all! However, it benefits from the scarcity effect; people are less likely to be total throwaway dicks if it’s harder to get a new account. See also Metafilter.

The feed technology is spectacularly good. Basically anyone with the necessary expertise can make a feed, which anyone can subscribe to. To make a feed, you apply an algorithm to the firehose of Bluesky posts; it can be as simple as “include any posts that have the word ‘filmsky’ or the words ‘film festival’ in them” or “include all posts from these users,” or as complex as “include all posts with the word ‘Seattle’ in them as long as they have more than 2 likes but fewer than 10 likes, and don’t include anything from these people.” So sort of a more powerful hybrid of Twitter lists and Twitter hashtags.

Communities form around feeds. You can get a good look at what there is at Goodfeeds, which is a third party tool. And for the less technical, there are a couple of third party tools which allow you to create your own simple feed.

Algorithms get a lot of bad press, for good reasons. But Bluesky feeds put the algorithms in the hands of users. If I want to write a custom feed that mutes popular memes because oh god I don’t want to see everyone’s ten albums that define them, I can do that. This is pretty cool.

So I like Bluesky. Also, Mastodon is losing its appeal for me. I’m not totally comfortable with the way my Mastodon instance is run; Sage is a good guy but I don’t like the benevolent dictator model for any social media node with more than a couple of dozen people on it. I was going to run my own instance, but I don’t want to deal with the complexity of stock Mastodon and the interesting alternatives are eternally six months away. Plus running a solo instance makes discovery even worse, and I live for discovery when I’m using social media.

And man, Mastodon has a cranky vibe. I think this is a federation problem, and I think Bluesky will have to deal with the same problem when they open up federation. (Which they will have to do.) It’s possible that feeds will generate their own cross-instance culture, and I kind of hope they do, but I’m unwilling to predict that one way or the other.

And finally Mastodon is not great for discovering stuff and I use social media primarily as an information firehose. It’s a personal need but it’s an important one for me.

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

I launched a new online campaign this week and with the consent of the players, I recorded the session for later reference. (One of them wrote a great summary, but it’s still nice to have the recording.) My original plan was to use Whisper to get a transcription but it turns out the built in Google Meet captioning system is plenty good enough. I did give Whisper a shot anyhow, and Whisper’s quality was higher, but the thing about Google Meet is that it adds speaker information to the transcriptions which is a huge difference.

Google One will cost you ten bucks a month, which gets you Google Meet sessions longer than an hour and transcripts, among other benefits. Worth it to me since I can afford it and I don’t like using my work Zoom for personal stuff, but YMMV.

Example Meet Transcription

So what you get out of the box is a VTT subtitle file that looks like this:

00:48:24.000 --> 00:48:28.000
(Bryant)
And you are there with Representative Ledger who speaks for 

00:48:28.000 --> 00:48:32.000
(Bryant)
the hogs. He is, in fact, the voice of the hogs TM 

00:48:32.000 --> 00:48:36.000
(Bryant)
registered trademark, etc, etc. Um he is like 

00:48:36.000 --> 00:48:40.000
(Bryant)
a super skinny guy with like elaborate 

00:48:40.000 --> 00:48:44.000
(Bryant)
wire thing on his head and like some antenna sticking out of it. 

00:48:44.000 --> 00:48:48.000
(Bryant)
Um and like every now and then there's like little sparks coming off of it. 

00:48:48.000 --> 00:48:52.000
(Bryant)
Like you have to keep them from setting things on fire because he's 

00:48:52.000 --> 00:48:56.000
(M.)
You say have to. 
-
 
(Bryant)
too important to do that himself. Um and 

00:48:56.000 --> 00:49:00.000
(Bryant)
um you know it depends on whether or not you want things near you to be on 

00:49:00.000 --> 00:49:04.000
(Bryant)
fire, I'm not forcing you to you know, maybe there's some 

00:49:04.000 --> 00:49:08.000
(Bryant)
things that would be better off if they were on fire.

Which is cool, but not as readable as I want it to be, so I wrote a python script to turn that into this:

Bryant: And you are there with Representative Ledger who
    speaks for the hogs. He is, in fact, the voice of the hogs TM 
    registered trademark, etc, etc. Um he is like a super skinny 
    guy with like elaborate wire thing on his head and like some 
    antenna sticking out of it. Um and like every now and then 
    there's like little sparks coming off of it. Like you have to 
    keep them from setting things on fire because he's --

M.: You say have to.

Bryant: -- too important to do that himself. Um and um you
    know it depends on whether or not you want things near you to be
    on fire, I'm not forcing you to you know, maybe there's some
    things that would be better off if they were on fire.

Mostly formatting but also I did some processing to add dashes in appropriate places. I could probably screen out the ums and uhhs but that starts to get fancy and this is all good enough to read.

You can grab the script here if you like. No warranty available.

Make It Whisper

For comparison, the Whisper transcript looks like this:

00:48:24.600 --> 00:48:27.520
And you are there with Representative Ledger

00:48:27.520 --> 00:48:29.240
who speaks for the hogs.

00:48:29.240 --> 00:48:31.520
He is in fact the voice of the hogs,

00:48:31.520 --> 00:48:34.360
TM, registered trademark, et cetera, et cetera.

00:48:34.360 --> 00:48:40.280
He is like a super skinny guy with like a elaborate

00:48:40.280 --> 00:48:43.320
wire thing on his head and like some antenna sticking out

00:48:43.320 --> 00:48:49.120
it. And like every now and then there's like little sparks coming off of it. Like you

00:48:49.120 --> 00:48:53.160
have to keep them from setting things on fire because he's too important to do that

00:48:53.160 --> 00:48:54.160
himself.

00:48:54.160 --> 00:49:02.160
You have to. You know, it depends on whether or not you want things near you to be on fire.

00:49:02.160 --> 00:49:05.400
I'm not forcing you to. You know, maybe there's some things that will be better off if they

00:49:05.400 --> 00:49:11.520
were on fire.

That’s actually a noticeably better transcript but it does not have the voices identified, and in fact it doesn’t even notice that me and M. have different voices. The script will not work on this file. I guess if I was feeling really spicy I could try and use the timestamps to interpolate the better text from that file into the original Google Meet transcript but this is starting to sound like work.

There’s a pull request to add word-level timestamps to Whisper output, so if that goes through I think I could merge the two transcripts effectively. Work for another day, then.

How To

If you’ve come this far, the least I can give you is a walkthrough. Google Meet drops transcripts and recordings into a Google Drive folder called Meet Recordings. Go in there, find your recording, and select it. Then click on the little three dot menu and select Manage caption tracks.

You’ll get a new window; find your caption track to the right (probably English – 1 unless you recorded it in another language), three dot menu, Download. Easy as pie.

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

Nah, I don’t do these on a schedule or anything.

James Fallows writes about word processors … in 1982. Paywall, sorry. Really good reminder of what computing used to be like. The Sol-20 he was using was a pretty important machine, historically speaking.

Do you interact with other human beings on a regular basis in any way? Read this piece. It’s aimed at engineers but it’s good general advice, which I can summarize as “learn to write well.” You know how you can always find the rough spot on a floor by walking on it barefoot? People notice bad writing, spelling, and grammar even if they don’t know they notice it.

Disney ran a booth at New York Comic Con to advertise the new Guardians of the Galaxy rollercoaster, and the whole thing was an interactive roleplaying experience with a lot of levels. Disney Imagineers have been exploring this area for a while; the high end version is the Galactic Starcruiser, for example. A lesser-known version was the Legends of Frontierland experiment. It’s interesting watching them try new things.

I am currently watching the classic movie serial Les Vampires on the Criterion Channel, and I’m looking forward to rewatching Irma Vep when I’m done, and then I’m looking forward to watching the new TV show also named Irma Vep. Olivier Assayas did a great interview with the LA Times about the interplay between them all. It’s meta, and I do love me some meta.

If you like fanfic and creepypasta and meta-discourse about the nature of fandoms, you might enjoy Northern Caves.

If you like housing developments, you might enjoy reading about Corviale. One kilometer long! It was completed about a decade late for my Delta Green Years of Lead game.

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

Previously…

The song coverage is more varied than I’d thought. For example, Warren Zevon’s Excitable Boy has all three levels of coverage. “Johnny Strikes Up The Band” has line-by-line lyric tracking, “Roland the Headless Thompson Gunner” has no lyric tracking, and “Werewolves of London” has syllable-by-syllable lyric tracking.

“Werewolves of London” is in the Sing: Classic Rock playlist, for what it’s worth.

It seems more and more like the process that generates a Sing-compatible track is either manual, automatic but time-consuming, or costly in terms of licensing. Otherwise surely you’d want every album with a playlist song on it to be fully enabled, to give explorers like me the sense that there’s a ton of coverage?

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

I am not a karaoke aficionado, for the record; I just like singing loudly to the music of my childhood.

So I updated my Apple devices today, as one does, and with the updates came Apple Music Sing. It’s pretty cool; like it says on the tin, for songs it works with, you can turn the vocals way down and the lyric display shows you where you are in the song — down to the syllable — and you can sing along. Nice.

You do not get any cool mixing, which is a shame. You kind of want to be holding a microphone and you want your vocals to get mixed into the backing music and maybe add a little autotune? I don’t know how that stuff works but I know I’m always flat. But it’s still fun. I just lost half an hour to it.

The question on my mind, of course, was “what songs work with this?” For starters, Apple has a bunch of playlists:

They’re what you’d expect. Popular fare, nothing too weird, certainly enough to make me pretty happy.

What about your own library? Not the streaming stuff, the actual library that only weirdos who used to buy CDs have? Welp, nope, this is an Apple Music feature so it doesn’t work with your antique library, even if you have Apple Match and you’ve synced with the cloud and all. I checked a couple of Boston songs which worked when streaming but not when looking at my library. Fair enough.

What about more obscure streaming stuff? It apparently depends, but here’s something interesting: there are two classes of song that support Apple Music Sing. More popular stuff supports the syllable-by-syllable tracking of the song lyrics:

But, say, “Who Knows Where The Time Goes?” from Fairport Convention’s Unhalfbricking only goes line by line.

And coverage is spotty: Richard and Linda Thompson’s Shoot Out The Lights doesn’t support Sing, but their I Want To See The Bright Lights Tonight does. The former is licensed through Rhino and thus Warner, while the latter is by way of Universal — maybe that’s the difference? No, because Hokey Pokey is on Universal and doesn’t support Sing. So who knows?

There’s no way to tell whether or not a song supports Sing without popping it open, and you can’t tell whether it’s syllable by syllable or line by line without playing it.

All in all I like it.

[Crossposted from Population: One; go here for the original post.]

bryant: (Library Science)

New text AI! Let’s try it on some tabletop RPG work. Bold is my prompts; I’ve snipped the polite banter out of most of the AI’s answers.

Spoiler: this is way better than the last one I tried. If I repeat the same prompt it gets a little repetitive, but still not bad.

Read the rest of this entry »

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

Phew. No big Paxlovid bounce, thankfully.

This is what I thought of when I heard about Tesla engineers coming over to validate Twitter code. It’s both true that the author seems pretty savvy and that the culture over at Tesla is focused on velocity over anything. Good times.

Let’s get all the Twitter stuff out of the way!

  • Evelyn Douek has smart things to say about Twitter’s regulatory challenges. Not just in the US, not just in the EU — India’s going to be a huge headache.
  • This layoff guide for Twitter employees is worth reading for anyone who’s nervous about their job. Or anyone, really. Use your work laptop in a way which will enable you to execute on those precautions quickly.
  • One billion dollars in infrastructure cuts? This is already working out badly. Sympathies to the guy who just went on call for a bunch of systems he doesn’t know. Gergley has a good thread on the problems ahead. Here’s another SRE still employed by Twitter, and he thinks it’s gonna be ugly. Rakyll is a well-respected principal engineer in the reliability biz; she’s pessimistic and thinks people are leaving.
  • Tangentially related: Starlink is inevitably having to throttle bandwidth. Some math: Starlink wants $5K/month for 2 terminals with a total of 350 Mbps download. That’s cheap and cool but the existing mobile solutions can deliver bandwidth in the Gbps range.

OK, that’s enough horrified observation of the train wreck. Mastodon is treating me OK so far.

If I had to choose one word to capture the difference between engineering levels, I agree that impact is a good one. But there are a lot of different ways to have an impact. I kind of want to do career progression as a spider chart.

I like this story about enclaves and exclaves but what really caught my eye is the platform — this is apparently open to anyone to write this kind of post? In my copious spare time I wanna mess with it.

This program looks like a good entrance point to New Taiwan Cinema. I’ve seen Rebels of the Neon God and I liked it, although I’m not sure I have the right flavor of patience for this particular cinematic movement.

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

Still got covid but thanks to Paxlovid or my natural recuperative energies or something, I’m feeling much better than I did on Tuesday. Let’s see how this goes.

The report described severe staffing challenges that included large numbers of unfilled positions on its Site Integrity team, one of three business units responsible for policing misinformation. It also highlighted a lack of language capabilities so severe that many content moderators resorted to Google Translate to fill the gaps. In one of the most startling parts of the report, a head count chart said Site Integrity had just two full-time people working on misinformation in 2021, and four working full-time to counter foreign influence operations from operatives based in places like Iran, Russia and China.

Elizabeth Dwoskin, Joseph Menn, and Cat Zakrzewski: Washington Post

Elon Musk just laid off half of Twitter to save money, and he will need those lawyers and engineers if he wants to handle the regulatory challenges ahead of him. Maybe he didn’t lay off the really critical employees, like the people supporting the infrastructure. After all:

“Even a temporary but overlapping outage of a small number of datacenters would likely result in the service [Twitter] going offline for weeks, months, or permanently.”

Mudge

Well.

Elon Musk has directed Twitter Inc’s teams to find up to $1 billion in annual infrastructure cost savings, according to two sources familiar with the matter and an internal Slack message reviewed by Reuters, raising concerns that Twitter could go down during high-traffic events like the U.S. midterm elections.

The company is aiming to find between $1.5 million and $3 million a day in savings from servers and cloud services, said the Slack message, which referred to the project as “Deep Cuts Plan.”

Sheila Dang, Paresh Dave and Katie Paul: Reuters

I’m feeling like I’m recovering, but boy do I ever feel for Twitter.

It’s too late for Twitter employees who were laid off, but I strongly recommend this guide. It’s valuable any time you’re thinking you might leave a company, under your own power or not.

[Crossposted from Population: One; go here for the original post.]

bryant: (Panda)

It’s reasonably well known that one path to a hospitable online community is charging people a fee to register. See Metafilter for the best case. They charge $5 to register and it cuts way down on drive-by assholes. The less good case is Something Awful, which charges $10 to register and is often a pit. But that’s because they don’t moderate all that hard.

(Something Awful is also the only pro wrestling discussion forum I know of where you’ll get raked through the coals for saying things like “Unfortunately abadon as an attractive woman would have a lot more success if her gimmick didn’t involve making herself extremely unattractive”. It’s a pit of contradictions. Anyhow.)

So what is Elon trying to do with this “$20/month for verified users” thing?

First off, if you assume that all 300,000 verified users will go for it, that’s enough money to make it worthwhile. $72 million a year is only 7% of the new debt load Twitter has to service, but that’s not nothing! If you told me I could knock off 7% of my debt by crunching for a week, I’d do it in a split second.

But this isn’t just for verified users. He wants to open it up to everyone.

Jason is making a couple of big mistakes here but he is close to correct. If Twitter charged $20 as a one time fee for using the platform, and then moderated, it would clean up pretty quickly. It would also get very small. This does not fulfill Elon’s dream, alas.

That’s the first mistake. The second mistake is making it a subscription; if you have to pay $20 a month whether you misbehave or not, it’s not a psychological sunk cost and you’re more willing to break the rules and get kicked off. On Something Awful, you can immediately re-register if you get banned, so banning people is an income stream. Again, this doesn’t work if the cost is a subscription.

Can’t wait to see what they come up with next.

[Crossposted from Population: One; go here for the original post.]

bryant: (Library Science)
awk '/in_reply_to_screen_name/ { print $3 }' tweets.json | sed 's/[","]//g' | sort | uniq -c | sort | grep -v BryantD | tail -20
  56 graphxgrrl
  57 patrickoduffy
  58 jessnevins
  59 othergretchen
  60 GlobeChadFinn
  61 ryantomorrow
  65 smakofsky
  66 seclectech
  69 multiplexer
  73 gentlyepigrams
  75 rmd1023
  76 mgrasso
  79 JimHenleyMusic
  79 Wolf_six
  89 carlrigney
 102 _r_o_n_e_
 107 rone_____1
 129 emilytheslayer
 306 rdonoghue
 341 ce_murphy

[Crossposted from Population: One; go here for the original post.]

bryant: (Maggie)

I made a thing! I have been on a minor roll with python recently and this seemed like a fun project so I started working on it. Towards the end I reached out to the awesome person who inspired me, since she didn’t seem to have been keeping her tracker up to date, and she said I should go ahead and launch mine. So here we are.

I used this as an excuse to try out new technology and libraries. Click and Cloup made the list; the first because I wanted to try out new argument parsers and the second because I needed option groups. This forced me to learn to use setuptools better, which was a win. I am gonna keep using this tech going forward.

I also wound up sticking Rich in there for better CLI output and it’s kinda great, so that’ll stay in my toolbox too.

Dataset turned out to be too limited, because it’s really just for columnar data in a single table and it turns out itch.io jams have one little thing which break that paradigm; namely, multiple owners per jam. So now I’m using sqlite with JSON support and honestly it’s a bit grungy. Maybe next time I’ll learn SQLAlchemy for real.

My python has gotten significantly better over the last year with this kind of small but enjoyable work, and I am gonna keep doing it.

[Crossposted from Population: One; go here for the original post.]

bryant: (Default)

Before the pandemic, I’d been thinking about writing a little aggregator to pull movie times at my favorite local indie theaters into a calendar. I’m bad at remembering to see that cool showing a month from now but if I had a calendar that would theoretically help.

Obviously I didn’t need it for the last couple of years but the silver lining is that I got better at Python. I spent some time coding over the last week of my sabbatical and voila: the Seattle Arthouse Movie Calendar.

The code is here. I was sort of fiddling around with making it a real library but decided not to chew off too much at once. It was enough fun learning how to use classes to make it super-easy to add a new theater.

Lots of potential improvements. I am probably going to generate separate calendars for each theater next, for convenience. I’d also like to render maybe three days worth of calendar on the Web page. I feel like doing more than that risks pulling traffic away from the official theater sites, which I’d prefer to avoid, but three days seems reasonable.

I’m going to write up some notes on using the code for other cities too. If you’re decent with Python you could probably figure it out from reading what’s there but documentation is a good practice anyhow.

[Crossposted from Population: One; go here for the original post.]

bryant: (Library Science)

Got a wild hair, updated my ten-years-fallow technical operations blog with a reading list I wrote up for my last job. This inexorably led to changing themes and doing some maintenance. This sort of industriousness will never last.

[Crossposted from Population: One; go here for the original post.]

bryant: (Library Science)

This week I needed to do some analysis of JIRA tickets that goes beyond the reporting JIRA provides — not entirely an uncommon task. My usual quickie toolkit for that purpose involves Jupyter notebooks, which I prefer over downloading CSVs and playing with spreadsheets because I can automate the notebooks given a JIRA API key.

In this case, though, I really want one of my PMs to be able to run these reports, and I don’t want to get into the whole “OK then type this at the command line” thing. The post title kind of gives this away, but after some thought I realized, hey, just check the notebook into the company’s GitHub and there we go.

But how about that API key? Obviously I don’t want to embed mine in the notebook. Is there some way to use GitHub secrets for this? Answer: yes, there is, and it’s really simple, but I don’t see it documented step by step anywhere else so I’m gonna do that here.

If you want the quick answer: GitHub makes secrets available as environment variables, and if you’re working in the GitHub Jupyter environment, you don’t need to do anything special with workflows to make that happen. Therefore, you can just use Python’s os.environ mapping object to get at secrets.

Read the rest of this entry »

[Crossposted from Population: One; go here for the original post.]

bryant: (I <3 Cube)

FoundryVTT is a high quality virtual online tabletop platform. Unlike Roll20, however, there’s not a central server — once you buy a license, you have to run it someplace. There are a few services that will do this for you at a reasonable price, but I’m a geek, so if I start using FoundryVTT I want to host it myself.

Fly.io is a very cool new application hosting cloud. I experimented with it a month ago for hosting an NJPWWorld RSS feed generator and it was awesomely simple. They support persistent disk, so I couldn’t see any reason why it wouldn’t work for FoundryVTT. And it did! Details after the cut.

Read the rest of this entry »

[Crossposted from Population: One; go here for the original post.]

bryant: (Maggie)

My old boss Steve Makosfky was singing the praises of Scriptable the other day — it’s an iOS app that allows you to run Javascript in a number of ways, including as a widget. It’s also free (but tip the developer if you like and use it). So I modified an existing script that displays random photos from Flickr to replace my clunky Unsplash photo widget.

It’s way better than my previous solution. It doesn’t clog up my photo library, plus it’s mildly configurable. (Could be more so, but I’ll leave that as an exercise to the reader.) I’ve set mine up such that it only pulls down nature photos, which means I’m no longer seeing people I don’t know on my iOS home screen.

I stuck my code in a gist in case anyone finds this useful.

[Crossposted from Population: One; go here for the original post.]

bryant: (Maggie)

So far this month I’ve received a couple hundred email messages from Instagram notifying me that their Terms of Use have been updated. They’re legitimate emails; it looks like someone signed up hundreds of Instagram accounts using randomized innocence.com email addresses. Since I moved my mail to Fastmail, I’m now seeing them all. I poked around a couple of the accounts (hi, kurt.clemons78446!) and the ones I spot checked have all been deleted.

I imagine someone used innocence.com as a domain for non-existent email addresses, and these either date back to before Instagram added a confirmation step (and were later removed for spamming) or they just never confirmed the accounts. You’d think that deleted accounts would be removed from whatever list of email addresses Facebook is using to generate these emails. Unconfirmed accounts… I guess those should still see the Terms of Use changes.

Good times. Finally added a filtering rule for the emails, anyhow.

[Crossposted from Population: One; go here for the original post.]

bryant: (Maggie)

I don’t like wearing headphones all day and since I’m lucky enough to have a spare room for an office, I can play music through my Bluetooth speaker. However, I’m lazy, and I don’t want to fiddle around with my music player just cause I’m starting a Zoom meeting. Thus, automation.

Zoom provides callbacks when meetings start, but that’s aimed at people writing plugin modules. OK, we can go a bit lower level. I can’t just watch for a process, cause Zoom is always running on my laptop. But I can watch for open UDP sockets!

$ lsof -i 4UDP | grep zoom
zoom.us 88028 bryantd 83u IPv4 0xa90f1ce03eca5d5 0t0 UDP xx.xx.xx.xx:57275
zoom.us 88028 bryantd 84u IPv4 0xa90f1ce03eca2ed 0t0 UDP xx.xx.xx.xx:52612
zoom.us 88028 bryantd 90u IPv4 0xa90f1ce09340175 0t0 UDP *:65048
zoom.us 88028 bryantd 91u IPv4 0xa90f1ce0939ce8d 0t0 UDP *:60088
zoom.us 88028 bryantd 95u IPv4 0xa90f1ce0939c8bd 0t0 UDP *:50594

That’ll do, pig. That’ll do.

#!/bin/bash

trap ctrl_c INT

function ctrl_c() {
        stop_music
        exit
}

function stop_music() {
        /usr/bin/osascript -e 'tell application "Music" to pause'
}

while true; do
        zoom_status=$( /usr/sbin/lsof -i UDP | /usr/bin/grep zoom | wc -l )

        if (( $zoom_status > 0 )); then
                stop_music
        fi

        sleep 30
done

I’m catching interrupts because I want a quick keyboard method to stop the music just in case. Right now I don’t think I want it to restart music when I leave a Zoom call, but easy enough to add that if I do.

[Crossposted from Population: One; go here for the original post.]

October 2025

S M T W T F S
    1234
567891011
12131415161718
19202122232425
2627 28293031 

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Dec. 25th, 2025 01:36 pm
Powered by Dreamwidth Studios