Replace github links with git.voussoir.net.

[minor]
master
voussoir 2023-09-18 21:01:09 -07:00
parent 22bd73f818
commit 3475cb0abf
17 changed files with 35 additions and 35 deletions

View File

@ -798,7 +798,7 @@ Youtube, the system:
- [YouTubers have to declare ads. Why doesn't anyone else?](https://www.youtube.com/watch?v=L-x8DYTOv7w) by Tom Scott.
I'd like to take this opportunity to ~~advertise~~ *tell you about* my own program, [YCDL](https://github.com/voussoir/ycdl), which presents me with a custom interface for subscribing to, watching, and downloading videos. Imagine watching youtube without a "recommendation" feed, autoplay, unrelated spam, or algorithmic determination of what does or doesn't show in the sub box. As long as you can tolerate the slower channel refresh cycle.
I'd like to take this opportunity to ~~advertise~~ *tell you about* my own program, [YCDL](https://git.voussoir.net/voussoir/ycdl), which presents me with a custom interface for subscribing to, watching, and downloading videos. Imagine watching youtube without a "recommendation" feed, autoplay, unrelated spam, or algorithmic determination of what does or doesn't show in the sub box. As long as you can tolerate the slower channel refresh cycle.
I'd also like to ~~advertise~~ spread the word about [SponsorBlock](https://sponsor.ajay.app/), a browser extension which automatically skips in-video sponsorship segments based on crowdsourced reports. The database can be freely downloaded so you could hypothetically implement a system for sponsorblocking locally saved videos. This would be great, since I was just about ready to starting excising them with ffmpeg.
@ -920,7 +920,7 @@ I think about this quote very often because it applies to every kind of publishe
Here are my promises to you, reader.
If I come up with a program or website that I think is a worthy venture to make money from, I'll do so via one-time purchases, well-justified subscriptions, or [donations](/donate), not advertisements. And as you know I publish plenty of what I make [for free](https://github.com/voussoir).
If I come up with a program or website that I think is a worthy venture to make money from, I'll do so via one-time purchases, well-justified subscriptions, or [donations](/donate), not advertisements. And as you know I publish plenty of what I make [for free](https://git.voussoir.net/voussoir).
The website you are reading now will always be free of third-party ads. If your adblocker ever reports more than 0 hits, it's probably due to URL patterns or CSS names, so please let me know and I will fix it. Obviously this is a website about myself so in a sense I'm generally advertising myself, but it's your choice to come here and read about me.

View File

@ -114,7 +114,7 @@ Forgive me if I don't provide too many other case studies of bad examples. I don
[Bill Hammack aka The Engineer Guy](https://youtube.com/channel/UC2bkHVIDjXS7sgrgjFtzOXQ/videos) is a clear communicator who shows the fascinating internals and design of things we take for granted, often with a poetic bent. It's hard to pick a single best video, so check out [Nitinol](https://www.youtube.com/watch?v=wI-qAxKJoSU), [Plastic injection molding](https://www.youtube.com/watch?v=RMjtmsr3CqA) and [Aluminum cans](https://www.youtube.com/watch?v=hUhisi2FBuw).
[Ian LaSpina aka Knyght Errant](https://youtube.com/channel/UC1T4KJG1L_kTrP9RcdU5Csw/videos) participates in Medieval Living History events and makes videos about medieval culture, arms, and armor. Although I never considered myself to be very interested in history, I was absolutely absorbed by his [Palazzo Ducale Bascinet video](https://www.youtube.com/watch?v=mSjmxMfEtvE) and quickly [binged](https://github.com/voussoir/ycdl#easily-watch-every-video-on-the-channel) his whole channel.
[Ian LaSpina aka Knyght Errant](https://youtube.com/channel/UC1T4KJG1L_kTrP9RcdU5Csw/videos) participates in Medieval Living History events and makes videos about medieval culture, arms, and armor. Although I never considered myself to be very interested in history, I was absolutely absorbed by his [Palazzo Ducale Bascinet video](https://www.youtube.com/watch?v=mSjmxMfEtvE) and quickly [binged](https://git.voussoir.net/voussoir/ycdl#easily-watch-every-video-on-the-channel) his whole channel.
## Escape the bubble

View File

@ -224,7 +224,7 @@ Note: this article will make more sense if you are using a mouse or other pointi
Warning: things can get a little spooky in the dark!
You can read the source code for this document [here](https://github.com/voussoir/voussoir.net/raw/master/voussoir.net/writing/browser_in_the_dark/browser_in_the_dark.md) (or by pressing Ctrl+U in chrome / firefox).
You can read the source code for this document [here](https://git.voussoir.net/voussoir/voussoir.net/raw/branch/master/voussoir.net/writing/browser_in_the_dark/browser_in_the_dark.md) (or by pressing Ctrl+U in chrome / firefox).
## mix-blend-mode

View File

@ -17,7 +17,7 @@ Let's start here: the podcast culture has traditionally used RSS for publishing
At this point, you can either search the Internet for "RSS podcast downloader" and find a pre-existing software solution to this problem, or you can continue reading and see how I do it. As you'll find, the nature of RSS does often require getting dirty and putting in some extra labor.
With no background experience in RSS readers, I tried [QuiteRSS](https://quiterss.org/) and have been happy with it. QuiteRSS uses SQLite to store the feeds and news items. With a tool like [sqlitebrowser](https://github.com/sqlitebrowser/sqlitebrowser/releases), or just the [command line sqlite3](https://www.sqlite.org/download.html), it's then trivial to get the `published` date and `enclosure_url` fields which I then throw into my own [`threaded_dl.py`](https://github.com/voussoir/cmd/blob/master/threaded_dl.py) by preparing a file that looks like:
With no background experience in RSS readers, I tried [QuiteRSS](https://quiterss.org/) and have been happy with it. QuiteRSS uses SQLite to store the feeds and news items. With a tool like [sqlitebrowser](https://github.com/sqlitebrowser/sqlitebrowser/releases), or just the [command line sqlite3](https://www.sqlite.org/download.html), it's then trivial to get the `published` date and `enclosure_url` fields which I then throw into my own [`threaded_dl.py`](https://git.voussoir.net/voussoir/cmd/src/branch/master/threaded_dl.py) by preparing a file that looks like:
```
https://example.com/episode1.mp3 Podcastname 2015-10-31.mp3
@ -40,7 +40,7 @@ To be clear, I only do this for the initial download of the historical posts. Af
However, you'll find that many or most RSS feeds don't contain the entire history of the show. Squarespace, for example, only publishes the 300 most recent items in an RSS feed. Why? Because that's the number [Apple uses](https://support.squarespace.com/hc/en-us/articles/205814338-Podcasting-with-Squarespace-overview#:~:text=Squarespace%20podcast%20feeds%20display%20up,episodes%20that%20Apple%20Podcasts%20supports.) and apparently it's easier to follow Apple's lead than to realize that RSS is an open format for a variety of consumers. I have seen other feeds that show only 10 items, which surely must be a result of poor configuration [footnote_link].
One thing you can try is searching the [Wayback machine](https://web.archive.org/) for the RSS url. If you're lucky, it will have been scraped in the past often enough so as to contain the entire history, though perhaps in piecemeal. You could download the snapshots, combine them into a single xml file, serve it out of a [webserver](https://github.com/voussoir/else/tree/master/SimpleServer), and temporarily point your RSS client at localhost so it ingests all those historical posts before pointing it back at the real url. Convoluted, perhaps, but I'd rather do this than inject the posts into the db myself. Then, you can pull the enclosures out of QuiteRSS's database (or from the xml file you just made).
One thing you can try is searching the [Wayback machine](https://web.archive.org/) for the RSS url. If you're lucky, it will have been scraped in the past often enough so as to contain the entire history, though perhaps in piecemeal. You could download the snapshots, combine them into a single xml file, serve it out of a [webserver](https://git.voussoir.net/voussoir/else/src/branch/master/SimpleServer), and temporarily point your RSS client at localhost so it ingests all those historical posts before pointing it back at the real url. Convoluted, perhaps, but I'd rather do this than inject the posts into the db myself. Then, you can pull the enclosures out of QuiteRSS's database (or from the xml file you just made).
![](wayback_archives.png "Finding archived RSS feeds on Wayback")

View File

@ -1,7 +1,7 @@
Sending emails to myself
========================
In April, I wrote [operatornotify.py](https://github.com/voussoir/voussoirkit/blob/master/voussoirkit/operatornotify.py), a module which allows my programs to notify me of important information or errors. I was immediately very proud of it. It felt awesome to get so much impact out of a sub-100 line module as I went around hooking it up to my existing programs and getting the tracebacks from my horribly broken code delivered right to me.
In April, I wrote [operatornotify.py](https://git.voussoir.net/voussoir/voussoirkit/src/branch/master/voussoirkit/operatornotify.py), a module which allows my programs to notify me of important information or errors. I was immediately very proud of it. It felt awesome to get so much impact out of a sub-100 line module as I went around hooking it up to my existing programs and getting the tracebacks from my horribly broken code delivered right to me.
![](ycdl_errors.png "there's more")

View File

@ -88,9 +88,9 @@ When you first start programming, it will be difficult to write a program from s
Well, the great thing about Javascript is that every website you visit already has a program that you can modify and play with! Just right click and choose "Inspect Element", or press F12, and check out the elements, console, network, and more.
Try making [bookmarklets](https://en.wikipedia.org/wiki/Bookmarklet), which are pieces of Javascript code that you can save on your browser's bookmark bar, and then you just click them to activate it. The code goes in the place where you'd normally put the URL. For example, [this bookmarklet](https://github.com/voussoir/else/blob/master/Javascript/loopplayall.js) automatically plays any video element on the page, which is great for forums with a lot of webms, and [this one](https://github.com/voussoir/else/blob/master/Javascript/tab_renamer.js) can rename the current tab, which is great when several tabs have the same name.
Try making [bookmarklets](https://en.wikipedia.org/wiki/Bookmarklet), which are pieces of Javascript code that you can save on your browser's bookmark bar, and then you just click them to activate it. The code goes in the place where you'd normally put the URL. For example, [this bookmarklet](https://git.voussoir.net/voussoir/else/src/branch/master/Javascript/loopplayall.js) automatically plays any video element on the page, which is great for forums with a lot of webms, and [this one](https://git.voussoir.net/voussoir/else/src/branch/master/Javascript/tab_renamer.js) can rename the current tab, which is great when several tabs have the same name.
Also try making .html files on your computer and opening them in your browser. It's like creating a website without actually having to create a website. For example, [this page I wrote](https://github.com/voussoir/else/blob/master/Javascript/reddit_live_new.html) shows new posts on a subreddit when they're made. You'll be amazed how simple it is to create something that could be described as a "web app".
Also try making .html files on your computer and opening them in your browser. It's like creating a website without actually having to create a website. For example, [this page I wrote](https://git.voussoir.net/voussoir/else/src/branch/master/Javascript/reddit_live_new.html) shows new posts on a subreddit when they're made. You'll be amazed how simple it is to create something that could be described as a "web app".
A word of warning, though. The Javascript community has created dozens and dozens of frameworks and libraries and packages that they say you should use. Don't. For the time being, don't download anything, don't import anything, don't `require` anything. No jQuery, no React, no nothing. The default javascript tools in your browser are enough to do what you want to do. The low barrier of entry to working with javascript is a great boon, but it also means the internet is overflowing with stuff people have made and shared, and it will be too confusing to navigate it all right now. Just have fun writing what achieves your goal. Stick to the standard stuff, and search [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript) for how to use it. For example, here's the page on [element.innerText](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText).
@ -100,7 +100,7 @@ A word of warning, though. The Javascript community has created dozens and dozen
The standard library (modules and tools included with Python when you download it) are very capable so, like Javascript, you shouldn't have to worry about downloading other people's stuff to get started with the very basics.
I use Python for just about every facet of file management on my computer, like [renaming all files in a folder](https://github.com/voussoir/cmd/blob/master/brename.py), [sending files to the recycle bin](https://github.com/voussoir/cmd/blob/master/recycle.py), [clearing out empty folders](https://github.com/voussoir/cmd/blob/master/prune_dirs.py), [counting file extensions](https://github.com/voussoir/cmd/blob/master/extension_summary.py), and putting together [ffmpeg commands](https://github.com/voussoir/cmd/blob/master/ffstreams.py) that I wouldn't want to write by hand. I also [crop](https://github.com/voussoir/cmd/blob/master/crop.py), [resize](https://github.com/voussoir/cmd/blob/master/resize.py), and convert images to [grayscale](https://github.com/voussoir/cmd/blob/master/grayscale.py) or the [Windows .ico format](https://github.com/voussoir/cmd/blob/master/icoconvert.py). I love writing in Python and I hope you will too.
I use Python for just about every facet of file management on my computer, like [renaming all files in a folder](https://git.voussoir.net/voussoir/cmd/src/branch/master/brename.py), [sending files to the recycle bin](https://git.voussoir.net/voussoir/cmd/src/branch/master/recycle.py), [clearing out empty folders](https://git.voussoir.net/voussoir/cmd/src/branch/master/prune_dirs.py), [counting file extensions](https://git.voussoir.net/voussoir/cmd/src/branch/master/extension_summary.py), and putting together [ffmpeg commands](https://git.voussoir.net/voussoir/cmd/src/branch/master/ffstreams.py) that I wouldn't want to write by hand. I also [crop](https://git.voussoir.net/voussoir/cmd/src/branch/master/crop.py), [resize](https://git.voussoir.net/voussoir/cmd/src/branch/master/resize.py), and convert images to [grayscale](https://git.voussoir.net/voussoir/cmd/src/branch/master/grayscale.py) or the [Windows .ico format](https://git.voussoir.net/voussoir/cmd/src/branch/master/icoconvert.py). I love writing in Python and I hope you will too.
Once again, you can start by downloading somebody else's program and making small tweaks to it to see what happens. I find this more approachable than starting from a blank screen.
@ -108,9 +108,9 @@ Although text editors and IDEs may let you run Python code within the editor, yo
### AutoHotkey
[AutoHotkey](https://autohotkey.com) is in a different category than most programming languages, but I think it's excellent for programming learners to play around with because it's quite exciting to watch your mouse cursor fly around and do things by itself. For example, I once made [an AHK script](https://github.com/voussoir/ahk/blob/master/clickerheroes.ahk) that would automatically click on the bonus items in Clicker Heroes, and a similar one for emptying my entire backpack into a chest in Minecraft.
[AutoHotkey](https://autohotkey.com) is in a different category than most programming languages, but I think it's excellent for programming learners to play around with because it's quite exciting to watch your mouse cursor fly around and do things by itself. For example, I once made [an AHK script](https://git.voussoir.net/voussoir/ahk/src/branch/master/clickerheroes.ahk) that would automatically click on the bonus items in Clicker Heroes, and a similar one for emptying my entire backpack into a chest in Minecraft.
The ability to create custom hotkeys really puts the Personal in Personal Computer. I have [custom volume buttons](https://github.com/voussoir/ahk/blob/master/volumecontrol.ahk), custom [English/Korean keyboard swap buttons](https://github.com/voussoir/ahk/blob/master/custom_hangul.ahk), and a button for keeping the current window [always on top](https://github.com/voussoir/ahk/blob/master/alwaysontop.ahk), which makes me feel like a wizard.
The ability to create custom hotkeys really puts the Personal in Personal Computer. I have [custom volume buttons](https://git.voussoir.net/voussoir/ahk/src/branch/master/volumecontrol.ahk), custom [English/Korean keyboard swap buttons](https://git.voussoir.net/voussoir/ahk/src/branch/master/custom_hangul.ahk), and a button for keeping the current window [always on top](https://git.voussoir.net/voussoir/ahk/src/branch/master/alwaysontop.ahk), which makes me feel like a wizard.
Also, AHK scripts tend to be short and sweet, so you don't get stuck on a particular project for too long, which is a good thing for learners. You can learn something, enjoy it, and move on.

View File

@ -64,7 +64,7 @@ I have been using [Anki](https://apps.ankiweb.net/) to keep up on Korean vocabul
Learning Korean requires spending a lot of time listening to it. Certainly the long-term value of learning the language is very high for me, but on a day-to-day basis the value of any individual listening session is hard to measure. I can't *feel* my improvement on such a short timescale, so individual listenings are unfortunately of somewhat low value. Thus it's necessary to reduce my friction as much as possible. That means keeping the tracks on my phone, using [Musicolet](https://play.google.com/store/apps/details?id=in.krosbits.musicolet&hl=en_US) which gives me a queue (not playlist!) dedicated to Korean, and putting the Musicolet widget on my home screen so I've got a play button right there. I mentioned this in [Are children better at learning languages](/writing/are_children_better_at_languages/#actionable_takeaways). My current source of friction is earbuds, I wish I could just have it beamed into my brain with no mortal coils to speak of. Those true-wireless bluetooth earbuds seem promising but then you've got the friction of charging them and my experience with bluetooth sound quality hasn't been great, though that might just be from low-quality devices. It would also be nice if Musicolet had widgets for playing particular queues, because the widget now only shows the current queue and you have to open the app to switch them. The next time I get a new phone I think I'll keep this one as a dedicated Korean device with 24/7 airplane mode to save battery and a play button right on the lock screen.
I wrote [gitcheckup.py](https://github.com/voussoir/cmd/blob/master/gitcheckup.py) to check the number of changed files and unpushed commits in each of my important git repositories. The cwd doesn't matter, it always shows me a complete list. I have a tendency to make small or experimental changes that I forget about and go unpushed for a long time. gitcheckup helps me keep my repositories in a clean state by showing me empty checkboxes (the horror!) next to what needs attention [footnote_link].
I wrote [gitcheckup.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/gitcheckup.py) to check the number of changed files and unpushed commits in each of my important git repositories. The cwd doesn't matter, it always shows me a complete list. I have a tendency to make small or experimental changes that I forget about and go unpushed for a long time. gitcheckup helps me keep my repositories in a clean state by showing me empty checkboxes (the horror!) next to what needs attention [footnote_link].
I have a [digitizer / drawing tablet](https://en.wikipedia.org/wiki/Graphics_tablet) that I like to use as a mouse even when I'm not doing any graphics work. It's fun to use and helps me prevent wrist strain from using a regular mouse too long. But when using the tablet, accurately double-clicking is a little harder, and switching from clicking to typing requires dropping the pen in its holder, so there is a bit more friction to using the tablet as a mouse replacement and I'll often instinctively reach for the regular mouse instead. So, I'll occasionally toss my regular mouse to the other side of my desk as a way of forcing the use of the tablet.

View File

@ -213,7 +213,7 @@ class Article:
repo_path = git_repo_for_file(self.md_file)
relative_path = self.md_file.relative_to(repo_path, simple=True).replace('\\', '/')
github_history = f'https://github.com/voussoir/voussoir.net/commits/master/{relative_path}'
github_history = f'https://git.voussoir.net/voussoir/voussoir.net/commits/master/{relative_path}'
commits = git_file_commit_history(self.md_file)
self.publication_id = f'{commits[-1].hash}/{self.md_file.parent.basename}' if commits else None
@ -231,7 +231,7 @@ class Article:
<p><a href="{{github_history}}">View this document's history</a></p>
<ul>
{% for commit in commits %}
<li><a href="https://github.com/voussoir/voussoir.net/commit/{{commit.hash}}"><time datetime="{{commit.date.isoformat()}}">{{commit.date.strftime('%Y-%m-%d')}}</time> {{commit.title}}</a></li>
<li><a href="https://git.voussoir.net/voussoir/voussoir.net/commit/{{commit.hash}}"><time datetime="{{commit.date.isoformat()}}">{{commit.date.strftime('%Y-%m-%d')}}</time> {{commit.title}}</a></li>
{% endfor %}
</ul>

View File

@ -395,7 +395,7 @@ Whereas by creating a main function, any variables in `main` are local variables
I understand that the examples shown here are very simplistic. Here are some links to my other programs which use ifmain in a purposeful way.
- [bytestring](https://github.com/voussoir/voussoirkit/blob/417c14a02338d10a34fdf4875761c9e4a92aef1c/voussoirkit/bytestring.py#L127) is imported by many of my other programs, but also is very useful on its own.
- [epubfile](https://github.com/voussoir/epubfile/blob/4f44cd642f04f1dc94b1d65c5a52f5f639460276/epubfile.py#L1657) can be imported into any program that wants to modify epubs, but has a number of builtin utilities that run from ifmain.
- [bytestring](https://git.voussoir.net/voussoir/voussoirkit/src/commit/417c14a02338d10a34fdf4875761c9e4a92aef1c/voussoirkit/bytestring.py#L127) is imported by many of my other programs, but also is very useful on its own.
- [epubfile](https://git.voussoir.net/voussoir/epubfile/src/commit/4f44cd642f04f1dc94b1d65c5a52f5f639460276/epubfile.py#L1657) can be imported into any program that wants to modify epubs, but has a number of builtin utilities that run from ifmain.
In a very large project with many files, it's more likely that you'll have a main launcher file, and none of the internals are very useful to run on their own. ifmain essentially bridges the gap to make single Python files useful as imports and standalone utilities.
In a very large project with many files, it's more likely that you'll have a main launcher file, and none of the internals are very useful to run on their own. ifmain essentially bridges the gap to make single Python files useful as imports and standalone utilities.

View File

@ -78,7 +78,7 @@ I always hate when I'm reaching for End or numpad-slash and accidentally hit Num
**A/한**
I gave myself a dedicated English/한글 toggle key so I could stop relying on my [Alt+Menu AHK script](https://github.com/voussoir/ahk/blob/master/custom_hangul.ahk). The AHK solution works perfectly fine, but I would occasionally blunder the Menu keypress and be left navigating the toolbar of the current application thanks to the Alt press. I put it next to Right Shift because why is that key so long in the first place.
I gave myself a dedicated English/한글 toggle key so I could stop relying on my [Alt+Menu AHK script](https://git.voussoir.net/voussoir/ahk/src/branch/master/custom_hangul.ahk). The AHK solution works perfectly fine, but I would occasionally blunder the Menu keypress and be left navigating the toolbar of the current application thanks to the Alt press. I put it next to Right Shift because why is that key so long in the first place.
![](layout_2.png)

View File

@ -27,9 +27,9 @@ For what it's worth, myself and [others](https://news.ycombinator.com/item?id=30
## Fixing dead links
I wrote my own [linkchecker.py](https://github.com/voussoir/voussoir.net/blob/master/linkchecker.py) because for some reason I like writing my own solutions instead of using other people's. It gives me a report organized by HTTP status and domain. If the link has a problem, it tells me what article it's on.
I wrote my own [linkchecker.py](https://git.voussoir.net/voussoir/voussoir.net/src/branch/master/linkchecker.py) because for some reason I like writing my own solutions instead of using other people's. It gives me a report organized by HTTP status and domain. If the link has a problem, it tells me what article it's on.
It immediately found multiple problematic links, [all](https://github.com/voussoir/voussoir.net/commit/2b966be6357c073043dc2b5b64edc448a559a43b) of [which](https://github.com/voussoir/voussoir.net/commit/9c287c719fdf9c07c098a6430fe0fb5dfcbdbadf) were [my fault](https://github.com/voussoir/voussoir.net/commit/8224052c8cc9d79a3acf832f69fcd46a4d045552). My engineering team dropped the ball on this because they were too focused on innovating new ways to put paragraphs into documents, but the linkchecker should reduce these incidents in the future.
It immediately found multiple problematic links, [all](https://git.voussoir.net/voussoir/voussoir.net/commit/2b966be6357c073043dc2b5b64edc448a559a43b) of [which](https://git.voussoir.net/voussoir/voussoir.net/commit/9c287c719fdf9c07c098a6430fe0fb5dfcbdbadf) were [my fault](https://git.voussoir.net/voussoir/voussoir.net/commit/8224052c8cc9d79a3acf832f69fcd46a4d045552). My engineering team dropped the ball on this because they were too focused on innovating new ways to put paragraphs into documents, but the linkchecker should reduce these incidents in the future.
Actually, it's a good thing if all the dead links are my fault, because I can easily fix it. As long as I continue making SingleFile archives, the majority of dead links I encounter should be resources that I renamed or images that I forgot to upload. I should be able to run it on a cronjob and use [operatornotify](/writing/emailing_myself) to get emails about it, after I fine-tune the error / warning levels. I tend to link to a lot of youtube videos which, of course, I won't be rehosting here due to their large file size, but if it's a video I really care I'll have downloaded a copy to my personal computer and can find some way of getting it to you.

View File

@ -77,7 +77,7 @@ Here's a fantastic trick I started using just recently. Since all my software is
When I download a new version of a program, I just delete that junction and make it again with the new version number, and all linked paths work as normal, as long as the name of the .exe itself hasn't changed.
I don't like filling up my desktop or start menu with shortcuts, so I wrote [PGUI](https://github.com/voussoir/cmd/blob/master/PGUI.pyw) which acts as a launcher for any .lnk files located inside my PGUI folder. Each of these shortcuts actually point to the `__latest\program.exe` path. As a bonus, my PGUI folder is one of the few folders to enjoy the honor of being on my PATH, which means I can just start them from the command line or Win+R -- that's all possible without a gui launcher of course, but it's a great symbiosis.
I don't like filling up my desktop or start menu with shortcuts, so I wrote [PGUI](https://git.voussoir.net/voussoir/cmd/src/branch/master/PGUI.pyw) which acts as a launcher for any .lnk files located inside my PGUI folder. Each of these shortcuts actually point to the `__latest\program.exe` path. As a bonus, my PGUI folder is one of the few folders to enjoy the honor of being on my PATH, which means I can just start them from the command line or Win+R -- that's all possible without a gui launcher of course, but it's a great symbiosis.
![](latest_junction_1.png)
@ -97,7 +97,7 @@ D:\git\cmd\PGUI
Two folders for system executables, and three folders owned by **me**, because this is a monarchy and I'll appoint whoever I want. No application, under any circumstance, gets to put its own install directory on the PATH. If I need global commandline access to that program, I'll put an .lnk or symlink in \cmd.
At one point, I had some issues with programs that I wanted to subprocess from Python. `subprocess.run(shutil.which('ffmpeg'))` was breaking because `subprocess.run` wants to receive executable files and of course the only thing on my path was an .lnk. So, I wrote [winwhich.py](https://github.com/voussoir/voussoirkit/blob/master/voussoirkit/winwhich.py) and now that problem is solved. The software bends to my will, not the other way around.
At one point, I had some issues with programs that I wanted to subprocess from Python. `subprocess.run(shutil.which('ffmpeg'))` was breaking because `subprocess.run` wants to receive executable files and of course the only thing on my path was an .lnk. So, I wrote [winwhich.py](https://git.voussoir.net/voussoir/voussoirkit/src/branch/master/voussoirkit/winwhich.py) and now that problem is solved. The software bends to my will, not the other way around.
## Use junctions to reign in appdata
@ -116,7 +116,7 @@ Now you are closer to portable enlightenment and can zip up the program folder w
Although I run regular backups, I don't include my whole Software folder in the backup routine because I already keep all the portable zips and installers backed up separately. The only items of value in the install directory are config files, most of which are not highly personalized and I don't mind losing either.
But, there are a few programs which I've heavily configured and I'd hate to start from scratch with them. Sublime, Putty, and WinSCP for example. For those I wrote a wrapper around WinRAR, [rarpar.py](https://github.com/voussoir/cmd/blob/master/rarpar.py), which I use to create highly compressed archives with a datestamp in the filename. Because the `__latest` files will be included in duplicate, you might think it'd waste a lot of space, but with a Solid archive and a very high dictionary size, that duplication causes no extra disk usage.
But, there are a few programs which I've heavily configured and I'd hate to start from scratch with them. Sublime, Putty, and WinSCP for example. For those I wrote a wrapper around WinRAR, [rarpar.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/rarpar.py), which I use to create highly compressed archives with a datestamp in the filename. Because the `__latest` files will be included in duplicate, you might think it'd waste a lot of space, but with a Solid archive and a very high dictionary size, that duplication causes no extra disk usage.
## Dealing with uncooperative software

View File

@ -23,7 +23,7 @@ I have likened programming to art, but really it is more like craftsmanship. If
This is why I have decided to start writing these articles. I have many ideas and though I am absolutely fraught with [counter-counter-thoughts](/writing/counter_counter_thoughts), I'm doing it anyway so that I can feel like I've actually used the thoughts for something instead of letting them be forgotten in timid silence. I want to have something to show non-programmers for what I do and what I think. To explain my interest in such-and-such technology in some way other than "here's a git repository I made for that".
[footnote_text] This section is basically me whining about not getting enough praise so let me clarify. I write programs because I enjoy it and because they benefit me. [Here's a bunch of scripts](https://github.com/voussoir/cmd) unlikely to be read by anyone, but which represent the me-shaped indentation I've left on my computer like a bed that you slept on too long. The more things I automate, the more time I have left over to... search for something else to automate. It's just that, let's face it, sometimes I wish I could show code to people and have them say "wow, nice!".
[footnote_text] This section is basically me whining about not getting enough praise so let me clarify. I write programs because I enjoy it and because they benefit me. [Here's a bunch of scripts](https://git.voussoir.net/voussoir/cmd) unlikely to be read by anyone, but which represent the me-shaped indentation I've left on my computer like a bed that you slept on too long. The more things I automate, the more time I have left over to... search for something else to automate. It's just that, let's face it, sometimes I wish I could show code to people and have them say "wow, nice!".
## My other art and future ideas
@ -39,7 +39,7 @@ The only valuable video left on my channel is [2^2n - 1 = 3k](https://www.youtub
Many of the articles I write here are things that I would like to make videos about. But while text is cheap and gittable, videos are not, so I think this is the better way to start.
**Minecraft otherwise**: My first real programming experience beyond .bat files was in Java, which I had to learn in order to make mods for Minecraft. I made my own ores, tools, plants, and trees. About 30% of the items I made had some semblance of legitimate purpose, the rest were because I had come up with some pixel art that I liked and wanted to make an item for it. If you want to see some python projects that spawned out of my Minecraft interest, see [VoxelSphereGenerator](https://github.com/voussoir/else/tree/master/VoxelSphereGenerator) and [Minecraft3DVector](https://github.com/voussoir/else/tree/master/Minecraft3DVector).
**Minecraft otherwise**: My first real programming experience beyond .bat files was in Java, which I had to learn in order to make mods for Minecraft. I made my own ores, tools, plants, and trees. About 30% of the items I made had some semblance of legitimate purpose, the rest were because I had come up with some pixel art that I liked and wanted to make an item for it. If you want to see some python projects that spawned out of my Minecraft interest, see [VoxelSphereGenerator](https://git.voussoir.net/voussoir/else/tree/master/VoxelSphereGenerator) and [Minecraft3DVector](https://git.voussoir.net/voussoir/else/tree/master/Minecraft3DVector).
**TF2**: I have 1700 hours in TF2. Some hundreds of those are from idling to get refined metal back when it was 2.33 per key. Out of the active play hours, I'd say at least 90%+ were spent on cp\_orange\_x3 or variant maps. There was a server with a 100% crits mod that I played on regularly, and when I made my own orange map cp\_orange\_Skyward they were kind enough to host it and I became a moderator on the server. Here are some screenshots of Skyward ([one](tf2_skyward1.jpg), [two](tf2_skyward2.jpg), [three](tf2_skyward3.jpg), [four](tf2_skyward4.jpg)) and I will see about cleaning up the messy vmf and uploading that.

View File

@ -35,7 +35,7 @@ These video files contain 80 frames, each a tile of a larger image. I used ffmpe
The first 16 frames can be stitched together in a 4x4 grid to produce a 1024x688 image, and the last 64 frames can be stitched 8x8 to produce a 2048x1376 image. It's quite astonishing to see these levels in such high resolution after all these years.
I deleted 01-16 from each folder, then updated my [stitch.py](https://github.com/voussoir/cmd/blob/master/stitch.py) to take a `--grid` argument, and stitched the remaining 64 images together for each level.
I deleted 01-16 from each folder, then updated my [stitch.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/stitch.py) to take a `--grid` argument, and stitched the remaining 64 images together for each level.
![](stitch_8x8.png)
@ -45,7 +45,7 @@ I also extracted the audio clues and converted them to flac. Unfortunately, lots
![](hxd.png)
I wrote [ffdecodetest.py](https://github.com/voussoir/cmd/blob/master/ffdecodetest.py) to help me with this sort of task. The backyard and living room levels got particularly rekt, with only three and one clue passing the test, respectively.
I wrote [ffdecodetest.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/ffdecodetest.py) to help me with this sort of task. The backyard and living room levels got particularly rekt, with only three and one clue passing the test, respectively.
![](ffdecodetest1.png)

View File

@ -25,9 +25,9 @@ Initially, I was concerned that if I recorded too many tracks, the boring commut
However, the data structure and user interface of Trackbook are not geared toward 24/7 recording. It is designed for recording outings with a distinct start and end, and this presented me with some [friction](/writing/friction) in using it the way I imagined.
<p style="text-align:center;"><a style="display:inline-block;" href="https://github.com/voussoir/trkpt"><img src="trkpt_squircle_128x128.png"/></a></p>
<p style="text-align:center;"><a style="display:inline-block;" href="https://git.voussoir.net/voussoir/trkpt"><img src="trkpt_squircle_128x128.png"/></a></p>
I sat on the problem for a very long time, and then I finally decided to make a fork of Trackbook called [trkpt](https://github.com/voussoir/trkpt) that takes the data model in a direction more suited to my goal. This is my first time forking someone else's project and republishing it with a new name. It makes me feel somewhat guilty, or rude, but it's ok by the license and I'm enjoying the freedom to make this work just how I want it.
I sat on the problem for a very long time, and then I finally decided to make a fork of Trackbook called [trkpt](https://git.voussoir.net/voussoir/trkpt) that takes the data model in a direction more suited to my goal. This is my first time forking someone else's project and republishing it with a new name. It makes me feel somewhat guilty, or rude, but it's ok by the license and I'm enjoying the freedom to make this work just how I want it.
Basically, I swapped out the JSON-based file storage with an [SQLite](/writing/sqlite_what_a_hunk) database, and removed the entire concept of "Tracks" as a stored object. Instead, the database of trackpoints is queried on the fly to produce tracks with any start and end time you want, which you can export as GPX files. This also opens the door to JOSM-style megarenders and geospatial queries like "what day was I here?". I even made it pink.

View File

@ -17,7 +17,7 @@ The first step is to take the pictures. This step is very important.
I am currently using [Honeyview](https://en.bandisoft.com/honeyview/) to review and cull photos. I like that it has minimal interface elements and a good fullscreen mode.
I don't do much editing, but some pictures need a bit of level adjustment due to harsh sunlight or missed exposure. I do this in Photoshop, but Photoshop has an annoying habit of trampling all over the EXIF data as if it owns the place. I always make a copy of the original photo before doing these edits, so that afterward a quick call to this [exiftool bat file](https://github.com/voussoir/cmd/blob/master/exifcopy.bat) can put the EXIF back where it belongs. You may say that this results in dishonest metadata, but to me that is less of a sin than letting Adobe put its name all over my files.
I don't do much editing, but some pictures need a bit of level adjustment due to harsh sunlight or missed exposure. I do this in Photoshop, but Photoshop has an annoying habit of trampling all over the EXIF data as if it owns the place. I always make a copy of the original photo before doing these edits, so that afterward a quick call to this [exiftool bat file](https://git.voussoir.net/voussoir/cmd/src/branch/master/exifcopy.bat) can put the EXIF back where it belongs. You may say that this results in dishonest metadata, but to me that is less of a sin than letting Adobe put its name all over my files.
For hosting and distributing the files, it is important to me that the system is under my ownership, for a few reasons. Firstly, the HTML that I can put together is better for the purpose of looking at and downloading original pictures than the interface you'll get from most image sharing or cloud drive websites. Secondly, I want all of these files to be private, or at least non-indexed, because I respect my friends and don't want to make pictures of them public on the internet. They can download their favorites and put those on social media if they choose to, but I won't.
@ -25,11 +25,11 @@ The website you are reading now is currently hosted on an OVH VPS, which comes w
In order to give my R2 bucket a domain name, I had to move my DNS nameservers to Cloudflare's. Adding A records on my registrar's DNS was not good enough because Cloudflare does special serverside checks of some kind. If I am wrong about this and it's possible to use R2 with non-cloudflare nameservers, please email me as I'd like to know. I am not thrilled about making this compromise but, obviously, the other factors outweighed it. I turned off all of Cloudflare's enabled-by-default caching / traffic routing for the rest of voussoir.net. All of the files I upload to R2 are publicly accessible by just their URL, but since there are no directory listings it is not really possible for search engines to discover them unless someone I've given the link to has put it online [footnote_link].
I wrote [photo_rename.py](https://github.com/voussoir/cmd/blob/master/photo_rename.py) to rename the files from "DSCF0001.jpg" to timestamps. Even though we live in an age of 10 to 30 fps burst shooting modes, a lot of cameras still don't put subsecond timing in their EXIF data, mine included unfortunately. So, photos from the same second are marked as x1, x2, etc.
I wrote [photo_rename.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/photo_rename.py) to rename the files from "DSCF0001.jpg" to timestamps. Even though we live in an age of 10 to 30 fps burst shooting modes, a lot of cameras still don't put subsecond timing in their EXIF data, mine included unfortunately. So, photos from the same second are marked as x1, x2, etc.
I wrote [photogallery.py](https://github.com/voussoir/cmd/blob/master/photogallery.py) to generate a simple HTML page. Remember, the goals are to **look at pictures**, so there is no "page 1... page 2... page 3..." nuisance or teeny-tiny thumbnails as you'd get on most photo gallery websites; and to **download pictures**, so every element links to the full res file and an `<a download>` button is added to every photo for additional convenience. This requires that the server allows hotlinking, which you don't typically get from cloud drive providers but you do get from object storage providers. The image links are `target="_blank"` so that if you miss the download button, you get the big photo in a new tab and don't lose your scroll position on the gallery. `photogallery *.jpg --title "2022-11-12 Sharing Photos" --urlroot "https://files.voussoir.net/2022-11-12 Sharing Photos/"` prepares the HTML, and I usually go in and add some kind of greeting like "Hi everyone, here are the photos from today's event, please enjoy".
I wrote [photogallery.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/photogallery.py) to generate a simple HTML page. Remember, the goals are to **look at pictures**, so there is no "page 1... page 2... page 3..." nuisance or teeny-tiny thumbnails as you'd get on most photo gallery websites; and to **download pictures**, so every element links to the full res file and an `<a download>` button is added to every photo for additional convenience. This requires that the server allows hotlinking, which you don't typically get from cloud drive providers but you do get from object storage providers. The image links are `target="_blank"` so that if you miss the download button, you get the big photo in a new tab and don't lose your scroll position on the gallery. `photogallery *.jpg --title "2022-11-12 Sharing Photos" --urlroot "https://files.voussoir.net/2022-11-12 Sharing Photos/"` prepares the HTML, and I usually go in and add some kind of greeting like "Hi everyone, here are the photos from today's event, please enjoy".
I use [resize.py](https://github.com/voussoir/cmd/blob/master/resize.py) to prepare the gallery thumbnails. `resize *.jpg --width 1024 --height 1024 --output thumbs\small_{filename} --quality 80` does the job. 1024px is large enough to comfortably browse the photos without clicking through to the big version of each, and the thumbnails are decently light and not too [jpeggy](https://www.youtube.com/watch?v=QEzhxP-pdos "Do I look like I know what a JPEG is?") at about 125-150 kilobytes on average. I make sure to put the word "small" at the beginning of the thumbnail filename so that if someone downloads the thumbnail instead of the full picture by mistake, it should be pretty obvious unless they are particularly undiscerning, in which case their ignorance is bliss.
I use [resize.py](https://git.voussoir.net/voussoir/cmd/src/branch/master/resize.py) to prepare the gallery thumbnails. `resize *.jpg --width 1024 --height 1024 --output thumbs\small_{filename} --quality 80` does the job. 1024px is large enough to comfortably browse the photos without clicking through to the big version of each, and the thumbnails are decently light and not too [jpeggy](https://www.youtube.com/watch?v=QEzhxP-pdos "Do I look like I know what a JPEG is?") at about 125-150 kilobytes on average. I make sure to put the word "small" at the beginning of the thumbnail filename so that if someone downloads the thumbnail instead of the full picture by mistake, it should be pretty obvious unless they are particularly undiscerning, in which case their ignorance is bliss.
I followed Cloudflare's guide to set up the AWS S3 commandline utility for use with R2 ([one](https://developers.cloudflare.com/r2/data-access/s3-api/api/), [two](https://developers.cloudflare.com/r2/data-access/s3-api/tokens/), [three](https://developers.cloudflare.com/r2/examples/aws-cli/)) and I put that in a shortcut file on my PATH. So, `voussoirr2.lnk` points to `C:\Python\__latest\Scripts\aws.cmd --endpoint-url "https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.r2.cloudflarestorage.com"`. I call `voussoirr2 s3 cp . "s3://voussoir/2022-11-12 Sharing Photos" --recursive` to upload.

View File

@ -200,7 +200,7 @@ Regardless of the value of PATHEXT, you are always allowed to specify the full n
And just like that, we've achieved the black box principle of executables. You can rewrite a .bat program in .py, or you can supercede a System32 exe with a script of your own. The only thing you can't do is supercede the shell builtins -- `dir` always does `dir` even if you have a candidate in the cwd or on the PATH -- you'll have to call those files by their extensioned names.
Between my public [cmd](https://github.com/voussoir/cmd) repository and my non-public cmd folder, I've got more than 180 .py and .bat files making up my command line toolkit. Of course I'm not specifying the extensions when I run them, guys.
Between my public [cmd](https://git.voussoir.net/voussoir/cmd) repository and my non-public cmd folder, I've got more than 180 .py and .bat files making up my command line toolkit. Of course I'm not specifying the extensions when I run them, guys.
## Hedges