Add themes and thumbnail quality selections

Moved colors out of style.css to default.css. This file can be copied
and modified to create custom themes. Any file other than style.css and
mobile.css within the css directory will be selectable as a theme in the
conf menu.

Thumbnail quality is also configurable.
This commit is contained in:
John Mertz 2023-03-01 23:16:29 -05:00
parent 8cf6fde5ca
commit cd2e0e61ce
Signed by: jpm
GPG Key ID: E9C5EA2D867501AB
4 changed files with 616 additions and 35 deletions

104
www/css/default.css Executable file
View File

@ -0,0 +1,104 @@
/*
* This is the built-in 'default' theme. If you would like to make your own
* custom theme, you should make a copy of this file, apply the changes you
* want to that copy, then select that copy by name in the settings to apply.
*/
body {
background: #323232;
color: #ccc;
font-family: Arial, sans-serif;
}
#debug {
background: #000;
color: #f00;
border-color: #f00;
}
#debug button, #notice button {
background: #b00;
color: #000;
}
a {
color: #ccc;
}
div#error {
background: #b00;
}
div#dismiss {
background: #ccc;
color: #000;
}
div#title {
background: #000;
}
div#title h1 {
color: #b00;
}
div#subs {
background: #323232;
border-color: #b00;
}
div#subs img {
border-color: #fff;
}
div.row {
background: #000;
}
.mark {
background: #b00;
}
.unmark {
background: #00bb00;
}
div.time h4 {
background: #000;
}
.category, .no-category, .unsubscribe, .solo, .no-solo, .regex, .no-regex {
border-color: solid 2px #fff;
}
.category a, .no-category a, .unsubscribe a, .solo a, .no-solo a, .regex a, .no-regex a {
color: #fff;
}
.unsubscribe {
background: #bb0000;
}
.no-category, .no-solo, .no-regex {
background: #323232;
}
.category, .solo, .regex {
background: #00bb00;
}
.menu {
color: #000;
background: #cdcdcd;
}
.menu button, div.button {
background: #bb0000;
color: #fff;
}
.menu select {
background: #fff;
color: #000;
}
submit {
background: #bb0000;
}
#footer {
background: #000;
}
#footer a {
color: #bb0000;
}
#timeout {
color: #bb0000;
}
#copied {
background: rgba(255,31,31,0.5);
color: #fff;
}
.web, .embed, .clip {
background: #000;
}
img.tab-focused {
background: #CDCDCD;
}

123
www/css/mobile.css Executable file
View File

