commit 9656f3b061a4ec43449ccf2abe18580317899267 Author: Ryan Fox Date: Mon Jul 13 02:35:49 2020 +0000 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67f550c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +ftp_url.txt +music/* +dist/ +scripts/__pycache__/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..19f6d17 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +The code & design of this website are under the MIT license. +Do not assume anything about the contents of the website itself. + +Copyright (c) 2020 Ryan Fox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8b65639 --- /dev/null +++ b/Makefile @@ -0,0 +1,53 @@ +PAGES_IN = $(wildcard pages/*) +POSTS_IN = $(wildcard posts/*.md) +MUSIC_IN = $(wildcard music/*.mp3) + +PAGES_OUT = $(addprefix dist/,$(notdir $(PAGES_IN:.md=.html))) +POSTS_OUT = $(addprefix dist/blog/,$(notdir $(POSTS_IN:.md=.html))) +MUSIC_OUT = $(addprefix dist/music/,$(notdir $(MUSIC_IN))) + +POST_LISTS = dist/blog/index.html dist/blog/feed.xml +MUSIC_LISTS = dist/music/index.html dist/music/feed.xml + +all: $(PAGES_OUT) $(POSTS_OUT) $(MUSIC_OUT) $(POST_LISTS) $(MUSIC_LISTS) + cp -urv static/. dist + +dist/%.html: pages/%.md | dist + scripts/template_md.py $< $@ + +dist/%.html: pages/%.html | dist + cp $< $@ + +dist/blog/%.html: posts/%.md | dist/blog + scripts/template_md.py $< $@ + +dist/music/%.mp3: music/%.mp3 | dist/music + cp $< $@ + +$(POST_LISTS): $(POSTS_OUT) + scripts/gen_blog.py posts $@ + +$(MUSIC_LISTS): music.json | $(OUTPUT_MUSIC) + scripts/gen_music.py $< $@ + +dist/blog: + mkdir -p dist/blog + +dist/music: + mkdir -p dist/music + +dist: + mkdir dist + +.PHONY: clean + +upload: dist + lftp -c "set ftp:list-options -a;\ + set ssl:verify-certificate false;\ + open '$(shell cat ftp_url.txt)';\ + lcd dist;\ + cd public_html;\ + mirror --reverse" + +clean: + rm -rf dist diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..9d752d8 --- /dev/null +++ b/README.txt @@ -0,0 +1,6 @@ +flewkey.com + +This repository contains flewkey.com, my personal website. +Jekyll was too complicated for me, so I cobbled together some Python scripts. + +Send questions, issues, and patches to flewkey@2a03.party. diff --git a/music.json b/music.json new file mode 100644 index 0000000..6a9eb96 --- /dev/null +++ b/music.json @@ -0,0 +1,210 @@ +[ + { + "id": "calm_down", + "name": "Calm Down", + "description": "This is a bit of a mess, but I like some of the ideas here.", + "timestamp": 1588820400 + }, + { + "id": "larry_the_cow", + "name": "Larry the Cow", + "description": "I haven't posted any music since May, so here's something I never uploaded.", + "timestamp": 1588462200 + }, + { + "id": "microwave", + "name": "Microwave", + "description": "This isn't a coherent song, my goal was to have fun.", + "timestamp": 1583470800 + }, + { + "id": "scrap_5", + "name": "Scrap #5", + "description": "I tried Ardour for the first time. It's fun.", + "group": "Scraps", + "timestamp": 1582677000 + }, + { + "id": "scrap_4", + "name": "Scrap #4", + "description": "I tried publishing this without Windows this time.", + "group": "Scraps", + "timestamp": 1582509600 + }, + { + "id": "scrap_3", + "name": "Scrap #3", + "description": "I thought this sounded neat.", + "group": "Scraps", + "timestamp": 1580418000 + }, + { + "id": "scrap_2", + "name": "Scrap #2", + "description": "Another scrapped idea.", + "group": "Scraps", + "timestamp": 1578792600 + }, + { + "id": "baby_steps", + "name": "Baby Steps", + "description": "I tried using Ableton to edit a song, and I think it went well.", + "timestamp": 1575594000 + }, + { + "id": "cold_spoon", + "name": "Cold Spoon", + "description": "I pulled this out of my unfinished folder. Very loopy.", + "timestamp": 1574973000 + }, + { + "id": "feeling_chaotic", + "name": "Feeling Chaotic", + "description": "I felt like using FamiTracker again, so I did this quickly.", + "timestamp": 1573686000 + }, + { + "id": "scrap_1", + "name": "Scrap #1", + "description": "I'm going to post \"scraps\" now. These happen when I don't want to finish something I make.", + "group": "Scraps", + "timestamp": 1565319600 + }, + { + "id": "seriously_what_the_hell_is_this", + "name": "Seriously What The Hell Is This", + "description": "Still figuring stuff out. Why do all Ableton instruments sounds weird? Yeah screw this, I'm not making any more.", + "timestamp": 1562025600 + }, + { + "id": "learning_new_software", + "name": "Learning New Software", + "description": "My first attempt making music outside of a tracker. I spent nearly an hour fighting MuseScore and I have no clue what happened.", + "timestamp": 1561939200 + }, + { + "id": "screw_this", + "name": "Screw This!", + "description": "FamiSpam failed due to my complete laziness. This is the last song I will upload to the series. Afterwards, I'm probably going to learn proper composition and stop making tracker music for a while.", + "group": "FamiSpam", + "timestamp": 1560384000 + }, + { + "id": "puzzle_house", + "name": "Puzzle House", + "description": "Happy birthday DevEd!", + "group": "FamiSpam", + "timestamp": 1558396800 + }, + { + "id": "shaking_off_rust", + "name": "Shaking Off Rust", + "description": "I haven't made music for a while. Most of this is terrible, but that won't stop me from publishing it.", + "group": "FamiSpam", + "timestamp": 1557360000 + }, + { + "id": "back_into_it", + "name": "Back Into It", + "description": "Sorry this one sounds awkward, I feel rusty with this. I quit after 12 days last time, so I'll try to beat my previous streak.", + "group": "FamiSpam", + "timestamp": 1555804800 + }, + { + "id": "it_gets_better", + "name": "It Gets Better", + "description": "I was frustrated with this at first.", + "group": "FamiSpam", + "timestamp": 1554595200 + }, + { + "id": "boring_and_loopy", + "name": "Boring and Loopy", + "description": "I tried to make a loopy song, and it turned out terribly. I'll try to do better next time.", + "group": "FamiSpam", + "timestamp": 1554422400 + }, + { + "id": "racing_or_something", + "name": "Racing or Something", + "description": "This is one of my favourite ones so far.", + "group": "FamiSpam", + "timestamp": 1554249600 + }, + { + "id": "the_confusing_one", + "name": "The Confusing One", + "description": "Made in confusion.", + "group": "FamiSpam", + "timestamp": 1554076800 + }, + { + "id": "the_first_half_sucks", + "name": "The First Half Sucks", + "description": "I have no clue how to use arps properly.", + "group": "FamiSpam", + "timestamp": 1553904000 + }, + { + "id": "music_box", + "name": "Music Box", + "description": "I'm not happy with my music, so I've resolved to make 30 seconds of music a day. My goal is to write 20 minutes of music in 2 months.", + "group": "FamiSpam", + "timestamp": 1553731200 + }, + { + "id": "laziness", + "name": "Laziness.mp3", + "description": "I made this for an OHB on Battle of the Bits.", + "group": "FamiSpam", + "timestamp": 1551207600 + }, + { + "id": "filthy_foe", + "name": "Filthy Foe.nsf", + "description": "I made this for an OHB on Battle of the Bits.", + "timestamp": 1551193200 + }, + { + "id": "ohc03815_famitracker_pack_pack_0cc", + "name": "OHC03815 famitracker pack pack.0cc", + "description": "I made this for an OHB on Battle of the Bits.", + "timestamp": 1551171600 + }, + { + "id": "unsanitary_snow", + "name": "Unsanitary Snow", + "description": "A track I made for Winter Chip XIV on Battle of the Bits. Hopefully I'll have the time to try participating in OHBs this year.", + "timestamp": 1550016000 + }, + { + "id": "we_give_up", + "name": "We Give Up", + "description": "Some short FamiTracker songs.", + "timestamp": 1547942400 + }, + { + "id": "running_away", + "name": "Running Away", + "description": "Some short FamiTracker songs.", + "timestamp": 1547942400 + }, + { + "id": "begin_quest", + "name": "Begin Quest", + "description": "Some short FamiTracker songs.", + "timestamp": 1547942400 + }, + { + "id": "spooky_sunset", + "name": "Spooky Sunset", + "description": "Messing around in FamiTracker again.", + "timestamp": 1546905600 + }, + { + "id": "spaceship", + "name": "Spaceship", + "description": "An old-ish track I wanted to consider finished.", + "timestamp": 1532822400 + } +] diff --git a/pages/about.md b/pages/about.md new file mode 100644 index 0000000..a6ccc84 --- /dev/null +++ b/pages/about.md @@ -0,0 +1,50 @@ +template: page +title: Info +license: CC-BY + +# About Me + +Ahoy, my name is Ryan Fox. I am a Canadian guy who likes programming and music. + +I have not done anything to earn online credibility, and you are most likely +here because I sent you a link. Despite this, you are spending time reading my +website, and I am very grateful for that. + +--- + +## Encryption + +These are my PGP and SSH keys. If you would like to encrypt messages, or allow +me to access your server, these are the ones to use. I am somewhat confident +that they will not get compromised. + +* PGP: [D910C0CAC5BF5739861A4ABD94F56ADFD848851E]({root}pgp.txt) +* SSH: [SHA256:IggQMESZIgwIP4/zfYI8ZA1xnMd9g9huOUtgfaKjmCo]({root}ssh.txt) + +--- + +## Profiles + +Here is a list of my main profiles, the ones worth listing. + +* GitHub: [@flewkey](https://github.com/flewkey) +* Twitter: [@flewkey](https://twitter.com/flewkey) +* YouTube: [@flewkey](https://www.youtube.com/channel/UCRUfdaM8EiXu7SCfGl8FUpQ) +* PeerTube: [@flewkey_channel@toobnix.org](https://toobnix.org/video-channels/flewkey_channel/) +* Mastodon: [@flewkey@layer8.space](https://layer8.space/@flewkey) +* Newgrounds: [@flewkey](https://flewkey.newgrounds.com/) +* Soundcloud: [@flewkey](https://soundcloud.com/flewkey) +* SDF GIT Society [@flewkey](https://git.sdf.org/flewkey) + +--- + +## Contact + +If you would like to contact me privately, please send me an e-mail if possible. +Even a long, drawn out e-mail will take less time out of my day than the average +text exchange. Thanks! + +I can also be found at [##dystopia]({root}dystopia{ext}) on Freenode. While I +encourage people to use the IRC channel, there is also a +[Discord server]({root}dystopia{ext}) linked to the chatroom for those who find +IRC inconvenient for some reason. diff --git a/pages/dystopia.md b/pages/dystopia.md new file mode 100644 index 0000000..1ce82e9 --- /dev/null +++ b/pages/dystopia.md @@ -0,0 +1,30 @@ +template: page +title: Dystopia +license: CC-BY + +# Dystopia + +Dystopia is a personal Discord server and IRC channel which I run for myself, +and various other people I know. It is also one of the easiest places to contact +me. + +All messages on the IRC channel will be mirrored to Discord and logged. + +--- + +## Links + +* \#\#dystopia on Freenode +([webchat](https://webchat.freenode.net/?channels=##dystopia)) +* on Discord + +--- + +## Rules + +* Use common sense. +* Do not annoy others. +* Absolutely no flame wars. +* Conduct yourself as a guest. +* Never post slurs, I won't allow it. +* Avoid posting personal information. diff --git a/pages/index.md b/pages/index.md new file mode 100644 index 0000000..09c34c7 --- /dev/null +++ b/pages/index.md @@ -0,0 +1,19 @@ +template: page +title: Homepage +license: CC-BY + +# Homepage + +Ahoy, my name is Ryan Fox. I am a Canadian guy who likes programming and music. + +Have you come to [read about me]({root}about{ext})? + +If not, feel free to [read some blog posts]({root}blog/) or +[listen to my music]({root}music/). + +--- + +## Projects + +* [flewkey-overlay](https://git.sdf.org/flewkey/flewkey-overlay) - My personal Gentoo overlay. +* [minecraft-tweaks-2a03](https://git.sdf.org/flewkey/minecraft-tweaks-2a03) - A basic Minecraft mod. diff --git a/posts/2020-06-30-blog.md b/posts/2020-06-30-blog.md new file mode 100644 index 0000000..008ec61 --- /dev/null +++ b/posts/2020-06-30-blog.md @@ -0,0 +1,21 @@ +template: post +title: Website update +author: flewkey +timestamp: 1593548958 +license: CC-BY + +I like blogs. Blogs are one of the only suitable platforms for people to +properly present ideas. There are no character limits, the author can embed +whatever they want, and — most importantly — they can disable the +comments section. This caught my attention, so I decided to start my own blog. +Unfortunately, blogging on my plain HTML website was a pain. It needed an +update. + +Multiple people had recommended Jekyll to me, and I had seen many others use it +for their websites. Making a simple blog with it seemed simple, but extending +it to handle a custom page for my music was a pain. It didn't help that I am far +too lazy to learn about Ruby. + +The solution was ridiculously simple: a Makefile and some Python scripts. I had +to fiddle with it for a couple of hours, but I am quite happy with the result. +There should be less typos in the RSS feeds now! diff --git a/posts/2020-07-12-nds-constraint.md b/posts/2020-07-12-nds-constraint.md new file mode 100644 index 0000000..b3eb272 --- /dev/null +++ b/posts/2020-07-12-nds-constraint.md @@ -0,0 +1,97 @@ +template: post +title: nds-constrain't +author: flewkey +timestamp: 1594525504 +license: CC-BY + +Once upon a time, shutterbug2000 discovered nds-constrain't: a bug in Nintendo's +NTR SSL library that allowed it's connections to be easily intercepted. This +made it possible to connect to alternative online services without ROM patching. + +The bug itself is simple: the NDS SSL library does not care whether or not a +certificate is authorized to act as a certificate authority. This means that +— with any valid certificate — we can sign whatever we want, even a +certificate under a false hostname. + +A guide to using this bug for fun and profit is now available on the +[official page](https://github.com/KaeruTeam/nds-constraint), which is much +better written than mine. However, you are free to keep reading this one. + +--- + +### Getting the Wii client certificate + +As explained in the [official page](https://github.com/KaeruTeam/nds-constraint) +for nds-constrain't, the Wii client certificate is signed by Nintendo and +considered valid. Therefore, we can use it's key to sign whatever we want. You +_could_ grab it from a Wii, but it is much easier to download it from +[Larsenv's page](https://larsenv.github.io/NintendoCerts/index.html). You will +want to use the link labelled "Wii NWC Prod 1", by the way. + +### Converting it to a useable format + +The file is a PKCS12, and we can't do anything useful with it until we extract +the certificate and the private key. Thankfully, this is pretty simple. + + openssl pkcs12 -in WII_NWC_1_CERT.p12 -passin pass:alpine -passout pass:alpine -out keys.txt + +That command will export the X.509 certificate and private key from the archive, +and store the output in keys.txt. They can then be copied into their appropriate +files, which I will name NWC.crt and NWC.key. + +### Signing your certificate + +Instructions for this are listed on the official GitHub page, but I have copied +them for reference. If I remember correctly, the DS can only handle the SHA-1 +and MD5 hash formats, so pay attention to the `-sha1` flag. + + openssl genrsa -out server.key 1024 + openssl req -new -key server.key -out server.csr + openssl x509 -req -in server.csr -CA NWC.crt -CAkey NWC.key -CAcreateserial -out server.crt -days 3650 -sha1 + +Your webserver probably wants the certificate chain as well, so let's generate +that as well. + + cat server.crt NWC.crt > server-chain.crt + +We are ready to rock and roll! + +### Using your phony certificate + +Once the SSL certificate is installed, you may run into issues connecting with +your DS. This because your NDS only knows how to use SSLv3, with the SHA-1 or +MD5 cipher sets. Enabling SSLv3 and SHA-1 isn't always possible with webservers, +so I recommend using NGINX as a reverse-proxy. To enable DS compatibility for +NGINX, add the following lines to your NGINX configuration. + + ssl_protocols SSLv3; + ssl_ciphers ECDHE-RSA-AES128-SHA; + +Most services on Nintendo consoles make liberal use of headers. Because of this, +some extra options need to be enabled. + + underscores_in_headers on; + proxy_pass_request_headers on; + +Because we have enabled insecure SSL settings on NGINX, you probably don't want +to use it for any mission-critical web applications. If you continue having +issues with NDS connectivity, please contact me. + +### Having fun + +The possibilities are infinite. Want to run services through a debugging proxy? +Implement WFC protocols? Make a Flipnote Studio server? All of this is possible +without ROM patches! + +--- + +### Update + +After receiving some e-mails, I have learned that the NDS-supported ciphers have +been disabled in OpenSSL versions past 1.0.2g, unless configured with +"enable-weak-ssl-ciphers". This means that you may have to re-build NGINX (or +mod_ssl for Apache) to get it working. + +If you have the means, I suggest taking Wireshark captures to find the cause of +any SSL issues. Enabling debug logging in NGINX can also help you pinpoint +handshake errors. If all else fails, you can find my e-mail on the about page. diff --git a/scripts/gen_blog.py b/scripts/gen_blog.py new file mode 100755 index 0000000..1da213c --- /dev/null +++ b/scripts/gen_blog.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys +import markdown +from template import template + +def gen_list(files, ext): + md = markdown.Markdown(extensions = ["meta", "extra"]) + items = "" + for post in files: + with open("posts/"+post, "r", encoding="utf-8") as file_in: + html = md.convert(file_in.read()) + md.Meta["template"] = "post_preview" + items += template(html, "blog/"+post[:-3]+ext, ext, md.Meta) + return items + +def main(): + meta = {"template": "post_list"} + ext = "."+sys.argv[2].split(".")[-1] + files = sorted(os.listdir(sys.argv[1]), reverse=True) + output = template(gen_list(files, ext), sys.argv[2], ext, meta) + with open(sys.argv[2], "w+", encoding="utf-8") as file_out: + file_out.write(output) + +if __name__ == "__main__": + main() diff --git a/scripts/gen_music.py b/scripts/gen_music.py new file mode 100755 index 0000000..ae61b93 --- /dev/null +++ b/scripts/gen_music.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys +import json +from template import template + +def gen_list(music, ext): + items = "" + for track in music: + track["template"] = "track_preview" + track["file_size"] = str(os.path.getsize("music/"+track["id"]+".mp3")) + if "group" in track: + track["group_text"] = "Part of "+track["group"]+"
" + items += template("", sys.argv[2], ext, track) + return items + +def main(): + meta = {"template": "music_list"} + ext = "."+sys.argv[2].split(".")[-1] + with open(sys.argv[1], "r", encoding="utf-8") as file_in: + music = json.loads(file_in.read()) + output = template(gen_list(music, ext), sys.argv[2], ext, meta) + with open(sys.argv[2], "w+", encoding="utf-8") as file_out: + file_out.write(output) + +if __name__ == "__main__": + main() diff --git a/scripts/template.py b/scripts/template.py new file mode 100644 index 0000000..cfcad89 --- /dev/null +++ b/scripts/template.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import markdown +from datetime import datetime +from email import utils + +def get_path(path, ext_in, ext_out): + path = path[len("pages"):] + if path[-len("index"+ext_in):] == "index"+ext_in: + return path[:-len("index"+ext_in)] + return path[:-len(ext_in)] + ext_out + +def get_dir(path): + return "/".join(path.split("/")[1:-1])+"/" + +def get_root(path): + root = "" + for i in range(len(path.split("/"))-2): + root += "../" + return root + +licenses = { + "CC-BY": ["cc-by", "CC BY 4.0", "https://creativecommons.org/licenses/by/4.0/"] +} + +def get_badge(license, root): + if license not in licenses: + return "" + data = licenses[license] + return "\""+data[1]+"\"/" + +def read_template(template, meta_in): + if template[:10] != "meta: yes\n": + return [template, {"meta": "no"}] + data = template[:-1].split("\n\n") # Strip trailing newline + md = markdown.Markdown(extensions = ["meta"]) + md.convert(data[0]) + content = "\n\n".join(data[1:]) + content = content.replace("{", "{{").replace("}", "}}") + meta = meta_in.copy() + meta.update(md.Meta) + return [content, MetaDict(meta)] + +defaults = { + "base": "https://flewkey.com/", + "title": "Untitled", + "author": "flewkey", + "ext": ".html", + "head": "" +} + +class MetaDict(dict): + def __missing__(self, key): + return defaults[key] if key in defaults else "" + def __getitem__(self, key): + value = dict.__getitem__(self, key) + if isinstance(value, list): + return "\n".join(value) + else: + return value + +def template(content, path, ext, meta): + global base + meta = MetaDict(meta) + if meta["template"] == None: + return content.format_map(meta) + with open("templates/"+meta["template"]+ext, "r", encoding="utf-8") as file: + t_content = file.read() + t_info = read_template(t_content, meta) + if t_info[1]["meta"] != "no" and "template" in t_info[1]: + t_content = template(t_info[0], path, ext, t_info[1]) + if "root" not in meta: + meta["root"] = get_root(path) + if "home" not in meta: + meta["home"] = "." if meta["root"] == "" else meta["root"] + if "full_path" not in meta: + meta["full_path"] = get_path(path, ext, meta["ext"]) + if "full_dir" not in meta: + meta["full_dir"] = get_dir(path) + if "badge" not in meta and ext == ".html": + meta["badge"] = get_badge(meta["license"], meta["root"]) + if "date" not in meta and "timestamp" in meta: + meta["date"] = datetime.fromtimestamp(int(meta["timestamp"])).isoformat().split("T")[0] + if "pub_date" not in meta and "timestamp" in meta: + meta["pub_date"] = utils.format_datetime(datetime.fromtimestamp(int(meta["timestamp"]))) + meta["head"] += "\n" + meta["content"] = content.format_map(meta) + if "description" not in meta and ext == ".html": + meta["description"] = meta["content"].split("

")[0]+"

" # Disgusting! + return t_content.format_map(meta) diff --git a/scripts/template_md.py b/scripts/template_md.py new file mode 100755 index 0000000..d78a875 --- /dev/null +++ b/scripts/template_md.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import sys +import markdown +from template import template + +def main(): + md = markdown.Markdown(extensions = ["meta", "extra"]) + with open(sys.argv[1], "r", encoding="utf-8") as file_in: + html = md.convert(file_in.read()) + output = template(html, sys.argv[2], ".html", md.Meta) if "template" in md.Meta else html + with open(sys.argv[2], "w+", encoding="utf-8") as file_out: + file_out.write(output) + +if __name__ == "__main__": + main() diff --git a/static/.htaccess b/static/.htaccess new file mode 100644 index 0000000..61cf7fd --- /dev/null +++ b/static/.htaccess @@ -0,0 +1,9 @@ +ErrorDocument 404 /404.html + +Redirect 301 /nds-constraint.html /blog/2020-07-12-nds-constraint.html + +RewriteEngine On +RewriteCond %{HTTPS} off [OR] +RewriteCond %{HTTP_HOST} ^www\. [NC] +RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC] +RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301] diff --git a/static/assets/avatar.png b/static/assets/avatar.png new file mode 100644 index 0000000..62e1eec Binary files /dev/null and b/static/assets/avatar.png differ diff --git a/static/badges/cc-by.png b/static/badges/cc-by.png new file mode 100644 index 0000000..6c37b6f Binary files /dev/null and b/static/badges/cc-by.png differ diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..de01ea4 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/music/player.js b/static/music/player.js new file mode 100644 index 0000000..d2a332a --- /dev/null +++ b/static/music/player.js @@ -0,0 +1,14 @@ +function getAudio(player) { + return player.getElementsByTagName("audio")[0]; +} + +window.onload = function() { + var players = document.getElementsByClassName("player"); + for (var i = 0; i < players.length; i++) { + getAudio(players[i])["queue"] = i; + getAudio(players[i]).addEventListener('ended', function(event) { + const next = event.target["queue"] + 1; + if (next < players.length) getAudio(players[next]).play(); + }); + } +} diff --git a/static/pgp.txt b/static/pgp.txt new file mode 100644 index 0000000..88ac26b --- /dev/null +++ b/static/pgp.txt @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +mCHt +=BBAA +-----END PGP PUBLIC KEY BLOCK----- diff --git a/static/ssh.txt b/static/ssh.txt new file mode 100644 index 0000000..d93669d --- /dev/null +++ b/static/ssh.txt @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCnRLK2qyF44Fzcy6uyhZQwT1V0o8yTaybpXCMIYGXbOAAdWvdNYkg+smrebTgOq6BQsbiIl35x+lTjUI3vUnIRiaD7NQ6jKFW0sjhtxj5m48fT3lv2hNtd1G+zAokw9/K0//l/Ud93AijCgVP10UQi9Fi4a4Poim1B/DtUa5Bs0yp0PCSh8ZAimEhVpNby0GiI14iSnO09x/jHUC1o8hrq6zzZcsdo1Ar0Gf0NLBG9CnDG4W+wBryEzkkwpBEXT1j+cmY7Y/vs8x/UWULF+keIynb5WMCqzJAye1FdiMvx45hQiClKAJO7Qp3E1Nb/LzLiic8/s5zBSjatWSibnpF1yEuDUiBDKI8ImWJgIG5odenLgqqIw2VN5MQ6L/STseMvFN0zvqDGtGR9XCOvP0hOprP89eG0vtJLl7pUoZ3hDzdrp4tZDJczgkRTTWxkQ5yPkDaF6+B42l1FmwYJUFF9JiD+cEBC9/499yKqH++7DeynFZb5uzxAtwgu0duvrurrt2jzF0JQJ6zqzPV63lM/WrwlXPOE40rRBhShwmpLlFDB0cwchO0FPFmxfdL+mp3YXWVRnyg7QniitEtMmMmWZQVMN2/5TN9h/gziyux23BVaYsnYt+vFDmtXOztgbR/lVhgKZUIfR+CTr7qiHTkaDD8Ae7+KwruFJq3LjOkwsw== flewkey@2a03.party diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..329fc49 --- /dev/null +++ b/static/style.css @@ -0,0 +1,266 @@ +body { + font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif; + font-size: 18px; + line-height: 2; + margin: 0 0 0 0; + padding: 0 0 0 0; + background-color: #F8F8F8; + color: #111111; +} + +header { + margin-left: auto; + margin-right: auto; + max-width: 840px; + height: 128px; + border-bottom: solid 2px #AAAAAA; + padding: 32px 0 32px 0; + margin-bottom: 48px; +} + +header img { + display: inline-block; + height: 128px; + width: 128px; + border-radius: 32px; + margin-right: 16px; + float: left; +} + +header div.info div { + display: inline-block; + line-height: 1; + padding-top: 12px; + padding-bottom: 12px; + margin-left: 16px; + vertical-align: top; + text-align: left; + float: left; +} + +header div.info span.title { + display: block; + font-size: 48px; + font-weight: bold; + margin-bottom: 24px; +} + +header div.info span.subtext { + display: block; + font-size: 32px; + margin-top: 24px; +} + +nav { + text-align: center; + height: 42px; + line-height: 32px; + float: right; + text-align: right; +} + +nav a { + color: #666666; + display: block; +} + +nav a:hover { + color: #333333; +} + +main { + margin-left: auto; + margin-right: auto; + max-width: 840px; + padding-left: 32px; + padding-right: 32px; +} + +footer { + text-align: center; + border-top: solid 2px #AAAAAA; + padding-top: 16px; + padding-bottom: 16px; + margin-top: 48px; + font-size: 16px; + background-color: #DDDDDD; +} + +footer span { + display: block; +} + +span, h1, h2, h3, h4 { + padding: 0 0 0 0; + margin: 0 0 0 0; +} + +p { + margin-top: 18px; + margin-bottom: 18px; +} + +h1, h2 { + line-height: 1; + margin-bottom: 32px; +} + +h1 { + font-size: 40px; +} + +h2 { + font-size: 32px; +} + +h3 { + font-size: 24px; +} + +h4 { + font-size: 20px; +} + +div.title-subtext { + color: #555555; + font-weight: normal; + font-size: 20px; + margin-top: 18px; +} + +a { + color: #0074D9; + text-decoration: none; +} + +hr { + height: 0; + border: 0; + border-top: 2px solid #AAAAAA; + margin-top: 48px; + margin-bottom: 48px; +} + +div.block { + padding-top: 48px; + padding-bottom: 48px; + border-top: solid 2px #AAAAAA; +} +div.block:first-of-type { + padding-top: 0; + border-top: none; +} + +@media only screen and (max-width: 950px) { + header { + height: 172px; + } + + header div.info { + text-align: center; + } + + header div.info img { + float: none; + } + + header div.info div { + padding-top: 19px; + padding-bottom: 19px; + float: none; + } + + header div.info span.title { + font-size: 50px; + margin-bottom: 14px; + } + + header div.info span.subtext { + font-size: 26px; + margin-top: 14px; + } + + nav { + float: none; + line-height: 48px; + text-align: center; + } + + nav a { + display: inline; + margin-right: 32px; + margin-left: 32px; + } +} + +@media only screen and (max-width: 550px) { + body { + font-size: 17px; + } + + header { + height: 220px; + min-width: 230px; + padding: 16px 16px 16px 16px; + margin-bottom: 24px; + } + + header img { + display: block; + margin-left: auto; + margin-right: auto; + margin-bottom: 6px; + } + + header div.info div { + margin: 0 0 0 0; + padding: 0 0 0 0; + } + + header div.info span.title { + display: block; + text-align: center; + font-size: 24px; + margin-top: 6px; + margin-bottom: 6px; + } + + header div.info span.subtext { + display: block; + text-align: center; + font-size: 20px; + margin-top: 6px; + } + + nav a { + display: inline; + margin-right: 8px; + margin-left: 8px; + } + + hr { + margin-top: 24px; + margin-bottom: 24px; + } + + h1, h2 { + margin-bottom: 16px; + } + + h1 { + font-size: 36px; + } + + h2 { + font-size: 30px; + } + + div.block { + padding-top: 24px; + padding-bottom: 24px; + } + + div.title-subtext { + font-size: 18px; + } +} diff --git a/templates/music_list.html b/templates/music_list.html new file mode 100644 index 0000000..cd6f822 --- /dev/null +++ b/templates/music_list.html @@ -0,0 +1,21 @@ +meta: yes +title: Music +description: Listen to all of the music I've made! +head: + +template: page + + +

Music

+

On this page, you can find all of my attempts at music.
If you would like to listen to it elsewhere, you can find my other profiles on my about page.

+

Subscribe to my RSS feed to see my posts first.

+
+ +{content} diff --git a/templates/music_list.xml b/templates/music_list.xml new file mode 100644 index 0000000..0cd2f08 --- /dev/null +++ b/templates/music_list.xml @@ -0,0 +1,5 @@ +meta: yes +title: Music +template: rss + +{content} diff --git a/templates/page.html b/templates/page.html new file mode 100644 index 0000000..8220832 --- /dev/null +++ b/templates/page.html @@ -0,0 +1,34 @@ + + + + + + + {title} - flewkey +{head} + + +
+
+ My avatar +
+ flewkey + Level 10 Computer Mage +
+
+ +
+
+{content} +
+
+ Copyright © 2020 flewkey + {badge} +
+ + diff --git a/templates/post.html b/templates/post.html new file mode 100644 index 0000000..bdf81e6 --- /dev/null +++ b/templates/post.html @@ -0,0 +1,8 @@ +meta: yes +head: + +template: page + +

{title} +
Published on {date} by {author}

+{content} diff --git a/templates/post_list.html b/templates/post_list.html new file mode 100644 index 0000000..8772f7a --- /dev/null +++ b/templates/post_list.html @@ -0,0 +1,11 @@ +meta: yes +title: Blog +description: View a post, then view more posts. +head: +template: page + +

Blog

+

The chances of seeing a new blog post are the same as winning the lottery while getting struck by lightning twice on a leap day. If you still come across a post, I promise to make it disappointing.

+

Subscribe to my RSS feed to see my posts first.

+
+{content} diff --git a/templates/post_list.xml b/templates/post_list.xml new file mode 100644 index 0000000..c0a90ce --- /dev/null +++ b/templates/post_list.xml @@ -0,0 +1,5 @@ +meta: yes +title: Blog +template: rss + +{content} diff --git a/templates/post_preview.html b/templates/post_preview.html new file mode 100644 index 0000000..b97533d --- /dev/null +++ b/templates/post_preview.html @@ -0,0 +1,5 @@ +
+

{title}

+

{description}

+ Read more +
diff --git a/templates/post_preview.xml b/templates/post_preview.xml new file mode 100644 index 0000000..4643cfe --- /dev/null +++ b/templates/post_preview.xml @@ -0,0 +1,9 @@ + + {title} + {{base}}{{full_dir}}{full_path} + {{base}}{{full_dir}}{full_path} + {pub_date} + {description} + + {author} + diff --git a/templates/rss.xml b/templates/rss.xml new file mode 100644 index 0000000..009d351 --- /dev/null +++ b/templates/rss.xml @@ -0,0 +1,12 @@ + + + + Ryan's {title} + {base}{full_dir} + + {description} + en-ca + {author} +{content} + + diff --git a/templates/track_preview.html b/templates/track_preview.html new file mode 100644 index 0000000..b5c7522 --- /dev/null +++ b/templates/track_preview.html @@ -0,0 +1,6 @@ +
+

{name}

+ {group_text} + Published on {date} + +
diff --git a/templates/track_preview.xml b/templates/track_preview.xml new file mode 100644 index 0000000..50c6686 --- /dev/null +++ b/templates/track_preview.xml @@ -0,0 +1,9 @@ + + {name} + {{base}}{{full_dir}}#audio_{id} + {{base}}{{full_dir}}#audio_{id} + + {pub_date} + {description} + {{author}} +