Jericho's blog

Setting up a startpage

server 2021-05-20 23:40:05 +0000

I made a pretty unusual setup for generating a start page. All you need is this python script, sites.yml, and index.css. By default it expects to write the data to /var/www/html/index.html with index.css at /var/www/html/index.css. I find this script makes it pretty easy for me to add new sites whenever I want. If you want to look at other examples of start pages feel free to look at r/startpages

#!/usr/bin/env python3
import yaml
from sys import exit

try:
    with open('sites.yml', 'r') as input_file:
        data = yaml.safe_load(input_file)
except FileNotFoundError:
    print('sites.yml was not found')
    exit(1)
except yaml.scanner.ScannerError:
    print('Failed to parse yaml file')
    exit(2)

html = '''
<html>
<head>
<link rel="stylesheet" type="text/css" href="index.css">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="theme-color" content="#000000">
<title>Start Page</title>
<link href="https://fonts.googleapis.com/css2?family=Fira+Code&amp;display=swap" rel="stylesheet">
</head>
<body>
<div id="root">
<div class="App" style="display: flex;">
'''
for category, links in data.items():
    print(f'Adding category: {category}')
    html += '<div class="Feed_root">'
    html += f'<h2 class="Feed_title">#{category}</h2>'
    if links is not None:
        for header, values in links.items():
            print(f'Adding link: {header}')
            try:
                url = values['url']
            except:
                url = ''
            try:
                meta = values['meta']
            except:
                meta = ''
            try:
                description = values['description']
            except:
                description = ''
            html += '<div class="Feed_item">'
            html += f'<a href="{url}" target="_blank" rel="noopener noreferrer" class="Feed_link">'
            html += f'<span class="Feed_meta">{meta}</span>'
            html += f'<span class="Feed_heading">{header}</span>'
            html += '</a>'
            html += f'<span class="Feed_description">{description}</span>'
            html += '</div>'
    html += '</div>'

html += '''
</div>
</div>
</body>
</html>
'''

with open('/var/www/html/index.html', 'w') as f:
    f.write(html)

Here's an example of what sites.yml could look like. Each category generates a new column with each site with it's data. You can have as many categories and site entries as you want. The python script will also ignore any lines starting with #.

self-hosted:
  thelounge:
    url: 'https://thelounge.chat/'
    meta: 'communication'
    description: 'A self-hosted IRC client'
  gitea:
    url: 'https://gitea.com/'
    meta: 'code'
    description: 'A self-hosted git server with web UI'
  bitwarden:
    url: 'https://github.com/dani-garcia/vaultwarden'
    meta: 'password'
    description: 'A self-hosted password manager'
  searx:
    url: 'https://searx.me/'
    meta: 'search'
    description: 'A self-hosted search engine'
  blog:
    url: 'https://cobalt-org.github.io/'
    meta: 'blog'
    description: 'My self-hosted blog'
  miniflux:
    url: 'https://miniflux.app/'
    meta: 'rss'
    description: 'My self-hosted RSS reader, also has my YT subscriptions'
personal:
  hackaday:
    url: 'https://hackaday.com'
    meta: 'electronics'
    description: 'Hackaday serves up Fresh Hacks Every Day from around the Internet'
  fedora magazine:
    url: 'https://fedoramagazine.org/'
    meta: 'linux'
    description: 'A magazine that published various tips about using Fedora'
shopping:
  ebay:
    url: 'https://ebay.com'
    meta: 'shopping'
    description: 'eBay'
  amazon:
    url: 'https://smile.amazon.com'
    meta: 'shopping'
    description: 'Amazon'
media:
  youtube:
    url: 'https://youtube.com'
    meta: 'video'
    description: 'YouTube'
  hulu:
    url: 'https://hulu.com'
    meta: 'video'
    description: 'Hulu'
  netflix:
    url: 'https://netflix.com'
    meta: 'video'
    description: 'Netflix'
work:
  intranet:
    url: 'http://intranet'
    meta: 'admin'
    description: 'Misc internal links'
  work email:
    url: 'https://outlook.office.com'
    meta: 'communication'
    description: 'Email but for work'
category:
  name:
    url: 'url here'
    meta: 'any tags you want'
    description: 'a brief description'

Here's my index.css, feel free to modify it how you want.

