Switched from flask-bootstrap to standalone Bootstrap 5 and overhaul of UI

This commit is contained in:
Maximilian Wagner
2023-08-03 16:57:57 +02:00
parent d1489c134b
commit 0f2b607e88
55 changed files with 59032 additions and 134 deletions

7
app.py
View File

@@ -1,8 +1,7 @@
from flask import Flask, current_app, g
from flask_bootstrap import Bootstrap
from flask_wtf.csrf import CSRFProtect
from frontend import frontend, nav
from frontend import frontend
from backend import get_db
from os import urandom
@@ -16,16 +15,12 @@ def init_db():
def create_app():
app = Flask(__name__)
Bootstrap(app)
app.config['BOOTSTRAP_SERVE_LOCAL'] = True
app.config['SECRET_KEY'] = urandom(32)
app.config['BOOTSTRAP_USE_MINIFIED'] = False
CSRFProtect(app)
app.register_blueprint(frontend)
app.app_context().push()
nav.init_app(app)
init_db()

View File

@@ -304,6 +304,7 @@ def yt_download(location, ext='mp3'):
# if videos are wanted, adjust the options
if ext == 'mp4':
opts['format'] = 'bestvideo[height<=1080]+bestaudio/best[height<=1080]'
opts.pop('format')
opts.pop('postprocessors')

View File

@@ -4,6 +4,6 @@ from wtforms.validators import URL, DataRequired, AnyOf
class DownloadForm(FlaskForm):
url = URLField('Video, Channel or Playlist', validators=[DataRequired(), URL()], render_kw={'placeholder': 'YouTube Link'})
url = URLField('Video, Channel or Playlist', validators=[DataRequired(), URL()], render_kw={'placeholder': 'URL'})
ext = SelectField('Type', choices=[('mp3', 'Audio'), ('mp4', 'Video')], validators=[AnyOf(('mp3', 'mp4'))])
submit = SubmitField('Go')

View File

@@ -1,8 +1,7 @@
from __future__ import unicode_literals
from flask import Blueprint, request, render_template, flash, send_from_directory
from flask_nav3 import Nav
from flask_nav3.elements import Navbar, View
from flask import Blueprint, request, render_template, flash, send_from_directory, send_file
from forms.download import DownloadForm
from backend import zip_folder, downloads_path, enqueue_download
@@ -11,20 +10,12 @@ from file_cache import *
frontend = Blueprint('frontend', __name__)
nav = Nav()
nav.register_element('frontend_top', Navbar(
View('ytm-ls', '.index'),
View('Downloader', '.downloader'),
View('Library', '.library')
)
)
# index has a list of running downloads
@frontend.route('/', methods=['GET'])
def index():
if not running_downloads:
flash('Currently, no downloads are running.')
flash('Currently, no downloads are running.', 'primary')
return render_template('index.html', running_downloads=running_downloads, titles=titles, urls=urls, amount=len(urls))
@@ -52,7 +43,7 @@ def downloader():
enqueue_download(url, ext=ext)
# show download start confirmation
flash('Download enqueued and will finish in background.')
flash('Download enqueued and will finish in background.', 'primary')
return render_template('feedback-simple.html', amount=len(urls))
@@ -84,16 +75,17 @@ def update(url_rowid):
enqueue_download(url, update=True)
# show download start confirmation
flash('Update enqueued and will finish in background.')
flash('Update enqueued and will finish in background.', 'primary')
return render_template('feedback-simple.html', titles=titles, urls=urls, amount=len(urls))
@frontend.route('/library', methods=['GET'])
def library():
videos = query_db("SELECT name, ext, path FROM video")
videos = query_db("SELECT name, ext, path FROM video "
"LEFT JOIN collection ON video.id = collection.video WHERE collection.video IS NULL ")
playlists = query_db("SELECT name, ROWID FROM playlist")
if not playlists and not videos:
flash('Library ist currently empty. Try downloading something!')
flash('Library ist currently empty. Try downloading something!', 'primary')
return render_template('library.html', videos=videos, playlists=playlists, amount=len(playlists))
@@ -106,3 +98,27 @@ def library_playlist():
'playlist.ROWID = :playlist',
{'playlist': playlist})
return render_template('collection.html', videos=videos)
@frontend.route('/player', methods=['GET'])
def player():
return render_template('video-player.html')
@frontend.route('/serve', methods=['GET'])
def serve():
file = request.args.get('file', None)
if 'audio' in file:
file = r'downloads\\CptCatman\\my vital organs all lift together.mp3'
return send_file(
file,
'audio/mpeg',
True
)
else:
file = r'downloads\\Rick Astley - Never Gonna Give You Up (Official Music Video).webm'
return send_file(
file,
'video/webm',
True
)

View File

@@ -1,6 +1,4 @@
flask
flask_bootstrap
flask_nav3
flask_wtf
wtforms
yt_dlp