@ -0,0 +1,123 @@
div#header {
font-size: 25px;
}
div#header img {
height: 40px;
}
div#subs img {
height: 120px;
border-radius: 100px;
}
div#subs ul {
height: 160px;
}
div.thumb, div.details {
display: inline-block;
}
div.details {
width: 100%;
padding-left: 0px;
}
div.details a {
height: 100%;
}
div.playmodes {
display: inline-flex;
position: relative;
height: 50px;
width: 100%;
}
div.playmodes a {
background-color: #323232;
width: 25%;
}
div.playmodes img, div.playmodes span {
width: 100%;
height: 50px;
margin: 0px;
}
div.playmodes div {
height: 50px;
margin: 0px;
padding: 0px;
}
div.seen {
width: 100%;
}
div.seen h3 {
font-size: 32px;
padding-top: 7px;
position: absolute;
width: 100%;
}
div.thumb img {
display: block;
height: 226px;
}
div.time {
top: 178px;
}
div.row {
display: inline-grid;
}
div.video_body, div.seen, div.playmodes {
display: inline-flex;
}
.upload, .views {
position: relative;
bottom: 0px;
}
.views {
float: right;
}
div#title h1 {
line-height: 60px;
}
div.title {
position: relative;
padding-left: 10px;
top: 10px;
}
div.title img {
height: 80px;
border-radius: 60px;
}
.category, .no-category, .unsubscribe, .solo, .no-solo, .regex, .no-regex {
position: relative;
border: solid 2px;
border-radius: 40px;
height: 50px;
width: 50px;
text-align: center;
cursor: pointer;
}
.category a, .no-category a, .unsubscribe a, .solo a, .no-solo a, .regex a, .no-regex a {
line-height: 50px;
font-size: 35px;
}
.no-category, .category {
top: -160px;
left: -5px;
}
.unsubscribe {
top: -136px;
left: 75px;
}
.solo, .no-solo {
top: -190px;
left: -5px;
}
.regex, .no-regex {
top: -215px;
left: 75px;
}
.menu {
display: none;
}
.menu button, div.button, .menu select, .menu input {
font-size: 17px;
}
.menu button, div.button {
padding: 5px;
border: none;
margin: 5px;

300
www/css/style.css Executable file
View File

@ -0,0 +1,300 @@
/*
* This file contains structural CSS which should not be modified.
*/
body {
width: 100%;
border-collapse: collapse;
padding: 0px;
margin: 0px;
overflow-x: hidden;
height: 100%
}
#debug, #notice, #error {
margin: 10px;
padding: 10px;
border: solid 2px;
}
#debug ul, #notice ul, #error ul {
position: relative;
left: 16px;
}
#debug li, #notice li, #error li {
line-height: 24px;
}
#debug button, #notice button, #error button {
margin-top: 10px;
padding: 10px;
border: none;
font-size: 16px;
}
h1, h2, h3, h4, ul {
margin: 0px;
padding: 0px;
}
h4 {
margin-top: 10px;
}
#notice, #error {
display: block;
}
#notice p, #error p {
padding-top: 0px;
text-align: left;
}
#dismiss {
float: right;
position: relative;
margin: 0px;
padding: 8px;
cursor: pointer;
}
a {
text-decoration: none;
}
div#header {
text-align: center;
width: 100%;
}
div#title h1 {
line-height: 50px;
vertical-align: top;
}
div#subs {
width: 100%;
border-bottom: solid 5px;
}
div#subs img {
height: 78px;
border: solid 3px;
border-radius: 42px;
}
div#subs ul {
width: 100%;
list-style: none;
display: inline-flex;
overflow-x: scroll;
overflow-y: hidden;
}
div#subs li {
padding: 10px 0px 10px 10px;
height: 80px;
}
div.row {
position: relative;
margin: 10px;
margin-bottom: 0px;
display: inline-flex;
width: -moz-available;
width: -webkit-fill-available;
}
div.video_body, div.seen, div.playmodes {
display: inline-block;
position: relative;
height: 100%;
}
.video_body a {
display: block;
}
.playmodes div {
height: 32px;
}
div.video_body {
text-align: left;
float: left;
width: -moz-available;
width: -webkit-fill-available;
}
div.details img {
height: 40px;
border-radius: 20px;
float: left;
margin-right: 10px;
}
div.seen {
display: table;
width: 32px;
}
div.playmodes {
display: table-column;
height: 100%;
}
.mark {
display: table-cell;
text-align: center;
vertical-align: middle;
}
.unmark {
display: table-cell;
text-align: center;
font-size: 36px;
padding-top: 14px;
vertical-align: middle;
}
div.seen h3 {
position: relative;
height: 100%;
width: 32px;
line-height: 100%;
display: table-cell;
}
div.thumb, div.details {
vertical-align: top;
display: table-cell;
padding: 10px;
z-index: 20;
}
div.thumb {
padding: 0px 10px 0px 10px;
position: relative;
overflow-y: hidden;
}
div.thumb img {
display: block;
padding-right: 0px;
height: 126px;
}
div.time {
position: absolute;
top: 78px;
right: 0px;
}
div.time h4 {
padding: 2px;
line-height: 20px;
margin-right: 10px;
}
#title h1, #title img {
display: inline-block;
cursor: pointer;
}
#title img {
float: right;
padding: 10px 5px;
height: 30px;
}
.category, .no-category, .unsubscribe, .solo, .no-solo, .regex, .no-regex {
position: relative;
border: solid 2px;
border-radius: 16px;
height: 20px;
width: 20px;
text-align: center;
cursor: pointer;
}
.category a, .no-category a, .unsubscribe a, .solo a, .no-solo a, .regex a, .no-regex a {
line-height: 20px;
}
.no-category, .category {
top: -74px;
left: 0px;
}
.unsubscribe {
top: -90px;
left: 64px;
}
.solo, .no-solo {
top: -114px;
left: 0px;
}
.regex, .no-regex {
top: -98px;
left: 64px;
}
.menu {
display: none;
}
.menu button, div.button, .menu select, .menu input {
font-size: 17px;
}
.menu button, div.button {
padding: 5px;
border: none;
margin: 5px;
}
div.button {
display: inline-block;
}
.menu select {
border: none;
}
form {
margin: 0px;
min-height: 34px;
}
submit {
padding: 5px;
margin-bottom: 0px;
line-height: 34px;
}
div#videos {
margin-bottom: auto;
}
#footer {
position: relative;
bottom: 0px;
margin-top: 10px;
width: 100%;
}
#footer p {
text-align: center;
margin: 0px;
padding: 10px 10px 0px 10px;
}
#footer a {
line-height: 34px;
font-size: 24px;
}
#copyright {
padding-left: 10px;
}
#source {
float: right;
padding-right: 10px;
}
#copied {
width: 100%;
height: 100%;
position: absolute;
display: table;
top: 0px;
left: 0px;
text-align: center;
line-height: 100%;
z-index: 30;
}
#copied h3 {
vertical-align: middle;
display: table-cell;
}
#clipboard {
position: absolute;
top: -1000px;
}
.views {
right: 10px;
}
.upload, .views {
position: absolute;
bottom: 10px;
}
.web, .embed, .clip {
padding: 5px;
border: none;
}
.menu input[type="checkbox"] {
margin: 8px;
}
.menu input[type="textbox"], .menu select {
padding: 2px;
margin: 3px 3px 5px 3px;;
min-width: 250px;
}
.menu ul {
max-width: 250px;
margin: auto;
}
.menu li {
list-style: none;
}

