Better CSS support (#583)

* Remove banner hardcoding

* Refactor "colors" to "stylesheet"

* Remove logo hardcoding

* Remove stylesheet hardcoding

* Add very basic static CSS scanning and a new style

* Respect environment settings

* Check if selected stylesheet still exists

* New theme and title formatting

* Revert migration change

* Code linting

* More outlines for Matrix style

* Change wording in settings

* Forgot this wording

* Add suggested changes
This commit is contained in:
extome 2023-11-08 21:33:03 -05:00 committed by GitHub
parent 6bc0111d0a
commit 7133d6b441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 154 additions and 48 deletions

View File

@ -162,11 +162,11 @@ class Command(BaseCommand):
for user in users: for user in users:
new_conf = UserConfig(user) new_conf = UserConfig(user)
colors_key = f"{user}:colors" stylesheet_key = f"{user}:color"
colors = redis.get_message(colors_key).get("status") stylesheet = redis.get_message(stylesheet_key).get("status")
if colors: if stylesheet:
new_conf.set_value("colors", colors) new_conf.set_value("stylesheet", stylesheet)
redis.del_message(colors_key) redis.del_message(stylesheet_key)
sort_by_key = f"{user}:sort_by" sort_by_key = f"{user}:sort_by"
sort_by = redis.get_message(sort_by_key).get("status") sort_by = redis.get_message(sort_by_key).get("status")

View File

@ -2,9 +2,12 @@
- hold all form classes used in the views - hold all form classes used in the views
""" """
import os
from django import forms from django import forms
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.forms.widgets import PasswordInput, TextInput from django.forms.widgets import PasswordInput, TextInput
from home.src.ta.helper import get_stylesheets
class CustomAuthForm(AuthenticationForm): class CustomAuthForm(AuthenticationForm):
@ -29,14 +32,16 @@ class CustomAuthForm(AuthenticationForm):
class UserSettingsForm(forms.Form): class UserSettingsForm(forms.Form):
"""user configurations values""" """user configurations values"""
CHOICES = [ STYLESHEET_CHOICES = [("", "-- change stylesheet --")]
("", "-- change color scheme --"), STYLESHEET_CHOICES.extend(
("dark", "Dark"), [
("light", "Light"), (stylesheet, os.path.splitext(stylesheet)[0].title())
] for stylesheet in get_stylesheets()
]
)
colors = forms.ChoiceField( stylesheet = forms.ChoiceField(
widget=forms.Select, choices=CHOICES, required=False widget=forms.Select, choices=STYLESHEET_CHOICES, required=False
) )
page_size = forms.IntegerField(required=False) page_size = forms.IntegerField(required=False)

View File

@ -12,6 +12,7 @@ from datetime import datetime
from urllib.parse import urlparse from urllib.parse import urlparse
import requests import requests
from home.src.ta.settings import EnvironmentSettings
def ignore_filelist(filelist: list[str]) -> list[str]: def ignore_filelist(filelist: list[str]) -> list[str]:
@ -203,3 +204,21 @@ def ta_host_parser(ta_host: str) -> tuple[list[str], list[str]]:
csrf_trusted_origins.append(f"{parsed.scheme}://{parsed.hostname}") csrf_trusted_origins.append(f"{parsed.scheme}://{parsed.hostname}")
return allowed_hosts, csrf_trusted_origins return allowed_hosts, csrf_trusted_origins
def get_stylesheets():
"""Get all valid stylesheets from /static/css"""
app_root = EnvironmentSettings.APP_DIR
stylesheets = os.listdir(os.path.join(app_root, "static/css"))
stylesheets.remove("style.css")
stylesheets.sort()
stylesheets = list(filter(lambda x: x.endswith(".css"), stylesheets))
return stylesheets
def check_stylesheet(stylesheet: str):
"""Check if a stylesheet exists. Return dark.css as a fallback"""
if stylesheet in get_stylesheets():
return stylesheet
return "dark.css"

View File

@ -7,12 +7,13 @@ Functionality:
from typing import TypedDict from typing import TypedDict
from home.src.es.connect import ElasticWrap from home.src.es.connect import ElasticWrap
from home.src.ta.helper import get_stylesheets
class UserConfigType(TypedDict, total=False): class UserConfigType(TypedDict, total=False):
"""describes the user configuration""" """describes the user configuration"""
colors: str stylesheet: str
page_size: int page_size: int
sort_by: str sort_by: str
sort_order: str sort_order: str
@ -31,7 +32,7 @@ class UserConfig:
"""Handle settings for an individual user""" """Handle settings for an individual user"""
_DEFAULT_USER_SETTINGS = UserConfigType( _DEFAULT_USER_SETTINGS = UserConfigType(
colors="dark", stylesheet="dark.css",
page_size=12, page_size=12,
sort_by="published", sort_by="published",
sort_order="desc", sort_order="desc",
@ -46,7 +47,7 @@ class UserConfig:
sponsorblock_id=None, sponsorblock_id=None,
) )
VALID_COLORS = ["dark", "light"] VALID_STYLESHEETS = get_stylesheets()
VALID_VIEW_STYLE = ["grid", "list"] VALID_VIEW_STYLE = ["grid", "list"]
VALID_SORT_ORDER = ["asc", "desc"] VALID_SORT_ORDER = ["asc", "desc"]
VALID_SORT_BY = ["published", "downloaded", "views", "likes"] VALID_SORT_BY = ["published", "downloaded", "views", "likes"]
@ -91,7 +92,7 @@ class UserConfig:
) )
valid_values = { valid_values = {
"colors": self.VALID_COLORS, "stylesheet": self.VALID_STYLESHEETS,
"sort_by": self.VALID_SORT_BY, "sort_by": self.VALID_SORT_BY,
"sort_order": self.VALID_SORT_ORDER, "sort_order": self.VALID_SORT_ORDER,
"view_style_home": self.VALID_VIEW_STYLE, "view_style_home": self.VALID_VIEW_STYLE,

View File

@ -23,11 +23,7 @@
{% else %} {% else %}
<title>TubeArchivist</title> <title>TubeArchivist</title>
{% endif %} {% endif %}
{% if colors == "dark" %} <link rel="stylesheet" href="{% static 'css/' %}{{ stylesheet }}">
<link rel="stylesheet" href="{% static 'css/dark.css' %}">
{% else %}
<link rel="stylesheet" href="{% static 'css/light.css' %}">
{% endif %}
<script type="text/javascript" src="{% static 'script.js' %}"></script> <script type="text/javascript" src="{% static 'script.js' %}"></script>
{% if cast %} {% if cast %}
<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script> <script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
@ -39,12 +35,7 @@
<div class="boxed-content"> <div class="boxed-content">
<div class="top-banner"> <div class="top-banner">
<a href="{% url 'home' %}"> <a href="{% url 'home' %}">
{% if colors == 'dark' %} <img alt="tube-archivist-banner">
<img src="{% static 'img/banner-tube-archivist-dark.png' %}" alt="tube-archivist-banner">
{% endif %}
{% if colors == 'light' %}
<img src="{% static 'img/banner-tube-archivist-light.png' %}" alt="tube-archivist-banner">
{% endif %}
</a> </a>
</div> </div>
<div class="top-nav"> <div class="top-nav">

View File

@ -18,20 +18,11 @@
<meta name="msapplication-TileColor" content="#01202e"> <meta name="msapplication-TileColor" content="#01202e">
<meta name="msapplication-config" content="{% static 'favicon/browserconfig.xml' %}"> <meta name="msapplication-config" content="{% static 'favicon/browserconfig.xml' %}">
<meta name="theme-color" content="#01202e"> <meta name="theme-color" content="#01202e">
{% if colors == "dark" %} <link rel="stylesheet" href="{% static 'css/' %}{{ stylesheet }}">
<link rel="stylesheet" href="{% static 'css/dark.css' %}">
{% else %}
<link rel="stylesheet" href="{% static 'css/light.css' %}">
{% endif %}
</head> </head>
<body> <body>
<div class="boxed-content login-page"> <div class="boxed-content login-page">
{% if colors == 'dark' %} <img alt="tube-archivist-logo">
<img src="{% static 'img/logo-tube-archivist-dark.png' %}" alt="tube-archivist-logo">
{% endif %}
{% if colors == 'light' %}
<img src="{% static 'img/logo-tube-archivist-light.png' %}" alt="tube-archivist-banner">
{% endif %}
<h1>Tube Archivist</h1> <h1>Tube Archivist</h1>
<h2>Your Self Hosted YouTube Media Server</h2> <h2>Your Self Hosted YouTube Media Server</h2>
{% if form_error %} {% if form_error %}

View File

@ -7,11 +7,11 @@
<form action="{% url 'settings_user' %}" method="POST" name="user-update"> <form action="{% url 'settings_user' %}" method="POST" name="user-update">
{% csrf_token %} {% csrf_token %}
<div class="settings-group"> <div class="settings-group">
<h2>Color scheme</h2> <h2>Stylesheet</h2>
<div class="settings-item"> <div class="settings-item">
<p>Current color scheme: <span class="settings-current">{{ colors }}</span></p> <p>Current stylesheet: <span class="settings-current">{{ stylesheet }}</span></p>
<i>Select your preferred color scheme between dark and light mode.</i><br> <i>Select your preferred stylesheet.</i><br>
{{ user_form.colors }} {{ user_form.stylesheet }}
</div> </div>
</div> </div>
<div class="settings-group"> <div class="settings-group">

View File

@ -39,7 +39,7 @@ from home.src.index.playlist import YoutubePlaylist
from home.src.index.reindex import ReindexProgress from home.src.index.reindex import ReindexProgress
from home.src.index.video_constants import VideoTypeEnum from home.src.index.video_constants import VideoTypeEnum
from home.src.ta.config import AppConfig, ReleaseVersion, ScheduleBuilder from home.src.ta.config import AppConfig, ReleaseVersion, ScheduleBuilder
from home.src.ta.helper import time_parser from home.src.ta.helper import check_stylesheet, time_parser
from home.src.ta.settings import EnvironmentSettings from home.src.ta.settings import EnvironmentSettings
from home.src.ta.ta_redis import RedisArchivist from home.src.ta.ta_redis import RedisArchivist
from home.src.ta.users import UserConfig from home.src.ta.users import UserConfig
@ -73,7 +73,9 @@ class ArchivistViewConfig(View):
self.user_conf = UserConfig(self.user_id) self.user_conf = UserConfig(self.user_id)
self.context = { self.context = {
"colors": self.user_conf.get_value("colors"), "stylesheet": check_stylesheet(
self.user_conf.get_value("stylesheet")
),
"cast": EnvironmentSettings.ENABLE_CAST, "cast": EnvironmentSettings.ENABLE_CAST,
"sort_by": self.user_conf.get_value("sort_by"), "sort_by": self.user_conf.get_value("sort_by"),
"sort_order": self.user_conf.get_value("sort_order"), "sort_order": self.user_conf.get_value("sort_order"),
@ -220,7 +222,9 @@ class MinView(View):
def get_min_context(request): def get_min_context(request):
"""build minimal vars for context""" """build minimal vars for context"""
return { return {
"colors": UserConfig(request.user.id).get_value("colors"), "stylesheet": check_stylesheet(
UserConfig(request.user.id).get_value("stylesheet")
),
"version": settings.TA_VERSION, "version": settings.TA_VERSION,
"ta_update": ReleaseVersion().get_update(), "ta_update": ReleaseVersion().get_update(),
} }
@ -977,9 +981,9 @@ class SettingsUserView(MinView):
config_handler = UserConfig(request.user.id) config_handler = UserConfig(request.user.id)
if user_form.is_valid(): if user_form.is_valid():
user_form_post = user_form.cleaned_data user_form_post = user_form.cleaned_data
if user_form_post.get("colors"): if user_form_post.get("stylesheet"):
config_handler.set_value( config_handler.set_value(
"colors", user_form_post.get("colors") "stylesheet", user_form_post.get("stylesheet")
) )
if user_form_post.get("page_size"): if user_form_post.get("page_size"):
config_handler.set_value( config_handler.set_value(

View File

@ -9,4 +9,6 @@
--accent-font-light: #97d4c8; --accent-font-light: #97d4c8;
--img-filter: invert(50%) sepia(9%) saturate(2940%) hue-rotate(122deg) brightness(94%) contrast(90%); --img-filter: invert(50%) sepia(9%) saturate(2940%) hue-rotate(122deg) brightness(94%) contrast(90%);
--img-filter-error: invert(16%) sepia(60%) saturate(3717%) hue-rotate(349deg) brightness(86%) contrast(120%); --img-filter-error: invert(16%) sepia(60%) saturate(3717%) hue-rotate(349deg) brightness(86%) contrast(120%);
--banner: url("../img/banner-tube-archivist-dark.png");
--logo: url("../img/logo-tube-archivist-dark.png");
} }

View File

@ -9,4 +9,6 @@
--accent-font-light: #35b399; --accent-font-light: #35b399;
--img-filter: invert(50%) sepia(9%) saturate(2940%) hue-rotate(122deg) brightness(94%) contrast(90%); --img-filter: invert(50%) sepia(9%) saturate(2940%) hue-rotate(122deg) brightness(94%) contrast(90%);
--img-filter-error: invert(16%) sepia(60%) saturate(3717%) hue-rotate(349deg) brightness(86%) contrast(120%); --img-filter-error: invert(16%) sepia(60%) saturate(3717%) hue-rotate(349deg) brightness(86%) contrast(120%);
--banner: url("../img/banner-tube-archivist-light.png");
--logo: url("../img/logo-tube-archivist-light.png");
} }

View File

@ -0,0 +1,75 @@
:root {
--main-bg: #000000;
--highlight-bg: #080808;
--highlight-error: #880000;
--highlight-error-light: #aa0000;
--highlight-bg-transparent: #0c0c0caf;
--main-font: #00aa00;
--accent-font-dark: #007700;
--accent-font-light: #00aa00;
--img-filter: invert(50%) sepia(9%) saturate(2940%) hue-rotate(122deg) brightness(94%) contrast(90%);
--img-filter-error: invert(16%) sepia(60%) saturate(3717%) hue-rotate(349deg) brightness(86%) contrast(120%);
--banner: url("../img/banner-tube-archivist-dark.png");
--logo: url("../img/logo-tube-archivist-dark.png");
--outline: 1px solid green;
--filter: hue-rotate(310deg);
}
.settings-group {
outline: var(--outline);
}
.info-box-item {
outline: var(--outline);
}
.footer {
outline: var(--outline);
}
.nav-icons {
filter: var(--filter);
}
.view-icons {
filter: var(--filter);
}
.top-banner {
filter: var(--filter);
}
.icon-text {
outline: var(--outline);
}
.video-item {
outline: var(--outline);
}
.channel-banner {
outline: var(--outline);
}
.description-box {
outline: var(--outline);
}
.video-player {
outline: var(--outline);
}
#notification {
outline: var(--outline);
}
textarea {
background-color: var(--highlight-bg);
outline: var(--outline);
color: var(--main-font);
}
input {
background-color: var(--highlight-bg);
color: var(--main-font);
}

View File

@ -0,0 +1,14 @@
:root {
--main-bg: #000000;
--highlight-bg: #0c0c0c;
--highlight-error: #220000;
--highlight-error-light: #330000;
--highlight-bg-transparent: #0c0c0caf;
--main-font: #888888;
--accent-font-dark: #555555;
--accent-font-light: #999999;
--img-filter: invert(50%) sepia(9%) saturate(2940%) hue-rotate(122deg) brightness(94%) contrast(90%);
--img-filter-error: invert(16%) sepia(60%) saturate(3717%) hue-rotate(349deg) brightness(86%) contrast(120%);
--banner: url("../img/banner-tube-archivist-dark.png");
--logo: url("../img/logo-tube-archivist-dark.png");
}

View File

@ -165,12 +165,13 @@ button:hover {
.top-banner img { .top-banner img {
width: 100%; width: 100%;
max-width: 700px; max-width: 700px;
content: var(--banner);
} }
.footer { .footer {
margin: 0; margin: 0;
padding: 20px 0; padding: 20px 0;
background-color: var(--accent-font-dark); background-color: var(--highlight-bg);
grid-row-start: 2; grid-row-start: 2;
grid-row-end: 3; grid-row-end: 3;
} }
@ -725,6 +726,7 @@ video:-webkit-full-screen {
max-width: 200px; max-width: 200px;
max-height: 200px; max-height: 200px;
margin-bottom: 40px; margin-bottom: 40px;
content: var(--logo);
} }
.login-page form { .login-page form {