5002
static/bootstrap5/css/bootstrap-grid.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,426 @@
/*!
* Bootstrap Reboot v5.0.2 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
background-color: #fff;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: #0d6efd;
text-decoration: underline;
}
a:hover {
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
direction: ltr /* rtl:ignore */;
unicode-bidi: bidi-override;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: #d63384;
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #6c757d;
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: left;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v5.0.2 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,423 @@
/*!
* Bootstrap Reboot v5.0.2 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
background-color: #fff;
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
background-color: currentColor;
border: 0;
opacity: 0.25;
}
hr:not([size]) {
height: 1px;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-bs-original-title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul {
padding-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.2em;
background-color: #fcf8e3;
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: #0d6efd;
text-decoration: underline;
}
a:hover {
color: #0a58ca;
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
direction: ltr ;
unicode-bidi: bidi-override;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: #d63384;
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.2rem 0.4rem;
font-size: 0.875em;
color: #fff;
background-color: #212529;
border-radius: 0.2rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
font-weight: 700;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #6c757d;
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
select {
text-transform: none;
}
[role=button] {
cursor: pointer;
}
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]::-webkit-calendar-picker-indicator {
display: none;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
textarea {
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
float: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
outline-offset: -2px;
-webkit-appearance: textfield;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::file-selector-button {
font: inherit;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v5.0.2 (https://getbootstrap.com/)
* Copyright 2011-2021 The Bootstrap Authors
* Copyright 2011-2021 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10837
static/bootstrap5/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

10813
static/bootstrap5/css/bootstrap.rtl.css vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6748
static/bootstrap5/js/bootstrap.bundle.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4967
static/bootstrap5/js/bootstrap.esm.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

5016
static/bootstrap5/js/bootstrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

7
static/bootstrap5/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,22 +1,48 @@
{%- extends "bootstrap/base.html" %}
{% import "bootstrap/utils.html" as utils %}
<!DOCTYPE html>
<head>
{% block title %}
ytm-ls
<title>ytm-ls</title>
{% endblock %}
{% block navbar %}
{{ nav.frontend_top.render() }}
{% block head %}
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/static/bootstrap5/css/bootstrap.min.css" rel="stylesheet">
<script src="/static/bootstrap5/js/bootstrap.min.js"></script>
{% endblock %}
</head>
<body>
{% block body %}
<nav class="navbar navbar-expand-md navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="/">yt-mls</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="/downloader">downloader</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/library">library</a>
</li>
</ul>
</div>
</div>
</nav>
{% endblock %}
{% block content %}
{%- with messages = get_flashed_messages(with_categories=True) %}
{%- if messages %}
<div class="row">
<div class="col-md-12">
{{utils.flashed_messages(messages)}}
{% if messages %}
{% for category, message in messages %}
<div class="container d-flex justify-content-center" style="width: fit-content; padding: 1%">
<div class="alert alert-{{ category }} d-flex align-self-center justify-content-center" style="width: 150%" role="alert">{{ message }}</div>
</div>
</div>
{%- endif %}
{% endfor %}
{% endif %}
{%- endwith %}
{% endblock %}
{% endblock %}
</body>

View File

@@ -2,25 +2,32 @@
{% block content %}
{{ super() }}
<div class="container-fluid" style="width: fit-content(105%)">
<table id="videos" class="table">
<thead>
<tr>
<th scope="col" class="text-center">Song</th>
<th scope="col" class="text-center">Download</th>
</tr>
</thead>
<tbody>
{% for video in videos %}
<tr>
<td class="text-center">{{ video['name'] }}</td>
<td class="text-center"><a class="btn" href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Link</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<form action="/download/{{ videos[0]['path'] }}">
<input type="submit" class="btn pull-right" value="Download all"/>
</form>
<div class="container" style="width: fit-content(100%); padding: 1.5%">
<div class="card">
<div class="card-body">
<table id="videos" class="table">
<thead>
<tr>
<th scope="col" class="text-center">Song</th>
<th scope="col" class="text-center"></th>
</tr>
</thead>
<tbody>
{% for video in videos %}
<tr>
<td class="text-center">{{ video['name'] }}</td>
<td class="text-center"><a class="btn btn-link" href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Download</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="container" style="padding: 1.5%">
<form action="/download/{{ videos[0]['path'] }}">
<input type="submit" class="btn btn-primary float-end" value="Download all"/>
</form>
</div>
</div>
{%- endblock %}

View File

@@ -2,15 +2,29 @@
{% block content %}
{{ super() }}
<div class="container-fluid" style="width: fit-content; align-self: flex-start">
<div class="table-bordered" style="padding: .5cm">
<form method="POST" class="form-group">
{{ form.csrf_token }}
{{ form.url.label(class_="form-text") }} <br>
{{ form.url(class_="form-control") }} <br>
{{ form.ext(class_="form-control") }} <br>
{{ form.submit(class_="btn primary") }}
</form>
<div class="container-fluid d-flex justify-content-center" style="width: fit-content(100%); padding: 1.5%">
<div class="card">
<div class="card-body">
<form method="POST" class="form-group">
{{ form.csrf_token }}
<div class="container-fluid" style="width: fit-content">
<div class="row" style="width: fit-content">
{{ form.url.label(class_="form-label") }}
</div>
<div class="row" style="width: fit-content">
<div class="col">
{{ form.url(class_="form-control") }}
</div>
<div class="col">
{{ form.ext(class_="form-select") }}
</div>
<div class="col" style="width: fit-content">
{{ form.submit(class_="btn btn-primary") }}
</div>
</div>
</div>
</form>
</div>
</div>
{% if form.errors %}

View File

@@ -1,34 +1,34 @@
{%- extends "base.html" %}
{% import "bootstrap/utils.html" as utils %}
{% block content %}
{{ super() }}
<div class="container" style="width: fit-content(105%)">
<div class="container" style="width: fit-content(100%); padding: 1.5%">
{% if running_downloads %}
<div class="card">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="row">
<table id="videos" class="table">
<thead>
<tr>
<th scope="col" class="text-center">Queue</th>
<th scope="col" class="text-center">Started at</th>
</tr>
</thead>
<tbody>
{% for entry in running_downloads %}
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="row">
<table id="videos" class="table">
<thead>
<tr>
<td class="text-center"><a href="{{ entry[0] }}" target="_blank">{{ entry[0] }}</a></td>
<td class="text-center">{{ entry[1] }}</td>
<th scope="col" class="text-center">Queue</th>
<th scope="col" class="text-center">Started at</th>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</li>
</ul>
</thead>
<tbody>
{% for entry in running_downloads %}
<tr>
<td class="text-center"><a href="{{ entry[0] }}" target="_blank">{{ entry[0] }}</a></td>
<td class="text-center">{{ entry[1] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</li>
</ul>
</div>
</div>
<br><br>

View File

@@ -2,61 +2,61 @@
{% block content %}
{{ super() }}
<div class="container-fluid" style="width: fit-content(105%)">
<div class="container" style="width: fit-content(100%); padding: 1.5%">
{% if playlists %}
<div class="card">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="row">
<table id="playlists" class="table">
<thead>
<tr>
<th scope="col" class="text-center">Playlists</th>
<th scope="col" class="text-center">Update</th>
</tr>
</thead>
<tbody>
{% for playlist in playlists %}
<tr>
<td class="text-center"><a class="btn" href="/library-playlist?playlist={{ playlist['ROWID'] }}">{{ playlist['name'] }}</a></td>
<td class="text-center"><a class="btn" href="/update/{{ playlist[1] }}">Start</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</li>
</ul>
<div class="card-body">
<table id="playlists" class="table">
<thead>
<tr>
<th scope="col" class="text-center align-middle">Playlists</th>
<th scope="col" class="text-center align-middle">Update</th>
</tr>
</thead>
<tbody>
{% for playlist in playlists %}
<tr>
<td class="text-center align-middle"><a class="btn btn-link" href="/library-playlist?playlist={{ playlist['ROWID'] }}">{{ playlist['name'] }}</a></td>
<td class="text-center align-middle"><a class="btn btn-link" href="/update/{{ playlist[1] }}">Start</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<br><br>
<br>
{% if videos %}
<div class="card">
<ul class="list-group list-group-flush">
<li class="list-group-item">
<div class="row">
<table id="videos" class="table">
<thead>
<div class="card-body">
<table id="videos" class="table">
<thead>
<tr>
<th scope="col" class="text-center align-middle">Titles not in any playlist</th>
<th scope="col" class="text-center align-middle">Download</th>
</tr>
</thead>
<tbody>
{% for video in videos %}
{% if 'mp3' in video['ext'] %}
<tr>
<td class="text-center align-middle">{{ video['name'] }}</td>
<td class="text-center align-middle"><a class="btn btn-link" href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Download</a></td>
</tr>
{% endif %}
{% if 'mp4' in video['ext'] %}
<tr>
<th scope="col" class="text-center">Title</th>
<th scope="col" class="text-center">Download</th>
<td class="text-center align-middle">{{ video['name'] }}</td>
<td class="text-center align-middle"><a class="btn btn-link" href="/player?file={{ video['path'] + video['name'] + video['ext'] }}">Watch</a></td>
</tr>
</thead>
<tbody>
{% for video in videos %}
<tr>
<td class="text-center">{{ video['name'] }}</td>
<td class="text-center"><a class="btn" href="/download/{{ video['path'] + video['name'] + video['ext'] }}" download>Link</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</li>
</ul>
</div>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
</div>
{%- endblock %}

View File

@@ -0,0 +1,12 @@
{%- extends "base.html" %}
{% block content %}
{{ super() }}
<div class="container-fluid">
<div id="trailer" class="section d-flex justify-content-center embed-responsive embed-responsive-16by9">
<video class="embed-responsive-item" style="width: 100%; padding: 1.5%; height: auto" controls>
<source src="serve?file=video" type="video/webm">
</video>
</div>
</div>
{%- endblock %}