.jfk-bubble {
    filter: invert(100%) hue-rotate(180deg) contrast(90%) !important;
}
[data-darkreader-inline-bgcolor] {
  background-color: var(--darkreader-inline-bgcolor) !important;
}
[data-darkreader-inline-bgimage] {
  background-image: var(--darkreader-inline-bgimage) !important;
}
[data-darkreader-inline-border] {
  border-color: var(--darkreader-inline-border) !important;
}
[data-darkreader-inline-border-bottom] {
  border-bottom-color: var(--darkreader-inline-border-bottom) !important;
}
[data-darkreader-inline-border-left] {
  border-left-color: var(--darkreader-inline-border-left) !important;
}
[data-darkreader-inline-border-right] {
  border-right-color: var(--darkreader-inline-border-right) !important;
}
[data-darkreader-inline-border-top] {
  border-top-color: var(--darkreader-inline-border-top) !important;
}
[data-darkreader-inline-boxshadow] {
  box-shadow: var(--darkreader-inline-boxshadow) !important;
}
[data-darkreader-inline-color] {
  color: var(--darkreader-inline-color) !important;
}
[data-darkreader-inline-fill] {
  fill: var(--darkreader-inline-fill) !important;
}
[data-darkreader-inline-stroke] {
  stroke: var(--darkreader-inline-stroke) !important;
}
[data-darkreader-inline-outline] {
  outline-color: var(--darkreader-inline-outline) !important;
}
html {
    background-color: #181a1b !important;
}
html, body, input, textarea, select, button {
    background-color: #181a1b;
}
html, body, input, textarea, select, button {
    border-color: #575757;
    color: #e8e6e3;
}
a {
    color: #3391ff;
}
table {
    border-color: #4c4c4c;
}
::placeholder {
    color: #bab5ab;
}
::selection {
    background-color: #005ccc;
    color: #ffffff;
}
::-moz-selection {
    background-color: #005ccc;
    color: #ffffff;
}
input:-webkit-autofill,
textarea:-webkit-autofill,
select:-webkit-autofill {
    background-color: #545b00 !important;
    color: #e8e6e3 !important;
}
::-webkit-scrollbar {
    background-color: #1c1e1f;
    color: #c5c1b9;
}
::-webkit-scrollbar-thumb {
    background-color: #2a2c2e;
}
::-webkit-scrollbar-thumb:hover {
    background-color: #323537;
}
::-webkit-scrollbar-thumb:active {
    background-color: #3d4043;
}
::-webkit-scrollbar-corner {
    background-color: #181a1b;
}
* {
    scrollbar-color: #2a2c2e #1c1e1f;
}
:focus {
    outline: .1rem solid #24d1e7
}

html {
    font-size: 62.5%;
    font-family: "Fira Code", sans-serif;
    background: #282a36
}

body {
    font-size: 1.6em;
    overflow-x: hidden;
    margin: 0 auto;
    max-width: 120rem
}
body * {
    font-family: "Fira Code", sans-serif;
    font-size: 1.4rem;
    box-sizing: border-box
}
a {
    color: #24d1e7;
    text-decoration: none
}
img {
    max-width: 100%
}
::-webkit-input-placeholder {
    color: #f1f1f1
}
:-ms-input-placeholder {
    color: #f1f1f1
}
::-ms-input-placeholder {
    color: #f1f1f1
}
::placeholder {
    color: #f1f1f1
}
.Feed_root {
    flex: 0 1 33%;
    font-size: 1rem;
    overflow: hidden;
    text-overflow: ellipsis;
    padding: 2rem;
    margin-right: 5rem
}
.Feed_root:last-child {
    margin-right: 0
}
.Feed_title {
    font-size: 2rem;
    color: #bd99ff
}
.Feed_heading {
    overflow: hidden
}
.Feed_link:hover .Feed_heading {
    text-decoration: underline
}
.Feed_item {
    color: #fff;
    width: 100%;
    padding: .5rem 0 1rem;
    margin-bottom: 1rem
}
.Feed_description {
    margin-top: .5rem;
    font-size: 1.2rem;
    color: #fff;
    display: block
}
.Feed_meta {
    font-size: 1rem;
    color: #aaa;
    font-style: italic;
    display: block;
    margin-top: .4rem;
    text-decoration: none!important
}
.Feed_image {
    width: 100%;
    margin-top: 1rem
}
.vimvixen-hint {
    background-color: #7b5300 !important;
    border-color: #d8b013 !important;
    color: #f3e8c8 !important;
}
::placeholder {
    opacity: 0.5 !important;
}