mirror of
https://github.com/iv-org/invidious.git
synced 2025-12-21 07:29:10 +00:00
Updated styling, formatting, structure of frontend
This commit is contained in:
9
assets/css/animation.css
Normal file
9
assets/css/animation.css
Normal file
@@ -0,0 +1,9 @@
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,18 +10,18 @@
|
||||
}
|
||||
|
||||
.watch-on-invidious {
|
||||
font-size: 1.3em !important;
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
margin: 0 1em 0 1em !important;
|
||||
margin: 0 1em;
|
||||
order: 3;
|
||||
}
|
||||
/**/
|
||||
/* .watch-on-invidious > a { */
|
||||
/* color: white; */
|
||||
/* } */
|
||||
|
||||
.watch-on-invidious > a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.watch-on-invidious > a:hover,
|
||||
.watch-on-invidious > a:focus {
|
||||
color: rgba(0, 182, 240, 1);;
|
||||
}
|
||||
/* .watch-on-invidious > a:hover, */
|
||||
/* .watch-on-invidious > a:focus { */
|
||||
/* color: ; */
|
||||
/* } */
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
|
||||
/* Widget covers the whole page */
|
||||
#search-widget {
|
||||
text-align: center;
|
||||
margin: 20vh 0 50px 0;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
grid-gap: var(--gap);
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#logo > h1 {
|
||||
font-size: 3.5em;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
.search-homepage {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1500px) and (max-height: 1000px) {
|
||||
#logo > h1 {
|
||||
font-size: 10vmin;
|
||||
}
|
||||
#search-widget > h1 {
|
||||
align-self: flex-end;
|
||||
font-size: 3em;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.searchbar {
|
||||
/* reset pointer events for interactive components */
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
@@ -1,92 +1,106 @@
|
||||
.video-js {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* Youtube player style */
|
||||
.video-js.player-style-youtube .vjs-progress-control {
|
||||
height: 0;
|
||||
}
|
||||
/* .video-js.player-style-youtube .vjs-progress-control { */
|
||||
/* height: 0; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, */
|
||||
/* .video-js.player-style-youtube .vjs-progress-control { */
|
||||
/* position: absolute; */
|
||||
/* right: 0; */
|
||||
/* left: 0; */
|
||||
/* width: 100%; */
|
||||
/* margin: 0; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-control-bar { */
|
||||
/* background: linear-gradient(rgb(0 0 0 / 10%), rgba(0 0 0 / 50%)); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-slider { */
|
||||
/* background-color: rgb(255 255 255 / 20%); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-load-progress > div { */
|
||||
/* background-color: rgb(255 255 255 / 20%); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-play-progress { */
|
||||
/* background-color: var(--accent-bg-color); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube */
|
||||
/* .vjs-progress-control:hover */
|
||||
/* .vjs-progress-holder { */
|
||||
/* font-size: 1em; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-control-bar > .vjs-spacer { */
|
||||
/* flex: 1; */
|
||||
/* order: 2; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-play-progress .vjs-time-tooltip { */
|
||||
/* display: none; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-play-progress::before { */
|
||||
/* color: var(--accent-bg-color); */
|
||||
/* font-size: 0.85em; */
|
||||
/* display: none; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube */
|
||||
/* .vjs-progress-holder:hover */
|
||||
/* .vjs-play-progress::before { */
|
||||
/* display: unset; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-control-bar { */
|
||||
/* display: flex; */
|
||||
/* flex-direction: row; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-big-play-button { */
|
||||
/* /* */
|
||||
/* Styles copied from video-js.min.css, definition of */
|
||||
/* .vjs-big-play-centered .vjs-big-play-button */
|
||||
/* */
|
||||
*/
|
||||
/* top: 50%; */
|
||||
/* left: 50%; */
|
||||
/* margin-top: -0.8167em; */
|
||||
/* margin-left: -1.5em; */
|
||||
/* } */
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
/* .video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu { */
|
||||
/* margin-bottom: 2em; */
|
||||
/* padding-top: 2em; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, */
|
||||
/* .video-js.player-style-youtube .vjs-progress-control { */
|
||||
/* height: 0.3em; */
|
||||
/* margin-bottom: 0.6em; */
|
||||
/* } */
|
||||
/**/
|
||||
/* ul.vjs-menu-content::-webkit-scrollbar { */
|
||||
/* display: none; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .vjs-user-inactive { */
|
||||
/* cursor: none; */
|
||||
/* } */
|
||||
|
||||
.video-js.player-style-youtube .vjs-control-bar {
|
||||
background: linear-gradient(rgba(0,0,0,0.1), rgba(0, 0, 0,0.5));
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-slider {
|
||||
background-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-load-progress > div {
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-play-progress {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-control:hover .vjs-progress-holder {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-control-bar > .vjs-spacer {
|
||||
flex: 1;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-play-progress .vjs-time-tooltip {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-play-progress::before {
|
||||
color: red;
|
||||
font-size: 0.85em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-holder:hover .vjs-play-progress::before {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-control-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-big-play-button {
|
||||
/*
|
||||
Styles copied from video-js.min.css, definition of
|
||||
.vjs-big-play-centered .vjs-big-play-button
|
||||
*/
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -0.81666em;
|
||||
margin-left: -1.5em;
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu {
|
||||
margin-bottom: 2em;
|
||||
padding-top: 2em
|
||||
}
|
||||
|
||||
.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px;
|
||||
margin-bottom: 10px;}
|
||||
|
||||
ul.vjs-menu-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vjs-user-inactive {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.video-js .vjs-text-track-display > div > div > div {
|
||||
background-color: rgba(0, 0, 0, 0.75) !important;
|
||||
border-radius: 9px !important;
|
||||
padding: 5px !important;
|
||||
}
|
||||
/* .video-js .vjs-text-track-display > div > div > div { */
|
||||
/* background-color: rgb(0 0 0 / 75%); */
|
||||
/* border-radius: 0.5em; */
|
||||
/* padding: 0.3em; */
|
||||
/* } */
|
||||
|
||||
.vjs-play-control,
|
||||
.vjs-volume-panel,
|
||||
@@ -123,143 +137,252 @@ ul.vjs-menu-content::-webkit-scrollbar {
|
||||
order: 7;
|
||||
}
|
||||
|
||||
.vjs-playback-rate > .vjs-menu {
|
||||
width: 50px;
|
||||
/* .vjs-playback-rate > .vjs-menu { */
|
||||
/* width: 3.2em; */
|
||||
/* } */
|
||||
/**/
|
||||
|
||||
/* Make the video relative, instead of absolute, so that
|
||||
the parent container will size based on the video. Also,
|
||||
note the max-height rule. Note the line-height 0 is to prevent
|
||||
a small artifact on the bottom of the video.
|
||||
https://stackoverflow.com/questions/46747320/limit-the-height-in-videojs-in-fluid-mode/47039499#47039499
|
||||
*/
|
||||
.video-js.vjs-fluid,
|
||||
.video-js.vjs-16-9,
|
||||
.video-js.vjs-4-3,
|
||||
video.video-js,
|
||||
video.vjs-tech {
|
||||
max-height: 85vh;
|
||||
position: relative !important;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-width: 100% !important;
|
||||
padding-top: 0 !important;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.vjs-control-bar {
|
||||
.video-js.vjs-16-9 {
|
||||
/* Keep the video spaced to fit */
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.video-js.vjs-4-3 {
|
||||
/* Keep the video spaced to fit */
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
|
||||
#player {
|
||||
/* Default to 16/9 video spacing */
|
||||
aspect-ratio: 16 / 9;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.vjs-error .vjs-error-display:before,
|
||||
.video-js .vjs-volume-tooltip,
|
||||
.video-js .vjs-time-tooltip,
|
||||
.vjs-menu .vjs-menu-content {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
||||
background-color: var(--secondary-bg-color-dark);
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-menu-item:focus,
|
||||
.vjs-menu li.vjs-menu-item:hover,
|
||||
.js-focus-visible .vjs-menu li.vjs-menu-item:hover {
|
||||
background-color: var(--accent-bg-color-dark);
|
||||
}
|
||||
|
||||
.video-js .vjs-slider {
|
||||
background-color: rgb(166 166 166 / 50%);
|
||||
}
|
||||
|
||||
.video-js .vjs-load-progress {
|
||||
background-color: rgb(227 227 227 / 75%);
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
#player {
|
||||
/* Default to 16/9 video spacing */
|
||||
aspect-ratio: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-fullscreen video {
|
||||
max-height: 100vh !important;
|
||||
}
|
||||
|
||||
.video-js .vjs-control-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
scrollbar-width: none;
|
||||
/* Fix the control bar due to us resetting the line-height on the video-js */
|
||||
line-height: 1;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent 0%,
|
||||
var(--secondary-bg-color-dark) 50%
|
||||
);
|
||||
}
|
||||
|
||||
.vjs-control-bar button:hover,
|
||||
.vjs-control-bar button:focus {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.vjs-control-bar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.video-js .vjs-icon-cog {
|
||||
font-size: 18px;
|
||||
.vjs-playback-rate .vjs-playback-rate-value {
|
||||
font-size: 1em;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.video-js .vjs-control-bar,
|
||||
.vjs-menu-button-popup .vjs-menu .vjs-menu-content {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
.vjs-button > .vjs-icon-placeholder::before {
|
||||
font-size: 1.25em;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-menu-item:focus,
|
||||
.vjs-menu li.vjs-menu-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
color: rgba(49, 49, 51, 0.75);
|
||||
.vjs-menu li {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.vjs-menu li.vjs-selected,
|
||||
.vjs-menu li.vjs-selected:focus,
|
||||
.vjs-menu li.vjs-selected:hover {
|
||||
background-color: rgba(0, 182, 240, 0.75);
|
||||
.video-js .vjs-control {
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
/* Progress Bar */
|
||||
.video-js .vjs-slider {
|
||||
background-color: rgba(15, 15, 15, 0.5);
|
||||
.video-js .vjs-time-control {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.video-js .vjs-load-progress,
|
||||
.video-js .vjs-load-progress div {
|
||||
background: rgba(87, 87, 88, 1);
|
||||
.vjs-poster {
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.video-js .vjs-share__short-link-wrapper {
|
||||
color: var(--fg-color-dark);
|
||||
background-color: var(--secondary-bg-color-dark);
|
||||
height: 2em;
|
||||
margin: 0 auto;
|
||||
margin-bottom: var(--secondary-gap);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.video-js .vjs-share__short-link {
|
||||
padding: var(--secondary-gap);
|
||||
background-color: var(--secondary-bg-color-dark);
|
||||
color: var(--fg-color-dark);
|
||||
font-family: var(--monospace);
|
||||
min-width: 15em;
|
||||
}
|
||||
|
||||
.video-js .vjs-share__btn {
|
||||
background-color: var(--secondary-bg-color-dark);
|
||||
color: var(--fg-color-dark);
|
||||
height: 2em;
|
||||
width: 2.25em;
|
||||
padding: var(--secondary-gap);
|
||||
}
|
||||
|
||||
.video-js .vjs-share__title {
|
||||
font-size: 1.25em;
|
||||
color: var(--fg-color-dark);
|
||||
}
|
||||
|
||||
.video-js .vjs-share__subtitle {
|
||||
font-size: 1em;
|
||||
color: var(--fg-color-dark);
|
||||
margin: 0 auto var(--secondary-gap);
|
||||
}
|
||||
|
||||
.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button {
|
||||
height: var(--gap);
|
||||
}
|
||||
|
||||
.video-js.vjs-videojs-share_open .vjs-modal-dialog .vjs-close-button::before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
.video-js .vjs-control.vjs-close-button .vjs-icon-placeholder::before {
|
||||
line-height: 1.67;
|
||||
}
|
||||
|
||||
.video-js .vjs-share__middle {
|
||||
padding: 0 var(--secondary-gap);
|
||||
}
|
||||
|
||||
.vjs-modal-dialog .vjs-modal-dialog-content {
|
||||
font-size: 1em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
/**/
|
||||
/* .video-js .vjs-icon-cog { */
|
||||
/* font-size: 1em; */
|
||||
/* } */
|
||||
/**/
|
||||
|
||||
/* .video-js .vjs-control-bar, */
|
||||
/* .vjs-menu-button-popup .vjs-menu .vjs-menu-content { */
|
||||
/* background-color: rgb(35 35 35 / 75%); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .vjs-menu li.vjs-menu-item:focus, */
|
||||
/* .vjs-menu li.vjs-menu-item:hover { */
|
||||
/* background-color: rgb(255 255 255 / 75%); */
|
||||
/* color: rgb(49 49 51 / 75%); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .vjs-menu li.vjs-selected, */
|
||||
/* .vjs-menu li.vjs-selected:focus, */
|
||||
/* .vjs-menu li.vjs-selected:hover { */
|
||||
/* background-color: rgb(0 182 240 / 75%); */
|
||||
/* } */
|
||||
/**/
|
||||
/* /* Progress Bar */
|
||||
*/
|
||||
/* .video-js .vjs-slider { */
|
||||
/* background-color: rgb(15 15 15 / 50%); */
|
||||
/* } */
|
||||
/**/
|
||||
/* .video-js .vjs-load-progress, */
|
||||
/* .video-js .vjs-load-progress div { */
|
||||
/* background: rgb(87 87 88); */
|
||||
/* } */
|
||||
/**/
|
||||
.video-js .vjs-slider:hover,
|
||||
.video-js button:hover {
|
||||
color: rgba(0, 182, 240, 1);
|
||||
background-color: var(--accent-bg-color);
|
||||
}
|
||||
|
||||
.video-js.player-style-invidious .vjs-play-progress {
|
||||
background-color: rgba(0, 182, 240, 1);
|
||||
background-color: var(--accent-bg-color);
|
||||
}
|
||||
|
||||
/* Overlay */
|
||||
.video-js .vjs-overlay {
|
||||
background-color: rgba(35, 35, 35, 0.75) !important;
|
||||
}
|
||||
.video-js .vjs-overlay * {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
text-align: center;
|
||||
.vjs-modal-dialog .vjs-modal-dialog-content {
|
||||
padding: var(--gap);
|
||||
}
|
||||
|
||||
/* ProgressBar marker */
|
||||
.vjs-marker {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* Big "Play" Button */
|
||||
.video-js .vjs-big-play-button {
|
||||
background-color: rgba(35, 35, 35, 0.5);
|
||||
font-size: var(--gap);
|
||||
line-height: var(--gap);
|
||||
height: var(--gap);
|
||||
width: calc(var(--gap) * 2);
|
||||
top: var(--secondary-gap);
|
||||
left: var(--secondary-gap);
|
||||
background-color: var(--secondary-bg-color);
|
||||
border: none;
|
||||
opacity: 0.7;
|
||||
transition: opacity 240ms;
|
||||
}
|
||||
|
||||
.video-js:hover .vjs-big-play-button {
|
||||
background-color: rgba(35, 35, 35, 0.75);
|
||||
.video-js .vjs-big-play-button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.video-js .vjs-current-time,
|
||||
.video-js .vjs-time-divider,
|
||||
.video-js .vjs-duration {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.video-js .vjs-time-divider {
|
||||
min-width: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.video-js .vjs-poster {
|
||||
background-size: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.player-dimensions.vjs-fluid {
|
||||
padding-top: 82vh;
|
||||
}
|
||||
|
||||
video.video-js {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#player-container {
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
padding-bottom: 82vh;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.mobile-operations-bar {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 1px !important;
|
||||
left: initial !important;
|
||||
width: initial !important;
|
||||
}
|
||||
|
||||
.mobile-operations-bar ul {
|
||||
position: absolute !important;
|
||||
bottom: unset !important;
|
||||
top: 1.5em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.video-js .vjs-share {
|
||||
justify-content: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 650px) {
|
||||
.vjs-modal-dialog-content {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.vjs-control-bar {
|
||||
background-color: var(--secondary-bg-color-dark);
|
||||
}
|
||||
|
||||
73
assets/css/pure-fix.css
Normal file
73
assets/css/pure-fix.css
Normal file
@@ -0,0 +1,73 @@
|
||||
/** fixes for pure to support our colors */
|
||||
.pure-form input[type="color"],
|
||||
.pure-form input[type="date"],
|
||||
.pure-form input[type="datetime-local"],
|
||||
.pure-form input[type="datetime"],
|
||||
.pure-form input[type="email"],
|
||||
.pure-form input[type="month"],
|
||||
.pure-form input[type="number"],
|
||||
.pure-form input[type="password"],
|
||||
.pure-form input[type="search"],
|
||||
.pure-form input[type="tel"],
|
||||
.pure-form input[type="text"],
|
||||
.pure-form input[type="time"],
|
||||
.pure-form input[type="url"],
|
||||
.pure-form input[type="week"],
|
||||
.pure-form select,
|
||||
.pure-form textarea {
|
||||
font-size: inherit;
|
||||
padding: var(--secondary-gap);
|
||||
color: var(--fg-color);
|
||||
background-color: var(--secondary-bg-color);
|
||||
border: 1px solid var(--secondary-bg-color);
|
||||
box-shadow: unset;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.pure-menu-heading,
|
||||
.pure-g,
|
||||
.pure-g [class*="pure-u"] {
|
||||
font-family: inherit;
|
||||
letter-spacing: initial;
|
||||
}
|
||||
|
||||
.pure-form legend {
|
||||
color: var(--fg-color);
|
||||
border-bottom: 1px solid var(--accent-color);
|
||||
}
|
||||
|
||||
legend {
|
||||
border: initial;
|
||||
padding: initial;
|
||||
}
|
||||
|
||||
.pure-button {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
color: currentcolor;
|
||||
border: none;
|
||||
border: none transparent;
|
||||
background-color: transparent;
|
||||
text-decoration: none;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.pure-button-hover,
|
||||
.pure-button:focus,
|
||||
.pure-button:hover {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* Wider settings name to less word wrap */
|
||||
.pure-form-aligned .pure-control-group label {
|
||||
width: 19em;
|
||||
}
|
||||
|
||||
.pure-menu-heading {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
a:active, a:hover {
|
||||
outline: inherit;
|
||||
}
|
||||
@@ -1,121 +1,111 @@
|
||||
summary {
|
||||
/* This should hide the marker */
|
||||
display: block;
|
||||
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
margin: 0 auto 10px auto;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: 1.17em;
|
||||
margin: 0 auto 0.625em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
summary::-webkit-details-marker,
|
||||
summary::marker { display: none; }
|
||||
|
||||
summary:before {
|
||||
border-radius: 5px;
|
||||
content: "[ + ]";
|
||||
margin: -2px 10px 0 10px;
|
||||
padding: 1px 0 3px 0;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
summary::marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
details[open] > summary:before { content: "[ − ]"; }
|
||||
|
||||
|
||||
#filters-box {
|
||||
padding: 10px 20px 20px 10px;
|
||||
margin: 10px 15px;
|
||||
summary::before {
|
||||
content: "+ ";
|
||||
text-align: center;
|
||||
}
|
||||
#filters-flex {
|
||||
|
||||
details[open] > summary::before {
|
||||
content: "− ";
|
||||
}
|
||||
|
||||
.filters {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(12em, 1fr));
|
||||
grid-gap: var(--secondary-gap);
|
||||
margin: var(--gap) 0;
|
||||
}
|
||||
|
||||
.filters fieldset {
|
||||
display: grid;
|
||||
grid-gap: var(--secondary-gap);
|
||||
align-content: baseline;
|
||||
}
|
||||
|
||||
.filters fieldset div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: var(--secondary-gap);
|
||||
}
|
||||
|
||||
|
||||
fieldset, legend {
|
||||
display: contents !important;
|
||||
border: none !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.filter-column {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
width: max-content;
|
||||
min-width: max-content;
|
||||
max-width: 16em;
|
||||
margin: 15px;
|
||||
flex-grow: 2;
|
||||
flex-basis: auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
.filter-name, .filter-options {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
margin: 0;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.filter-options div { margin: 6px 0; }
|
||||
.filter-options div * { vertical-align: middle; }
|
||||
.filter-options label { margin: 0 10px; }
|
||||
|
||||
|
||||
#filters-apply {
|
||||
text-align: right; /* IE11 only */
|
||||
text-align: end; /* Override for compatible browsers */
|
||||
}
|
||||
/* #filters-box { */
|
||||
/* background-color: var(--secondary-bg-color); */
|
||||
/* } */
|
||||
/**/
|
||||
/* #filters-flex { */
|
||||
/* display: flex; */
|
||||
/* flex-flow: row wrap; */
|
||||
/* align-items: flex-start; */
|
||||
/* place-content: flex-start flex-start; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .filter-column { */
|
||||
/* display: inline-block; */
|
||||
/* display: inline-flex; */
|
||||
/* width: max-content; */
|
||||
/* min-width: max-content; */
|
||||
/* max-width: 16em; */
|
||||
/* margin: 0.9375rem; */
|
||||
/* flex-grow: 2; */
|
||||
/* flex-basis: auto; */
|
||||
/* flex-direction: column; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .filter-name, */
|
||||
/* .filter-options { */
|
||||
/* display: block; */
|
||||
/* padding: 0.3125rem 0.625rem; */
|
||||
/* margin: 0; */
|
||||
/* text-align: start; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .filter-options div { */
|
||||
/* margin: 0.375rem 0; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .filter-options div * { */
|
||||
/* vertical-align: middle; */
|
||||
/* } */
|
||||
/**/
|
||||
/* .filter-options label { */
|
||||
/* margin: 0 0.625rem; */
|
||||
/* } */
|
||||
/**/
|
||||
/* #filters-apply { */
|
||||
/* text-align: right; /* IE11 only */ */
|
||||
/* text-align: end; /* Override for compatible browsers */ */
|
||||
/* } */
|
||||
|
||||
/* Error message */
|
||||
|
||||
.no-results-error {
|
||||
text-align: center;
|
||||
line-height: 180%;
|
||||
font-size: 110%;
|
||||
padding: 15px 15px 125px 15px;
|
||||
text-align: center;
|
||||
font-size: 1.1em;
|
||||
padding: 1em 1em 8em;
|
||||
}
|
||||
|
||||
/* Responsive rules */
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
summary { font-size: 1.30em; }
|
||||
#filters-box {
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
#filters-apply {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Light theme */
|
||||
|
||||
.light-theme #filters-box {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.no-theme #filters-box {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
|
||||
.dark-theme #filters-box {
|
||||
background: #373737;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.no-theme #filters-box {
|
||||
background: #373737;
|
||||
}
|
||||
}
|
||||
/* @media only screen and (max-width: 50px) { */
|
||||
/* summary { */
|
||||
/* font-size: 1.3em; */
|
||||
/* } */
|
||||
/**/
|
||||
/* #filters-box { */
|
||||
/* margin: 0.6em 0 0; */
|
||||
/* padding: 0; */
|
||||
/* } */
|
||||
/**/
|
||||
/* #filters-apply { */
|
||||
/* text-align: center; */
|
||||
/* padding: 1em; */
|
||||
/* } */
|
||||
/* } */
|
||||
/**/
|
||||
|
||||
16
assets/css/theme-catppuccin-latte.css
Normal file
16
assets/css/theme-catppuccin-latte.css
Normal file
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--fg-color-dark: #f8f8f2;
|
||||
--bg-color-dark: #eff1f5;
|
||||
--accent-color-dark: #fff;
|
||||
--accent-bg-color-dark: #181926;
|
||||
--secondary-color-dark: #e3e3e3;
|
||||
--secondary-bg-color-dark: #494d64;
|
||||
|
||||
/* light theme colors */
|
||||
--fg-color-light: #4c4f69;
|
||||
--bg-color-light: #eff1f5;
|
||||
--accent-color-light: #bcc0cc;
|
||||
--accent-bg-color-light: #7287fd;
|
||||
--secondary-color-light: #5c5f77;
|
||||
--secondary-bg-color-light: #e6e9ef;
|
||||
}
|
||||
16
assets/css/theme-catppuccin-macchiato.css
Normal file
16
assets/css/theme-catppuccin-macchiato.css
Normal file
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--fg-color-dark: #f8f8f2;
|
||||
--bg-color-dark: #24273a;
|
||||
--accent-color-dark: #fff;
|
||||
--accent-bg-color-dark: #181926;
|
||||
--secondary-color-dark: #e3e3e3;
|
||||
--secondary-bg-color-dark: #494d64;
|
||||
|
||||
/* light theme colors */
|
||||
--fg-color-light: black;
|
||||
--bg-color-light: #eee;
|
||||
--accent-color-light: #3a3a3a;
|
||||
--accent-bg-color-light: #008bec;
|
||||
--secondary-color-light: #424242;
|
||||
--secondary-bg-color-light: #d9d9d9;
|
||||
}
|
||||
16
assets/css/theme-dracula.css
Normal file
16
assets/css/theme-dracula.css
Normal file
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--fg-color-dark: #f8f8f2;
|
||||
--bg-color-dark: #282a36;
|
||||
--accent-color-dark: #fff;
|
||||
--accent-bg-color-dark: #44475a;
|
||||
--secondary-color-dark: #e3e3e3;
|
||||
--secondary-bg-color-dark: #21222C;
|
||||
|
||||
/* light theme colors */
|
||||
--fg-color-light: black;
|
||||
--bg-color-light: #eee;
|
||||
--accent-color-light: #3a3a3a;
|
||||
--accent-bg-color-light: #008bec;
|
||||
--secondary-color-light: #424242;
|
||||
--secondary-bg-color-light: #d9d9d9;
|
||||
}
|
||||
16
assets/css/theme-tokyonight.css
Normal file
16
assets/css/theme-tokyonight.css
Normal file
@@ -0,0 +1,16 @@
|
||||
:root {
|
||||
--fg-color-dark: #f8f8f2;
|
||||
--bg-color-dark: #1a1b26;
|
||||
--accent-color-dark: #fff;
|
||||
--accent-bg-color-dark: #3e4f80;
|
||||
--secondary-color-dark: #e3e3e3;
|
||||
--secondary-bg-color-dark: #2e2e3e;
|
||||
|
||||
/* light theme colors */
|
||||
--fg-color-light: black;
|
||||
--bg-color-light: #eee;
|
||||
--accent-color-light: #3a3a3a;
|
||||
--accent-bg-color-light: #008bec;
|
||||
--secondary-color-light: #424242;
|
||||
--secondary-bg-color-light: #d9d9d9;
|
||||
}
|
||||
59
assets/css/theme.css
Normal file
59
assets/css/theme.css
Normal file
@@ -0,0 +1,59 @@
|
||||
:root {
|
||||
--fg-color-dark: #f0f0f0;
|
||||
--bg-color-dark: #131313;
|
||||
--accent-color-dark: #27a6ff;
|
||||
--accent-bg-color-dark: #004a7e;
|
||||
--secondary-color-dark: #e3e3e3;
|
||||
--secondary-bg-color-dark: #313131;
|
||||
--watched-overlay-color-dark: rgb(0 0 0 / 40%);
|
||||
|
||||
/* light theme colors */
|
||||
--fg-color-light: black;
|
||||
--bg-color-light: #eee;
|
||||
--accent-color-light: #044c99;
|
||||
--accent-bg-color-light: #3eaefd;
|
||||
--secondary-color-light: #404040;
|
||||
--secondary-bg-color-light: #dbdbdb;
|
||||
--watched-overlay-color-light: rgb(255 255 255 / 40%);
|
||||
|
||||
/** apply default colors to dark */
|
||||
--fg-color: var(--fg-color-dark);
|
||||
--bg-color: var(--bg-color-dark);
|
||||
--accent-color: var(--accent-color-dark);
|
||||
--accent-bg-color: var(--accent-bg-color-dark);
|
||||
--secondary-color: var(--secondary-color-dark);
|
||||
--secondary-bg-color: var(--secondary-bg-color-dark);
|
||||
--watched-overlay-color: var(--watched-overlay-color-dark);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--fg-color: var(--fg-color-light);
|
||||
--bg-color: var(--bg-color-light);
|
||||
--accent-color: var(--accent-color-light);
|
||||
--accent-bg-color: var(--accent-bg-color-light);
|
||||
--secondary-color: var(--secondary-color-light);
|
||||
--secondary-bg-color: var(--secondary-bg-color-light);
|
||||
--watched-overlay-color: var(--watched-overlay-color-dark);
|
||||
}
|
||||
}
|
||||
|
||||
.light-theme {
|
||||
--fg-color: var(--fg-color-light);
|
||||
--bg-color: var(--bg-color-light);
|
||||
--accent-color: var(--accent-color-light);
|
||||
--accent-bg-color: var(--accent-bg-color-light);
|
||||
--secondary-color: var(--secondary-color-light);
|
||||
--secondary-bg-color: var(--secondary-bg-color-light);
|
||||
--watched-overlay-color: var(--watched-overlay-color-light);
|
||||
}
|
||||
|
||||
.dark-theme {
|
||||
--fg-color: var(--fg-color-dark);
|
||||
--bg-color: var(--bg-color-dark);
|
||||
--accent-color: var(--accent-color-dark);
|
||||
--accent-bg-color: var(--accent-bg-color-dark);
|
||||
--secondary-color: var(--secondary-color-dark);
|
||||
--secondary-bg-color: var(--secondary-bg-color-dark);
|
||||
--watched-overlay-color: var(--watched-overlay-color-dark);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
|
||||
var spinnerHTML = '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
|
||||
var spinnerHTML = '<div class="loading"><i class="icon ion-ios-refresh"></i></div>';
|
||||
var spinnerHTMLwithHR = spinnerHTML + '<hr>';
|
||||
|
||||
String.prototype.supplant = function (o) {
|
||||
@@ -11,14 +11,14 @@ String.prototype.supplant = function (o) {
|
||||
};
|
||||
|
||||
function toggle_comments(event) {
|
||||
var target = event.target;
|
||||
var body = target.parentNode.parentNode.parentNode.children[1];
|
||||
if (body.style.display === 'none') {
|
||||
target.textContent = '[ − ]';
|
||||
body.style.display = '';
|
||||
const target = event.target;
|
||||
const comments = document.querySelector(".comments");
|
||||
if (comments.style.display === 'none') {
|
||||
target.textContent = '−';
|
||||
comments.style.display = '';
|
||||
} else {
|
||||
target.textContent = '[ + ]';
|
||||
body.style.display = 'none';
|
||||
target.textContent = '+';
|
||||
comments.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ function hide_youtube_replies(event) {
|
||||
|
||||
function show_youtube_replies(event) {
|
||||
var target = event.target;
|
||||
console.log(target);
|
||||
|
||||
var sub_text = target.getAttribute('data-inner-text');
|
||||
var inner_text = target.getAttribute('data-sub-text');
|
||||
@@ -75,23 +76,24 @@ function get_youtube_comments() {
|
||||
helpers.xhr('GET', url, {retries: 5, entity_name: 'comments'}, {
|
||||
on200: function (response) {
|
||||
var commentInnerHtml = ' \
|
||||
<div> \
|
||||
<h3> \
|
||||
<a href="javascript:void(0)">[ − ]</a> \
|
||||
<nav class="comments-header"> \
|
||||
<ul> \
|
||||
<li> \
|
||||
<button class="secondary" id="toggle-comments">−</button> \
|
||||
{commentsText} \
|
||||
</h3> \
|
||||
<b> \
|
||||
'
|
||||
</li> \
|
||||
\
|
||||
<li>'
|
||||
if (video_data.support_reddit) {
|
||||
commentInnerHtml += ' <a href="javascript:void(0)" data-comments="reddit"> \
|
||||
commentInnerHtml += ' <button data-comments="reddit"> \
|
||||
{redditComments} \
|
||||
</a> \
|
||||
</button> \
|
||||
'
|
||||
}
|
||||
commentInnerHtml += ' </b> \
|
||||
</div> \
|
||||
<div>{contentHtml}</div> \
|
||||
<hr>'
|
||||
commentInnerHtml += ' </li> \
|
||||
</ul> \
|
||||
</nav> \
|
||||
<div class="comments">{contentHtml}</div>'
|
||||
commentInnerHtml = commentInnerHtml.supplant({
|
||||
contentHtml: response.contentHtml,
|
||||
redditComments: video_data.reddit_comments_text,
|
||||
@@ -104,9 +106,9 @@ function get_youtube_comments() {
|
||||
})
|
||||
});
|
||||
comments.innerHTML = commentInnerHtml;
|
||||
comments.children[0].children[0].children[0].onclick = toggle_comments;
|
||||
document.getElementById("toggle-comments").onclick = toggle_comments;
|
||||
if (video_data.support_reddit) {
|
||||
comments.children[0].children[1].children[0].onclick = swap_comments;
|
||||
comments.children[1].children[1].onclick = swap_comments;
|
||||
}
|
||||
},
|
||||
onNon200: onNon200, // declared above
|
||||
@@ -122,7 +124,7 @@ function get_youtube_comments() {
|
||||
function get_youtube_replies(target, load_more, load_replies) {
|
||||
var continuation = target.getAttribute('data-continuation');
|
||||
|
||||
var body = target.parentNode.parentNode;
|
||||
var body = target.parentNode;
|
||||
var fallback = body.innerHTML;
|
||||
body.innerHTML = spinnerHTML;
|
||||
var baseUrl = video_data.base_url || '/api/v1/comments/'+ video_data.id
|
||||
@@ -140,26 +142,24 @@ function get_youtube_replies(target, load_more, load_replies) {
|
||||
helpers.xhr('GET', url, {}, {
|
||||
on200: function (response) {
|
||||
if (load_more) {
|
||||
body = body.parentNode.parentNode;
|
||||
body = body.parentNode;
|
||||
body.removeChild(body.lastElementChild);
|
||||
body.insertAdjacentHTML('beforeend', response.contentHtml);
|
||||
} else {
|
||||
body.removeChild(body.lastElementChild);
|
||||
|
||||
var p = document.createElement('p');
|
||||
var a = document.createElement('a');
|
||||
p.appendChild(a);
|
||||
var div = document.createElement('div');
|
||||
var button = document.createElement('button');
|
||||
div.appendChild(button);
|
||||
|
||||
a.href = 'javascript:void(0)';
|
||||
a.onclick = hide_youtube_replies;
|
||||
a.setAttribute('data-sub-text', video_data.hide_replies_text);
|
||||
a.setAttribute('data-inner-text', video_data.show_replies_text);
|
||||
a.textContent = video_data.hide_replies_text;
|
||||
button.onclick = hide_youtube_replies;
|
||||
button.setAttribute('data-sub-text', video_data.hide_replies_text);
|
||||
button.setAttribute('data-inner-text', video_data.show_replies_text);
|
||||
button.textContent = video_data.hide_replies_text;
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = response.contentHtml;
|
||||
|
||||
body.appendChild(p);
|
||||
body.appendChild(div);
|
||||
}
|
||||
},
|
||||
@@ -171,4 +171,4 @@ function get_youtube_replies(target, load_more, load_replies) {
|
||||
body.innerHTML = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +58,13 @@
|
||||
el.onclick = function () { mark_unwatched(el); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="add_playlist_video"]').forEach(function (el) {
|
||||
el.onclick = function () { add_playlist_video(el); };
|
||||
el.onclick = function (e) { add_playlist_video(e); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="add_playlist_item"]').forEach(function (el) {
|
||||
el.onclick = function () { add_playlist_item(el); };
|
||||
el.onclick = function (e) { add_playlist_item(e); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="remove_playlist_item"]').forEach(function (el) {
|
||||
el.onclick = function () { remove_playlist_item(el); };
|
||||
el.onclick = function (e) { remove_playlist_item(e); };
|
||||
});
|
||||
document.querySelectorAll('[data-onclick="revoke_token"]').forEach(function (el) {
|
||||
el.onclick = function () { revoke_token(el); };
|
||||
|
||||
@@ -1,131 +1,197 @@
|
||||
'use strict';
|
||||
var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
|
||||
"use strict";
|
||||
var notification_data = JSON.parse(
|
||||
document.getElementById("notification_data").textContent,
|
||||
);
|
||||
|
||||
/** Boolean meaning 'some tab have stream' */
|
||||
const STORAGE_KEY_STREAM = 'stream';
|
||||
const STORAGE_KEY_STREAM = "stream";
|
||||
/** Number of notifications. May be increased or reset */
|
||||
const STORAGE_KEY_NOTIF_COUNT = 'notification_count';
|
||||
const STORAGE_KEY_NOTIF_COUNT = "notification_count";
|
||||
|
||||
var notifications, delivered;
|
||||
var notifications_mock = { close: function () { } };
|
||||
var notifications_mock = { close: function () {} };
|
||||
|
||||
function get_subscriptions() {
|
||||
helpers.xhr('GET', '/api/v1/auth/subscriptions', {
|
||||
async function get_subscriptions_call() {
|
||||
return new Promise((resolve) => {
|
||||
helpers.xhr(
|
||||
"GET",
|
||||
"/api/v1/auth/subscriptions",
|
||||
{
|
||||
retries: 5,
|
||||
entity_name: 'subscriptions'
|
||||
}, {
|
||||
on200: create_notification_stream
|
||||
});
|
||||
entity_name: "subscriptions",
|
||||
},
|
||||
{
|
||||
on200: function (subscriptions) {
|
||||
create_notification_stream(subscriptions);
|
||||
resolve(subscriptions);
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Start the retry mechanism
|
||||
const get_subscriptions = exponential_backoff(get_subscriptions_call, 100, 1000);
|
||||
|
||||
function create_notification_stream(subscriptions) {
|
||||
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
|
||||
notifications = new SSE(
|
||||
'/api/v1/auth/notifications', {
|
||||
withCredentials: true,
|
||||
payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
delivered = [];
|
||||
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
|
||||
notifications = new SSE("/api/v1/auth/notifications", {
|
||||
withCredentials: true,
|
||||
payload:
|
||||
"topics=" +
|
||||
subscriptions
|
||||
.map(function (subscription) {
|
||||
return subscription.authorId;
|
||||
})
|
||||
.join(","),
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
});
|
||||
delivered = [];
|
||||
|
||||
var start_time = Math.round(new Date() / 1000);
|
||||
var start_time = Math.round(new Date() / 1000);
|
||||
|
||||
notifications.onmessage = function (event) {
|
||||
if (!event.id) return;
|
||||
notifications.onmessage = function (event) {
|
||||
if (!event.id) return;
|
||||
|
||||
var notification = JSON.parse(event.data);
|
||||
console.info('Got notification:', notification);
|
||||
var notification = JSON.parse(event.data);
|
||||
console.info("Got notification:", notification);
|
||||
|
||||
// Ignore not actual and delivered notifications
|
||||
if (start_time > notification.published || delivered.includes(notification.videoId)) return;
|
||||
// Ignore not actual and delivered notifications
|
||||
if (
|
||||
start_time > notification.published ||
|
||||
delivered.includes(notification.videoId)
|
||||
)
|
||||
return;
|
||||
|
||||
delivered.push(notification.videoId);
|
||||
delivered.push(notification.videoId);
|
||||
|
||||
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
|
||||
notification_count++;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
|
||||
notification_count++;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
|
||||
update_ticker_count();
|
||||
update_ticker_count();
|
||||
|
||||
// permission for notifications handled on settings page. JS handler is in handlers.js
|
||||
if (window.Notification && Notification.permission === 'granted') {
|
||||
var notification_text = notification.liveNow ? notification_data.live_now_text : notification_data.upload_text;
|
||||
notification_text = notification_text.replace('`x`', notification.author);
|
||||
// permission for notifications handled on settings page. JS handler is in handlers.js
|
||||
if (window.Notification && Notification.permission === "granted") {
|
||||
var notification_text = notification.liveNow
|
||||
? notification_data.live_now_text
|
||||
: notification_data.upload_text;
|
||||
notification_text = notification_text.replace("`x`", notification.author);
|
||||
|
||||
var system_notification = new Notification(notification_text, {
|
||||
body: notification.title,
|
||||
icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
|
||||
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname
|
||||
});
|
||||
var system_notification = new Notification(notification_text, {
|
||||
body: notification.title,
|
||||
icon: "/ggpht" + new URL(notification.authorThumbnails[2].url).pathname,
|
||||
img: "/ggpht" + new URL(notification.authorThumbnails[4].url).pathname,
|
||||
});
|
||||
|
||||
system_notification.onclick = function (e) {
|
||||
open('/watch?v=' + notification.videoId, '_blank');
|
||||
};
|
||||
}
|
||||
};
|
||||
system_notification.onclick = function (e) {
|
||||
open("/watch?v=" + notification.videoId, "_blank");
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
notifications.addEventListener('error', function (e) {
|
||||
console.warn('Something went wrong with notifications, trying to reconnect...');
|
||||
notifications = notifications_mock;
|
||||
setTimeout(get_subscriptions, 1000);
|
||||
});
|
||||
notifications.addEventListener("error", function (e) {
|
||||
console.warn(
|
||||
"Something went wrong with notifications, trying to reconnect...",
|
||||
);
|
||||
notifications = notifications_mock;
|
||||
|
||||
notifications.stream();
|
||||
});
|
||||
|
||||
notifications.stream();
|
||||
}
|
||||
|
||||
function update_ticker_count() {
|
||||
var notification_ticker = document.getElementById('notification_ticker');
|
||||
var notification_ticker = document.getElementById("notification_ticker");
|
||||
|
||||
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
|
||||
if (notification_count > 0) {
|
||||
notification_ticker.innerHTML =
|
||||
'<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';
|
||||
} else {
|
||||
notification_ticker.innerHTML =
|
||||
'<i class="icon ion-ios-notifications-outline"></i>';
|
||||
}
|
||||
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
|
||||
if (notification_count > 0) {
|
||||
notification_ticker.innerHTML =
|
||||
'<span id="notification_count">' +
|
||||
notification_count +
|
||||
'</span> <i class="icon ion-ios-notifications"></i>';
|
||||
} else {
|
||||
notification_ticker.innerHTML =
|
||||
'<i class="icon ion-ios-notifications-outline"></i>';
|
||||
}
|
||||
}
|
||||
|
||||
function start_stream_if_needed() {
|
||||
// random wait for other tabs set 'stream' flag
|
||||
setTimeout(function () {
|
||||
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
// if no one set 'stream', set it by yourself and start stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
notifications = notifications_mock;
|
||||
get_subscriptions();
|
||||
}
|
||||
}, Math.random() * 1000 + 50); // [0.050 .. 1.050) second
|
||||
// random wait for other tabs set 'stream' flag
|
||||
setTimeout(
|
||||
function () {
|
||||
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
// if no one set 'stream', set it by yourself and start stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
notifications = notifications_mock;
|
||||
get_subscriptions();
|
||||
}
|
||||
},
|
||||
Math.random() * 1000 + 50,
|
||||
); // [0.050 .. 1.050) second
|
||||
}
|
||||
|
||||
addEventListener("storage", function (e) {
|
||||
if (e.key === STORAGE_KEY_NOTIF_COUNT) update_ticker_count();
|
||||
|
||||
addEventListener('storage', function (e) {
|
||||
if (e.key === STORAGE_KEY_NOTIF_COUNT)
|
||||
update_ticker_count();
|
||||
|
||||
// if 'stream' key was removed
|
||||
if (e.key === STORAGE_KEY_STREAM && !helpers.storage.get(STORAGE_KEY_STREAM)) {
|
||||
if (notifications) {
|
||||
// restore it if we have active stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
} else {
|
||||
start_stream_if_needed();
|
||||
}
|
||||
// if 'stream' key was removed
|
||||
if (
|
||||
e.key === STORAGE_KEY_STREAM &&
|
||||
!helpers.storage.get(STORAGE_KEY_STREAM)
|
||||
) {
|
||||
if (notifications) {
|
||||
// restore it if we have active stream
|
||||
helpers.storage.set(STORAGE_KEY_STREAM, true);
|
||||
} else {
|
||||
start_stream_if_needed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener('load', function () {
|
||||
var notification_count_el = document.getElementById('notification_count');
|
||||
var notification_count = notification_count_el ? parseInt(notification_count_el.textContent) : 0;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
addEventListener("load", function () {
|
||||
var notification_count_el = document.getElementById("notification_count");
|
||||
var notification_count = notification_count_el
|
||||
? parseInt(notification_count_el.textContent)
|
||||
: 0;
|
||||
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
|
||||
|
||||
if (helpers.storage.get(STORAGE_KEY_STREAM))
|
||||
helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
start_stream_if_needed();
|
||||
if (helpers.storage.get(STORAGE_KEY_STREAM))
|
||||
helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
start_stream_if_needed();
|
||||
});
|
||||
|
||||
addEventListener('unload', function () {
|
||||
// let chance to other tabs to be a streamer via firing 'storage' event
|
||||
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
addEventListener("unload", function () {
|
||||
// let chance to other tabs to be a streamer via firing 'storage' event
|
||||
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
|
||||
});
|
||||
|
||||
function exponential_backoff(
|
||||
fn,
|
||||
maxRetries = 5,
|
||||
initialDelay = 1000,
|
||||
randomnessFactor = 0.5,
|
||||
) {
|
||||
let attempt = 0;
|
||||
|
||||
return function tryFunction() {
|
||||
fn()
|
||||
.then((response) => {
|
||||
attempt = 0;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (attempt < maxRetries) {
|
||||
attempt++;
|
||||
let delay = initialDelay * Math.pow(2, attempt); // Exponential backoff
|
||||
let randomMultiplier = 1 + Math.random() * randomnessFactor;
|
||||
delay = delay * randomMultiplier;
|
||||
console.log(
|
||||
`Attempt ${attempt} failed. Retrying in ${(delay / 1000).toPrecision(2)} seconds...`,
|
||||
);
|
||||
setTimeout(tryFunction, delay); // Retry after delay
|
||||
} else {
|
||||
console.log("Max retries reached. Operation failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
var playlist_data = JSON.parse(document.getElementById('playlist_data').textContent);
|
||||
var payload = 'csrf_token=' + playlist_data.csrf_token;
|
||||
|
||||
function add_playlist_video(target) {
|
||||
var select = target.parentNode.children[0].children[1];
|
||||
function add_playlist_video(event) {
|
||||
const target = event.target;
|
||||
var select = document.querySelector("#playlists");
|
||||
var option = select.children[select.selectedIndex];
|
||||
|
||||
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
|
||||
@@ -12,37 +13,43 @@ function add_playlist_video(target) {
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
on200: function (response) {
|
||||
option.textContent = '✓' + option.textContent;
|
||||
option.textContent = '✓ ' + option.textContent;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add_playlist_item(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
function add_playlist_item(event) {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const video_id = target.getAttribute('data-id');
|
||||
var card = document.querySelector(`#video-card-${video_id}`);
|
||||
card.classList.add("hide");
|
||||
|
||||
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
|
||||
'&video_id=' + target.getAttribute('data-id') +
|
||||
'&video_id=' + video_id +
|
||||
'&playlist_id=' + target.getAttribute('data-plid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
card.classList.remove("hide");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove_playlist_item(target) {
|
||||
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
|
||||
tile.style.display = 'none';
|
||||
function remove_playlist_item(event) {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const video_index = target.getAttribute('data-index');
|
||||
const card = document.querySelector(`.video-card [data-index="${video_index}"]`)
|
||||
card.classList.add("hide");
|
||||
|
||||
var url = '/playlist_ajax?action_remove_video=1&redirect=false' +
|
||||
'&set_video_id=' + target.getAttribute('data-index') +
|
||||
'&set_video_id=' + video_index +
|
||||
'&playlist_id=' + target.getAttribute('data-plid');
|
||||
|
||||
helpers.xhr('POST', url, {payload: payload}, {
|
||||
onNon200: function (xhr) {
|
||||
tile.style.display = '';
|
||||
card.classList.remove("hide");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ var subscribe_data = JSON.parse(document.getElementById('subscribe_data').textCo
|
||||
var payload = 'csrf_token=' + subscribe_data.csrf_token;
|
||||
|
||||
var subscribe_button = document.getElementById('subscribe');
|
||||
subscribe_button.parentNode.action = 'javascript:void(0)';
|
||||
|
||||
if (subscribe_button.getAttribute('data-type') === 'subscribe') {
|
||||
subscribe_button.onclick = subscribe;
|
||||
@@ -11,10 +10,29 @@ if (subscribe_button.getAttribute('data-type') === 'subscribe') {
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
}
|
||||
|
||||
function subscribe() {
|
||||
var fallback = subscribe_button.innerHTML;
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.innerHTML = '<b>' + subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
|
||||
function toggleSubscribeButton() {
|
||||
subscribe_button.classList.remove("primary");
|
||||
subscribe_button.classList.remove("secondary");
|
||||
subscribe_button.classList.remove("unsubscribe");
|
||||
subscribe_button.classList.remove("subscribe");
|
||||
|
||||
if (subscribe_button.getAttribute('data-type') === 'subscribe') {
|
||||
subscribe_button.textContent = subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text;
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.classList.add("secondary");
|
||||
subscribe_button.classList.add("unsubscribe");
|
||||
} else {
|
||||
subscribe_button.textContent = subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text;
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.classList.add("primary");
|
||||
subscribe_button.classList.add("subscribe");
|
||||
}
|
||||
}
|
||||
|
||||
function subscribe(e) {
|
||||
e.preventDefault();
|
||||
var fallback = subscribe_button.textContent;
|
||||
toggleSubscribeButton();
|
||||
|
||||
var url = '/subscription_ajax?action_create_subscription_to_channel=1&redirect=false' +
|
||||
'&c=' + subscribe_data.ucid;
|
||||
@@ -22,15 +40,15 @@ function subscribe() {
|
||||
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'subscribe request'}, {
|
||||
onNon200: function (xhr) {
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.innerHTML = fallback;
|
||||
subscribe_button.textContent = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function unsubscribe() {
|
||||
var fallback = subscribe_button.innerHTML;
|
||||
subscribe_button.onclick = subscribe;
|
||||
subscribe_button.innerHTML = '<b>' + subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
|
||||
function unsubscribe(e) {
|
||||
e.preventDefault();
|
||||
var fallback = subscribe_button.textContent;
|
||||
toggleSubscribeButton();
|
||||
|
||||
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
|
||||
'&c=' + subscribe_data.ucid;
|
||||
@@ -38,7 +56,7 @@ function unsubscribe() {
|
||||
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'unsubscribe request'}, {
|
||||
onNon200: function (xhr) {
|
||||
subscribe_button.onclick = unsubscribe;
|
||||
subscribe_button.innerHTML = fallback;
|
||||
subscribe_button.textContent = fallback;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
44
assets/js/theme.js
Normal file
44
assets/js/theme.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const themeSelector = document.querySelector("#theme-selector");
|
||||
themeSelector.addEventListener("change", (event) => {
|
||||
const select = event.target;
|
||||
const selected = select.options[select.selectedIndex].text;
|
||||
applyTheme(selected);
|
||||
});
|
||||
|
||||
const colorSchemeSelector = document.querySelector("#color-scheme");
|
||||
colorSchemeSelector.addEventListener("change", (event) => {
|
||||
const select = event.target;
|
||||
const selected = select.options[select.selectedIndex].text;
|
||||
applyColorScheme(selected);
|
||||
});
|
||||
|
||||
function applyTheme(theme) {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = `/css/theme-${theme}.css`;
|
||||
link.id = "theme-css";
|
||||
|
||||
const themeCss = document.querySelector("#theme-css");
|
||||
if (themeCss) {
|
||||
themeCss.parentNode.removeChild(themeCss);
|
||||
}
|
||||
|
||||
const head = document.getElementsByTagName("head")[0];
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
function applyColorScheme(colorScheme) {
|
||||
document.body.classList.remove("dark-theme");
|
||||
document.body.classList.remove("light-theme");
|
||||
|
||||
if (colorScheme === "dark" || colorScheme === "light") {
|
||||
document.body.classList.add(`${colorScheme}-theme`);
|
||||
}
|
||||
}
|
||||
|
||||
applyTheme(themeSelector.options[themeSelector.selectedIndex].text);
|
||||
applyColorScheme("dark");
|
||||
|
||||
// <link rel="stylesheet" href="/css/theme-dracula.css" />
|
||||
// <link rel="stylesheet" href="/css/theme-catppuccin-latte.css" />
|
||||
// <link rel="stylesheet" href="/css/ionicons.min.css" />
|
||||
@@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
var toggle_theme = document.getElementById('toggle_theme');
|
||||
toggle_theme.href = 'javascript:void(0)';
|
||||
|
||||
const STORAGE_KEY_THEME = 'dark_mode';
|
||||
const THEME_DARK = 'dark';
|
||||
const THEME_LIGHT = 'light';
|
||||
|
||||
// TODO: theme state controlled by system
|
||||
toggle_theme.addEventListener('click', function () {
|
||||
toggle_theme.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
const isDarkTheme = helpers.storage.get(STORAGE_KEY_THEME) === THEME_DARK;
|
||||
const newTheme = isDarkTheme ? THEME_LIGHT : THEME_DARK;
|
||||
setTheme(newTheme);
|
||||
|
||||
@@ -69,6 +69,8 @@ function get_playlist(plid) {
|
||||
|
||||
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
|
||||
on200: function (response) {
|
||||
if (response === null) return;
|
||||
|
||||
playlist.innerHTML = response.playlistHtml;
|
||||
|
||||
if (!response.nextVideo) return;
|
||||
|
||||
Reference in New Issue
Block a user