View File

@ -234,7 +234,7 @@ function print_video($video,$settings,$mobile,$show_seen)
$html .= "
$default" . '
<div class="thumb" id="thumbnail:' . $video['videoId'] . '">
<img src="https://i.ytimg.com/vi/' . $video['videoId'] . '/hqdefault.jpg"/>
<img src="https://i.ytimg.com/vi/' . $video['videoId'] . '/' . ($settings['thumbnail_quality'] ? $settings['thumbnail_quality'] : 'default') . '.jpg"/>
<div class="time">
<h4>' . $video['lengthText'] . '</h4>
</div>
@ -603,30 +603,6 @@ if ( !$no_db && $reload && $permissions['writable'] ) {
$target = remove_arg($target,'refresh');
}
# Update the default player
if (isset($args['player'])) {
if (preg_match("/^(clip|embed|web)$/",urldecode($args['player']))) {
$player = sql_escape(urldecode($args['player']));
$handle->query("UPDATE settings SET player = '$player'");
$debug_text .= "<p>Set player $player</p>";
} else {
error("<p>" . $args['player'] . " is not a valid player option</p>");
}
$target = remove_arg($target,'player');
}
# Update the embedded player type
if (isset($args['embed_type'])) {
if (preg_match("/^(embed|nocookie|proxy)$/",urldecode($args['embed_type']))) {
$embed_type = sql_escape(urldecode($args['embed_type']));
$handle->query("UPDATE settings SET embed_type = '$embed_type'");
$debug_text .= "<p>Set embed_type $embed_type</p>";
} else {
error("<p>" . $args['embed_type'] . " is not a valid embed_type option</p>");
}
$target = remove_arg($target,'embed_type');
}
# Update the theme
if (isset($args['theme'])) {
$found = 0;
@ -645,6 +621,46 @@ if ( !$no_db && $reload && $permissions['writable'] ) {
$target = remove_arg($target,'theme');
}
# Update the thumbnail quality
if (isset($args['thumbnail_quality'])) {
if (preg_match("/^(maxresdefault|sddefault|hqdefault|mqdefault|default|1|2|3)$/",urldecode($args['thumbnail_quality']))) {
$thumbnail_quality = sql_escape(urldecode($args['thumbnail_quality']));
if (!isset($thumbnail_quality) || $thumbnail_quality == '') {
$thumbnail_quality = 'default';
}
$thumbnail_quality = sql_escape(urldecode($args['thumbnail_quality']));
$handle->query("UPDATE settings SET thumbnail_quality = '$thumbnail_quality'");
$debug_text .= "<p>Set thumbnail_quality $thumbnail_quality</p>";
} else {
error("<p>" . $args['thumbnail_quality'] . " is not a valid thumbnail_quality option</p>");
}
$target = remove_arg($target,'thumbnail_quality');
}
# Update the embedded player type
if (isset($args['embed_type'])) {
if (preg_match("/^(embed|nocookie|proxy)$/",urldecode($args['embed_type']))) {
$embed_type = sql_escape(urldecode($args['embed_type']));
$handle->query("UPDATE settings SET embed_type = '$embed_type'");
$debug_text .= "<p>Set embed_type $embed_type</p>";
} else {
error("<p>" . $args['embed_type'] . " is not a valid embed_type option</p>");
}
$target = remove_arg($target,'embed_type');
}
# Update the default player
if (isset($args['player'])) {
if (preg_match("/^(clip|embed|web)$/",urldecode($args['player']))) {
$player = sql_escape(urldecode($args['player']));
$handle->query("UPDATE settings SET player = '$player'");
$debug_text .= "<p>Set player $player</p>";
} else {
error("<p>" . $args['player'] . " is not a valid player option</p>");
}
$target = remove_arg($target,'player');
}
# Subscribe to a new channel
if (isset($args['sub'])) {
$debug_text .= "<p>Adding " . urldecode($args['sub']) . "...</p>";
@ -873,10 +889,10 @@ $head .= '
var img = document.getElementById(id+\'-img\');
if (div.style.display == \'block\') {
div.style.display = \'none\';
img.style.background = \'rgba(0,0,0,0)\';
img.classList.remove(\'tab-focused\');;
} else {
div.style.display = \'block\';
img.style.background = \'#cdcdcd\';
img.classList.add(\'tab-focused\');;
}
}
// For menues, specifically hide other menues
@ -884,7 +900,7 @@ $head .= '
var div = document.getElementById(id);
var img = document.getElementById(id+\'-img\');
div.style.display = \'none\';
img.style.background = \'#000\';
img.classList.remove(\'tab-focused\');;
}
// Assume that the page has timed out unless \'elapsed\' is disabled by the end of the page
var elapsed = 1;
@ -1087,6 +1103,9 @@ if (isset($permissions['readable']) && $permissions['readable']) {
<li>Application Theme<select name="theme" autocomplete="off">';
foreach (glob($root.'/css/*.css') as $theme) {
$theme = preg_replace('/.*\/([^\/]*)\.css$/','\1',$theme);
if ($theme == 'mobile') {
next;
}
if ($theme != 'style') {
$header .= '<option value="' . $theme . '"';
if ($settings['theme'] == $theme) {
@ -1096,19 +1115,40 @@ if (isset($permissions['readable']) && $permissions['readable']) {
}
}
$header .= '</select></li>
<li>Default Player Type<select name="player" autocomplete="off"><option value="embed"';
if ($settings['player'] == 'embed' || $settings['player'] == 'nocookie' || $settings['player'] == 'proxy') {
<li>Thumbnails<br/><small>* letterboxed</small><select name="thumbnail_quality" autocomplete="off"><option value="maxresdefault"';
if ($settings['thumbnail_quality'] == 'maxres' || $settings['thumbnail_quality'] == 'maxresdefault') {
$header .= ' selected="selected"';
}
$header .= '>Embedded Player</option><option value="web"';
if ($settings['player'] == 'web') {
$header .= '>HD (1280x720)</option><option value="sddefault"';
if ($settings['thumbnail_quality'] == 'sddefault') {
$header .= ' selected="selected"';
}
$header .= '>Regular Website</option><option value="clip"';
if ($settings['player'] == 'clip') {
$header .= '>SD (640x480*)</option><option value="hqdefault"';
if ($settings['thumbnail_quality'] == 'high' || $settings['thumbnail_quality'] == 'hqdefault') {
$header .= ' selected="selected"';
}
$header .= '>Copy to Clipboard</option></select></li>
$header .= '>High (480x360*)</option><option value="mqdefault"';
if ($settings['thumbnail_quality'] == 'medium' || $settings['thumbnail_quality'] == 'mqdefault') {
$header .= ' selected="selected"';
}
$header .= '>Medium (320x180*)</option><option value="default"';
if ($settings['thumbnail_quality'] == 'low' || $settings['thumbnail_quality'] == 'default') {
$header .= ' selected="selected"';
}
$header .= '>Low (120x90*)</option><option value="1"';
if ($settings['thumbnail_quality'] == 'start' || $settings['thumbnail_quality'] == '1') {
$header .= ' selected="selected"';
}
$header .= '>Start (120x90*)</option><option value="2"';
if ($settings['thumbnail_quality'] == 'middle' || $settings['thumbnail_quality'] == '2') {
$header .= ' selected="selected"';
}
$header .= '>Middle (120x90*)</option><option value="3"';
if ($settings['thumbnail_quality'] == 'end' || $settings['thumbnail_quality'] == '3') {
$header .= ' selected="selected"';
}
$header .= '>End (120x90*)</option>';
$header .= '</select></li>
<li>Embedded Player Type<select name="embed_type" autocomplete="off"><option value="embed"';
if ($settings['embed_type'] == 'embed') {
$header .= ' selected="selected"';
@ -1122,6 +1162,20 @@ if (isset($permissions['readable']) && $permissions['readable']) {
$header .= ' selected="selected"';
}
$header .= '>Proxy (DuckDuckGo)</option></select></li>
</select></li>
<li>Default Player Type<select name="player" autocomplete="off"><option value="embed"';
if ($settings['player'] == 'embed' || $settings['player'] == 'nocookie' || $settings['player'] == 'proxy') {
$header .= ' selected="selected"';
}
$header .= '>Embedded Player</option><option value="web"';
if ($settings['player'] == 'web') {
$header .= ' selected="selected"';
}
$header .= '>Regular Website</option><option value="clip"';
if ($settings['player'] == 'clip') {
$header .= ' selected="selected"';
}
$header .= '>Copy to Clipboard</option></select></li>
</ul>
<button type="submit">Save</button>
</form>';