diff options
Diffstat (limited to 'youtube')
29 files changed, 5432 insertions, 1510 deletions
diff --git a/youtube/static/channel.css b/youtube/static/channel.css new file mode 100644 index 0000000..467bb0d --- /dev/null +++ b/youtube/static/channel.css @@ -0,0 +1,557 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* playlist */ +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} +/* /playlist */ + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + display: grid; + grid-row-gap: 1rem; +} + +/* fix hr when is children of grid */ +hr { + width: 100%; +} + +.author-container { + display: grid; + grid-template-columns: 1fr; + margin: auto; + grid-template-areas: + "author" + "summary" + "subscribe"; +} +.author { + grid-area: author; + display: grid; + grid-template-columns: 100px 1fr; + grid-column-gap: 1rem; + align-items: center; + justify-self: center; +} +.summary { grid-area: summary; } +.summary p { + text-align: center; +} +.subscribe { + grid-area: subscribe; + justify-self: center; +} +.subscribe .btn-subscribe { + background-color: var(--buttom); + color: var(--buttom-text); + text-shadow: none; + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + border: none; + border-radius: 0.2rem; +} + +/* Video list item */ +.video-container { + display: grid; + grid-row-gap: 0.5rem; +} + +.item-box { + display: grid; + grid-template-columns: 1.9fr 0.1fr; + grid-template-rows: 1fr; + grid-gap: 1px; + grid-template-areas: + "item-video item-checkbox"; +} + +.item-video { + grid-area: item-video; + + display: grid; + grid-template-columns: auto; + grid-template-rows: repeat(4, auto); + grid-row-gap: 0.4rem; + grid-template-areas: + "thumbnail-box" + "info-box"; + align-items: center; + + font-size: 0.7rem; +} + +.item-video a { + text-decoration: none; + cursor: pointer; +} + +.item-video.channel-item { + border-radius: 50%; + width: 150px; + height: 150px; +} + +.thumbnail-box { + grid-area: thumbnail-box; + position: relative; +} + +.thumbnail { + padding: 28.125%; + position: relative; + box-sizing: border-box; +} + +.thumbnail-img { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + object-fit: cover; +} + +.length { + z-index: 100; + position: absolute; + background-color: rgba(35, 35, 35, 0.75); + color: #fff; + border-radius: 2px; + padding: 2px; + font-size: 16px; + right: 0.25em; + bottom: -0.75em; +} + +.playlist-item .thumbnail-info { + position: absolute; + right: 0px; + bottom: 0px; + height: 100%; + width: 50%; + text-align: center; + white-space: pre-line; + opacity: .8; + color: var(--text); + font-size: 0.8125rem; + background: var(--secondary-background); + padding: 0; +} + +.playlist-item .thumbnail-info span { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + text-transform: none; +} + +.thumbnail-img { + margin: auto; + display: block; + max-height: 100%; + max-width: 100%; +} + +.info-box { + grid-area: info-box; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto auto auto auto; + grid-gap: 1px; + grid-template-areas: + "." + "." + "." + "." + "."; +} + +.title { + font-size: 0.8rem; + margin: 0px; + font-weight: normal; + overflow: hidden; + text-overflow: ellipsis; +} + +.info-box address { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.thumbnail-info { + background-color: var(--time-background); + color: #fff; + padding: 2px 5px; + text-transform: uppercase; + font-weight: 700; + font-size: 12px; + position: absolute; + right: 0; + bottom: .2rem; +} + +.item-checkbox { + grid-area: item-checkbox; + justify-self: start; + align-self: center; + min-width: 30px; + margin: 0px; +} + +.stats { + display: flex; + justify-content: space-between; +} + +.horizontal-stats { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.horizontal-stats > li { + display: inline; +} + +.horizontal-stats > li:first-child::after { + content: " | "; +} + +/* pagination */ +.main .pagination-container { + display: grid; + justify-content: center; +} + +.main .pagination-container .pagination-list { + display: grid; + grid-auto-flow: column; + grid-column-gap: 0.5rem; +} + +.main .pagination-container .pagination-list .page-link { + border-style: none; + font-weight: bold; + text-align: center; + background: var(--secondary-focus); + text-decoration: none; + align-self: center; + padding: .5rem; + width: 1rem; +} + +.main .pagination-container .pagination-list .page-link.is-current { + background: var(--secondary-background); +} + +/* /video list item */ + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .info-box { + grid-gap: 2px; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 600px) { + .video-container { + display: grid; + grid-row-gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; + position: absolute; + } + + .author-container { + max-width: 50vw; + } + + /* playlist */ + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 100px); + } + .play-clean > button { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 6px; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .video-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-row-gap: 1rem; + grid-column-gap: 1rem; + } +} diff --git a/youtube/static/comments.css b/youtube/static/comments.css index b177a4f..080810e 100644 --- a/youtube/static/comments.css +++ b/youtube/static/comments.css @@ -1,148 +1,283 @@ -.video-metadata{ - display: grid; - grid-template-columns: auto 1fr; - grid-template-rows: auto auto 1fr auto; +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); } - .video-metadata > .video-metadata-thumbnail-box{ - grid-row: 1 / span 3; - } - .video-metadata > .title{ - word-wrap:break-word; - grid-row: 1; - } - .video-metadata > h2{ - grid-row: 2; - font-size: 0.875rem; - } - .video-metadata > span{ - grid-row:3; - } - .video-metadata > hr{ - grid-row: 4; - grid-column: 1 / span 2; - width: 100%; - } -.comment-form{ +body { display: grid; - align-content: start; - justify-items: start; - align-items: start; -} - #comment-account-options{ - display:grid; - grid-auto-flow: column; - grid-column-gap: 10px; - margin-top:10px; - margin-bottom:10px; - } - #comment-account-options a{ - margin-left:10px; - } + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} -.comments-area{ - display:grid; +h1, h2, h3, h4, h5, h6, div, button { + margin: 0; + padding: 0; +} + +img { + width: 100%; + height: auto; } - .comments-area textarea{ - resize: vertical; - justify-self:stretch; - } - .post-comment-button{ - margin-top:10px; - justify-self:end; - } - .comment-links{ - display:grid; - grid-auto-flow: column; - grid-column-gap: 10px; - justify-content:start; - } -.comments{ - margin-top:10px; - grid-row-gap: 10px; +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + border-radius: 5px; + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { display: grid; - align-content:start; + grid-gap: 1px; + grid-template-areas: + "home" + "form"; + grid-area: header; } -.comment{ - display:grid; - grid-template-columns: auto auto 100px 1fr; - grid-template-rows: 0fr 0fr 0fr 0fr; - background-color: var(--interface-color); - justify-content: start; +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; } -.comment .author-avatar{ - grid-column: 1; - grid-row: 1 / span 3; - align-self: start; - margin-right: 5px; - height:32px; - width:32px; +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; } - .comment .author-avatar-img{ - max-height: 100%; - } -.comment address{ - grid-column: 2; - grid-row: 1; - margin-right:15px; +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; white-space: nowrap; - overflow:hidden; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); } -.comment .text{ - grid-column: 2 / span 3; - grid-row: 2; - white-space: pre-wrap; - min-width: 0; - word-wrap: break-word; +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; } +.dropdown-label { + grid-area: dropdown-label; -.comment .permalink{ - grid-column: 3; - grid-row: 1; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); } +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} -.comment .likes{ - grid-column:2; - grid-row:3; - font-weight:bold; +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; white-space: nowrap; } +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + margin: 0 auto; + max-width: 80ch; +} + +/* comments */ +.comments-area { + display: grid; + grid-row-gap: 0.5rem; +} + +.comments-area textarea { + resize: vertical; +} -.comment .bottom-row{ - grid-column:2 / span 3; - grid-row:4; - justify-self:start; +.video-metadata { display: grid; - grid-auto-flow: column; - grid-column-gap: 10px; + grid-template-columns: auto 1fr; + grid-column-gap: 0.5rem; + grid-template-rows: auto auto 1fr auto; + grid-template-areas: + "video-metadata-thumbnail-box ." + "video-metadata-thumbnail-box ." + "video-metadata-thumbnail-box ."; +} + +.video-metadata > h2 { + font-size: 0.875rem; } -details.replies > summary{ - background-color: var(--interface-color); - border-style: outset; - border-width: 1px; - font-weight: bold; - padding-bottom: 0px; +.video-metadata-thumbnail-box { + grid-area: video-metadata-thumbnail-box; } -.replies-open-new-tab{ - display: inline-block; - margin-top: 5px; +.comment-form { + display: grid; + grid-row-gap: 0.5rem; } -details.replies .comment{ - width: 600px; +.post-comment-button { + justify-self: end; } -.more-comments{ - justify-self:center; - margin-top:10px; +.comments { + display: grid; + grid-row-gap: 0.5rem; +} + +.comment { + display: grid; + grid-template-columns: repeat(3, auto) 3fr; + grid-template-rows: repeat(4, auto); + grid-column-gap: 0.4rem; + grid-template-areas: + "author-avatar author-name permalink permalink" + "author-avatar comment-text comment-text comment-text" + ". comment-likes comment-likes comment-likes" + ". button-row button-row button-row"; + background: var(--secondary-background); +} + +.author-avatar { grid-area: author-avatar; } +.author-name { grid-area: author-name; } +.permalink { grid-area: permalink; } +.comment-text { grid-area: comment-text; } +.comment-likes { grid-area: comment-likes; } +.button-row { grid-area: button-row; } + +.more-comments { + justify-self: center; + margin-top: 10px; margin-bottom: 10px; + background: var(--secondary-background); + padding: 5px; + + /* disable text selection */ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 780px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; + position: absolute; + } } diff --git a/youtube/static/dark_theme.css b/youtube/static/dark_theme.css index 221dc95..bc14f5c 100644 --- a/youtube/static/dark_theme.css +++ b/youtube/static/dark_theme.css @@ -1,39 +1,17 @@ -body{ - --interface-color: #333333; - --text-color: #cccccc; - --background-color: #000000; - --video-background-color: #080808; +:root { + --background: #121212; + --text: #FFF; + --secondary-hover: #73828c; + --secondary-focus: #616161; + --secondary-inverse: #FFF; + --secondary-background: #424242; + --link: #22aaff; + --link-visited: #7755ff; + --buttom: #dcdcdb; + --buttom-text: #415462; + --button-border: #91918c; + --buttom-hover: #BBB; + --search-text: #FFF; + --time-background: #000; + --time-text: #FFF; } - -a:link { - color: #22aaff; -} - -a:visited { - color: #7755ff; -} - -a:not([href]){ - color: var(--text-color); -} - -.comment .permalink{ - color: #ffffff; -} - -.setting-item{ - background-color: #444444; -} - - -.muted{ - background-color: #111111; - color: gray; -} - -.muted a:link { - color: #10547f; -} - - - diff --git a/youtube/static/gray_theme.css b/youtube/static/gray_theme.css index ca699c7..cae689d 100644 --- a/youtube/static/gray_theme.css +++ b/youtube/static/gray_theme.css @@ -1,18 +1,17 @@ -body{ - --interface-color: #dadada; - --text-color: #222222; - --background-color: #bcbcbc; - --video-background-color: #dadada; -} - -.comment .permalink{ - color: #000000; -} - -.setting-item{ - background-color: #eeeeee; -} - -.muted{ - background-color: #888888; +:root { + --background: #2d3743; + --text: #FFF; + --secondary-hover: #73828c; + --secondary-focus: rgba(115, 130, 140, 0.125); + --secondary-inverse: #FFF; + --secondary-background: #102027; + --link: #22aaff; + --link-visited: #7755ff; + --buttom: #dcdcdb; + --buttom-text: #415462; + --button-border: #91918c; + --buttom-hover: #BBB; + --search-text: #FFF; + --time-background: #000; + --time-text: #FFF; } diff --git a/youtube/static/home.css b/youtube/static/home.css new file mode 100644 index 0000000..9bea07c --- /dev/null +++ b/youtube/static/home.css @@ -0,0 +1,197 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + border-radius: 5px; + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + margin: 0 auto; + max-width: 80ch; +} + +.code-error { + background: var(--secondary-background); + padding: 1rem; +} + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 780px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; + position: absolute; + } +} diff --git a/youtube/static/light_theme.css b/youtube/static/light_theme.css index e4d7eb3..a0540f4 100644 --- a/youtube/static/light_theme.css +++ b/youtube/static/light_theme.css @@ -1,19 +1,17 @@ -body{ - --interface-color: #ffffff; - --text-color: #222222; - --background-color: #f8f8f8; - --video-background-color: #ffffff; +:root { + --background: #FAFAFA; + --text: #415462; + --secondary-hover: #415462; + --secondary-focus: rgba(115, 130, 140, 0.125); + --secondary-inverse: #FFF; + --secondary-background: #eeeeee; + --link: #22aaff; + --link-visited: #7755ff; + --buttom: #dcdcdb; + --buttom-text: #415462; + --button-border: #91918c; + --buttom-hover: #BBB; + --search-text: #415462; + --time-background: #000; + --time-text: #FFF; } - -.comment .permalink{ - color: #000000; -} - -.setting-item{ - background-color: #f8f8f8; -} - -.muted{ - background-color: #888888; -} - diff --git a/youtube/static/local_playlist.css b/youtube/static/local_playlist.css new file mode 100644 index 0000000..bd68c5b --- /dev/null +++ b/youtube/static/local_playlist.css @@ -0,0 +1,527 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* playlist */ +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} +/* /playlist */ + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + display: grid; + grid-row-gap: 1rem; +} + +/* fix hr when is children of grid */ +hr { + width: 100%; +} + +.playlist-metadata { + display: grid; + grid-template-columns: 1fr; + margin: auto; + grid-template-areas: + "play-title" + "play-action"; +} +.play-title { + grid-area: play-title; + text-align: center; +} +.play-action { + grid-area: play-action; +} + +/* Video list item */ +.video-container { + display: grid; + grid-row-gap: 0.5rem; +} + +.item-box { + display: grid; + grid-template-columns: 1.9fr 0.1fr; + grid-template-rows: 1fr; + grid-gap: 1px; + grid-template-areas: + "item-video item-checkbox"; +} + +.item-video { + grid-area: item-video; + + display: grid; + grid-template-columns: auto; + grid-template-rows: repeat(4, auto); + grid-row-gap: 0.4rem; + grid-template-areas: + "thumbnail-box" + "info-box"; + align-items: center; + + font-size: 0.7rem; +} + +.item-video a { + text-decoration: none; + cursor: pointer; +} + +.thumbnail-box { + grid-area: thumbnail-box; + position: relative; +} + +.thumbnail { + padding: 28.125%; + position: relative; + box-sizing: border-box; +} + +.thumbnail-img { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + object-fit: cover; +} + +.length { + z-index: 100; + position: absolute; + background-color: rgba(35, 35, 35, 0.75); + color: #fff; + border-radius: 2px; + padding: 2px; + font-size: 16px; + right: 0.25em; + bottom: -0.75em; +} + +.playlist-item .thumbnail-info { + position: absolute; + right: 0px; + bottom: 0px; + height: 100%; + width: 50%; + text-align: center; + white-space: pre-line; + opacity: .8; + color: var(--text); + font-size: 0.8125rem; + background: var(--secondary-background); + padding: 0; +} + +.playlist-item .thumbnail-info span { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + text-transform: none; +} + +.thumbnail-img { + margin: auto; + display: block; + max-height: 100%; + max-width: 100%; +} + +.info-box { + grid-area: info-box; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto auto auto auto; + grid-gap: 1px; + grid-template-areas: + "." + "." + "." + "." + "."; +} + +.title { + font-size: 0.8rem; + margin: 0px; + font-weight: normal; + overflow: hidden; + text-overflow: ellipsis; +} + +.info-box address { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.thumbnail-info { + background-color: var(--time-background); + color: #fff; + padding: 2px 5px; + text-transform: uppercase; + font-weight: 700; + font-size: 12px; + position: absolute; + right: 0; + bottom: .2rem; +} + +.item-checkbox { + grid-area: item-checkbox; + justify-self: start; + align-self: center; + min-width: 30px; + margin: 0px; +} + +.stats { + display: flex; + justify-content: space-between; +} + +.horizontal-stats { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.horizontal-stats > li { + display: inline; +} + +.horizontal-stats > li:first-child::after { + content: " | "; +} + +/* pagination */ +.main .pagination-container { + display: grid; + justify-content: center; +} + +.main .pagination-container .pagination-list { + display: grid; + grid-auto-flow: column; + grid-column-gap: 0.5rem; +} + +.main .pagination-container .pagination-list .page-link { + border-style: none; + font-weight: bold; + text-align: center; + background: var(--secondary-focus); + text-decoration: none; + align-self: center; + padding: .5rem; + width: 1rem; +} + +.main .pagination-container .pagination-list .page-link.is-current { + background: var(--secondary-background); +} + +/* /video list item */ + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .info-box { + grid-gap: 2px; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 600px) { + .video-container { + display: grid; + grid-row-gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; + position: absolute; + } + + .playlist-metadata { + max-width: 50vw; + } + + /* playlist */ + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 100px); + } + .play-clean > button { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 6px; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .video-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-row-gap: 1rem; + grid-column-gap: 1rem; + } +} diff --git a/youtube/static/normalize.css b/youtube/static/normalize.css new file mode 100644 index 0000000..192eb9c --- /dev/null +++ b/youtube/static/normalize.css @@ -0,0 +1,349 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers. + */ + +body { + margin: 0; +} + +/** + * Render the `main` element consistently in IE. + */ + +main { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Remove the gray background on active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * 1. Remove the bottom border in Chrome 57- + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove the border on images inside links in IE 10. + */ + +img { + border-style: none; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers. + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * Correct the inability to style clickable types in iOS and Safari. + */ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + vertical-align: baseline; +} + +/** + * Remove the default vertical scrollbar in IE 10+. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10. + * 2. Remove the padding in IE 10. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in Edge, IE 10+, and Firefox. + */ + +details { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Misc + ========================================================================== */ + +/** + * Add the correct display in IE 10+. + */ + +template { + display: none; +} + +/** + * Add the correct display in IE 10. + */ + +[hidden] { + display: none; +} diff --git a/youtube/static/playlist.css b/youtube/static/playlist.css new file mode 100644 index 0000000..8b75a7d --- /dev/null +++ b/youtube/static/playlist.css @@ -0,0 +1,537 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* playlist */ +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} +/* /playlist */ + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + display: grid; + grid-row-gap: 1rem; +} + +/* fix hr when is children of grid */ +hr { + width: 100%; +} + +.playlist-metadata { + display: grid; + grid-template-columns: 1fr; + margin: auto; + grid-template-areas: + "author" + "summary" + "playlist-stats"; +} +.author { + grid-area: author; + display: grid; + grid-template-columns: 100px 1fr; + grid-column-gap: 1rem; + align-items: center; + justify-self: center; +} +.summary { + grid-area: summary; + justify-self: start; +} +.playlist-stats { + grid-area: playlist-stats; + justify-self: start; +} + +/* Video list item */ +.video-container { + display: grid; + grid-row-gap: 0.5rem; +} + +.item-box { + display: grid; + grid-template-columns: 1.9fr 0.1fr; + grid-template-rows: 1fr; + grid-gap: 1px; + grid-template-areas: + "item-video item-checkbox"; +} + +.item-video { + grid-area: item-video; + + display: grid; + grid-template-columns: auto; + grid-template-rows: repeat(4, auto); + grid-row-gap: 0.4rem; + grid-template-areas: + "thumbnail-box" + "info-box"; + align-items: center; + + font-size: 0.7rem; +} + +.item-video a { + text-decoration: none; + cursor: pointer; +} + +.thumbnail-box { + grid-area: thumbnail-box; + position: relative; +} + +.thumbnail { + padding: 28.125%; + position: relative; + box-sizing: border-box; +} + +.thumbnail-img { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + object-fit: cover; +} + +.length { + z-index: 100; + position: absolute; + background-color: rgba(35, 35, 35, 0.75); + color: #fff; + border-radius: 2px; + padding: 2px; + font-size: 16px; + right: 0.25em; + bottom: -0.75em; +} + +.playlist-item .thumbnail-info { + position: absolute; + right: 0px; + bottom: 0px; + height: 100%; + width: 50%; + text-align: center; + white-space: pre-line; + opacity: .8; + color: var(--text); + font-size: 0.8125rem; + background: var(--secondary-background); + padding: 0; +} + +.playlist-item .thumbnail-info span { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + text-transform: none; +} + +.thumbnail-img { + margin: auto; + display: block; + max-height: 100%; + max-width: 100%; +} + +.info-box { + grid-area: info-box; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto auto auto auto; + grid-gap: 1px; + grid-template-areas: + "." + "." + "." + "." + "."; +} + +.title { + font-size: 0.8rem; + margin: 0px; + font-weight: normal; + overflow: hidden; + text-overflow: ellipsis; +} + +.info-box address { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.thumbnail-info { + background-color: var(--time-background); + color: #fff; + padding: 2px 5px; + text-transform: uppercase; + font-weight: 700; + font-size: 12px; + position: absolute; + right: 0; + bottom: .2rem; +} + +.item-checkbox { + grid-area: item-checkbox; + justify-self: start; + align-self: center; + min-width: 30px; + margin: 0px; +} + +.stats { + display: flex; + justify-content: space-between; +} + +.horizontal-stats { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.horizontal-stats > li { + display: inline; +} + +.horizontal-stats > li:first-child::after { + content: " | "; +} + +/* pagination */ +.main .pagination-container { + display: grid; + justify-content: center; +} + +.main .pagination-container .pagination-list { + display: grid; + grid-auto-flow: column; + grid-column-gap: 0.5rem; +} + +.main .pagination-container .pagination-list .page-link { + border-style: none; + font-weight: bold; + text-align: center; + background: var(--secondary-focus); + text-decoration: none; + align-self: center; + padding: .5rem; + width: 1rem; +} + +.main .pagination-container .pagination-list .page-link.is-current { + background: var(--secondary-background); +} + +/* /video list item */ + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .info-box { + grid-gap: 2px; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 600px) { + .video-container { + display: grid; + grid-row-gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; + position: absolute; + } + + .playlist-metadata { + max-width: 50vw; + } + + /* playlist */ + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 100px); + } + .play-clean > button { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 6px; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .video-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-row-gap: 1rem; + grid-column-gap: 1rem; + } +} diff --git a/youtube/static/search.css b/youtube/static/search.css new file mode 100644 index 0000000..6c463bc --- /dev/null +++ b/youtube/static/search.css @@ -0,0 +1,495 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* playlist */ +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} +/* /playlist */ + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + display: grid; + grid-row-gap: 1rem; +} + +/* fix hr when is children of grid */ +hr { + width: 100%; +} + +.result-info { + justify-self: center; +} + +/* pagination */ +.main .pagination-container { + display: grid; + justify-content: center; +} + +.main .pagination-container .pagination-list { + display: grid; + grid-auto-flow: column; + grid-column-gap: 0.5rem; +} + +.main .pagination-container .pagination-list .page-link { + border-style: none; + font-weight: bold; + text-align: center; + background: var(--secondary-focus); + text-decoration: none; + align-self: center; + padding: .5rem; + width: 1rem; +} + +.main .pagination-container .pagination-list .page-link.is-current { + background: var(--secondary-background); +} + +/* Video list item */ +.video-container { + display: grid; + grid-row-gap: 0.5rem; +} + +.length { + z-index: 100; + position: absolute; + background-color: rgba(35, 35, 35, 0.75); + color: #fff; + border-radius: 2px; + padding: 2px; + font-size: 16px; + right: 0.25em; + bottom: -0.75em; +} + +.item-box { + display: grid; + grid-template-columns: 1.9fr 0.1fr; + grid-template-rows: 1fr; + grid-gap: 1px; + grid-template-areas: + "item-video item-checkbox"; +} + +.item-video { + grid-area: item-video; + + display: grid; + grid-template-columns: auto; + grid-template-rows: repeat(4, auto); + grid-row-gap: 0.4rem; + grid-template-areas: + "thumbnail-box" + "info-box"; + align-items: center; + + font-size: 0.7rem; +} + +.item-video a { + text-decoration: none; + cursor: pointer; +} + +.item-video.channel-item .thumbnail.channel { + padding: 0px; + text-align: center; +} + +.item-video.channel-item .thumbnail-img.channel { + width: 56.25%; + position: relative; +} + +.thumbnail-box { + grid-area: thumbnail-box; + position: relative; +} + +.playlist-item .thumbnail-info { + position: absolute; + right: 0px; + bottom: 0px; + height: 100%; + width: 50%; + text-align: center; + white-space: pre-line; + opacity: .8; + color: var(--text); + font-size: 0.8125rem; + background: var(--secondary-background); + padding: 0; +} + +.playlist-item .thumbnail-info span { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + text-transform: none; +} + +.thumbnail { + padding: 28.125%; + position: relative; + box-sizing: border-box; +} + +.thumbnail-img { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + object-fit: cover; +} + +.title { + font-size: 0.8rem; + margin: 0px; + font-weight: normal; + overflow: hidden; + text-overflow: ellipsis; +} + +.info-box address { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.thumbnail-info { + background-color: var(--time-background); + color: #fff; + padding: 2px 5px; + text-transform: uppercase; + font-weight: 700; + font-size: 12px; + position: absolute; + right: 0; + bottom: .2rem; +} + +.item-checkbox { + grid-area: item-checkbox; + justify-self: start; + align-self: center; + min-width: 30px; + margin: 0px; +} + +.stats { + display: flex; + justify-content: space-between; +} + +.horizontal-stats > li { + display: inline; +} + + +.horizontal-stats > li:first-child::after { + content: " | "; +} + +/* /video list item */ + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .info-box { + grid-gap: 2px; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 600px) { + .video-container { + display: grid; + grid-row-gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; + position: absolute; + } + + /* playlist */ + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 100px); + } + .play-clean > button { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 6px; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .video-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-row-gap: 1rem; + grid-column-gap: 1rem; + } +} diff --git a/youtube/static/settings.css b/youtube/static/settings.css new file mode 100644 index 0000000..2e72505 --- /dev/null +++ b/youtube/static/settings.css @@ -0,0 +1,241 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + border-radius: 5px; + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + margin: 0 auto; + width: 100%; +} + +.settings-form { + background-color: var(--secondary-background); + padding: 1rem; +} + +.settings-list { + display: grid; + grid-row-gap: 1rem; + list-style: none; + padding: 0px; +} + +.setting-item { + display: grid; + grid-template-columns: auto auto; + background-color: var(--secondary-focus); + align-items: center; + padding: 5px; +} + +.setting-item select { + max-width: 100%; + justify-self: end; +} + +.setting-item input { + max-width: 80px; + justify-self: end; +} + +.settings-form input[type="submit"] { + cursor: pointer; +} + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 780px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; + position: absolute; + background: var(--background); + padding-right: 4rem; + } + .main { + display: grid; + justify-content: center; + } + .settings-form { + width: 50vw; + } + .setting-item select { + max-width: 250px; + } + .setting-item input { + max-width: 250px; + } +} diff --git a/youtube/static/subscription.css b/youtube/static/subscription.css new file mode 100644 index 0000000..74571fc --- /dev/null +++ b/youtube/static/subscription.css @@ -0,0 +1,530 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* playlist */ +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} +/* /playlist */ + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + display: grid; + grid-row-gap: 1rem; +} + +/* fix hr when is children of grid */ +hr { + width: 100%; +} + +.sidebar-links { + display: grid; + grid-template-columns: 1fr; + margin: auto; + grid-template-areas: + "sidebar-title" + "sidebar-action"; + align-items: center; + justify-items: center; + justify-content: center; +} +.sidebar-title { + grid-area: sidebar-title; + text-align: center; +} +.sidebar-action { + grid-area: sidebar-action; +} + +/* Video list item */ +.video-container { + display: grid; + grid-row-gap: 0.5rem; +} + +.item-box { + display: grid; + grid-template-columns: 1.9fr 0.1fr; + grid-template-rows: 1fr; + grid-gap: 1px; + grid-template-areas: + "item-video item-checkbox"; +} + +.item-video { + grid-area: item-video; + + display: grid; + grid-template-columns: auto; + grid-template-rows: repeat(4, auto); + grid-row-gap: 0.4rem; + grid-template-areas: + "thumbnail-box" + "info-box"; + align-items: center; + + font-size: 0.7rem; +} + +.item-video a { + text-decoration: none; + cursor: pointer; +} + +.thumbnail-box { + grid-area: thumbnail-box; + position: relative; +} + +.thumbnail { + padding: 28.125%; + position: relative; + box-sizing: border-box; +} + +.thumbnail-img { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + object-fit: cover; +} + +.length { + z-index: 100; + position: absolute; + background-color: rgba(35, 35, 35, 0.75); + color: #fff; + border-radius: 2px; + padding: 2px; + font-size: 16px; + right: 0.25em; + bottom: -0.75em; +} + +.playlist-item .thumbnail-info { + position: absolute; + right: 0px; + bottom: 0px; + height: 100%; + width: 50%; + text-align: center; + white-space: pre-line; + opacity: .8; + color: var(--text); + font-size: 0.8125rem; + background: var(--secondary-background); + padding: 0; +} + +.playlist-item .thumbnail-info span { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + text-transform: none; +} + +.thumbnail-img { + margin: auto; + display: block; + max-height: 100%; + max-width: 100%; +} + +.info-box { + grid-area: info-box; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto auto auto auto; + grid-gap: 1px; + grid-template-areas: + "." + "." + "." + "." + "."; +} + +.title { + font-size: 0.8rem; + margin: 0px; + font-weight: normal; + overflow: hidden; + text-overflow: ellipsis; +} + +.info-box address { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.thumbnail-info { + background-color: var(--time-background); + color: #fff; + padding: 2px 5px; + text-transform: uppercase; + font-weight: 700; + font-size: 12px; + position: absolute; + right: 0; + bottom: .2rem; +} + +.item-checkbox { + grid-area: item-checkbox; + justify-self: start; + align-self: center; + min-width: 30px; + margin: 0px; +} + +.stats { + display: flex; + justify-content: space-between; +} + +.horizontal-stats { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.horizontal-stats > li { + display: inline; +} + +.horizontal-stats > li:first-child::after { + content: " | "; +} + +/* pagination */ +.main .pagination-container { + display: grid; + justify-content: center; +} + +.main .pagination-container .pagination-list { + display: grid; + grid-auto-flow: column; + grid-column-gap: 0.5rem; +} + +.main .pagination-container .pagination-list .page-link { + border-style: none; + font-weight: bold; + text-align: center; + background: var(--secondary-focus); + text-decoration: none; + align-self: center; + padding: .5rem; + width: 1rem; +} + +.main .pagination-container .pagination-list .page-link.is-current { + background: var(--secondary-background); +} + +/* /video list item */ + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .info-box { + grid-gap: 2px; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 600px) { + .video-container { + display: grid; + grid-row-gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; + position: absolute; + } + + .sidebar-links { + max-width: 50vw; + } + + /* playlist */ + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 100px); + } + .play-clean > button { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 6px; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .video-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-row-gap: 1rem; + grid-column-gap: 1rem; + } +} diff --git a/youtube/static/subscription_manager.css b/youtube/static/subscription_manager.css new file mode 100644 index 0000000..a7d9780 --- /dev/null +++ b/youtube/static/subscription_manager.css @@ -0,0 +1,403 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 1px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +/* playlist */ +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} +/* /playlist */ + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + display: grid; + grid-row-gap: 1rem; +} + +/* fix hr when is children of grid */ +hr { + width: 100%; +} + +.import-export { + display: grid; + grid-template-columns: 1fr; + margin: auto; + grid-template-areas: + "subscriptions-import-form"; + align-items: center; + justify-items: center; + justify-content: center; +} +.subscriptions-import-form { + grid-area: subscriptions-import-form; + text-align: center; +} + +.sub-list-controls { + display: grid; + grid-row-gap: 0.2rem; +} + +.subscriptions-import-form input[type='submit'], +.sub-list-controls button[type='submit'], +.sub-list-controls input[type='reset'] { + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} + +.tag-group-list { + display: grid; + grid-template-columns: 1fr; + margin: auto; +} + +.sub-list { + display: grid; + grid-template-columns: 1fr; + margin: auto; +} + +/* pagination */ +.main .pagination-container { + display: grid; + justify-content: center; +} + +.main .pagination-container .pagination-list { + display: grid; + grid-auto-flow: column; + grid-column-gap: 0.5rem; +} + +.main .pagination-container .pagination-list .page-link { + border-style: none; + font-weight: bold; + text-align: center; + background: var(--secondary-focus); + text-decoration: none; + align-self: center; + padding: .5rem; + width: 1rem; +} + +.main .pagination-container .pagination-list .page-link.is-current { + background: var(--secondary-background); +} + +/* /video list item */ + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .info-box { + grid-gap: 2px; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 600px) { + .video-container { + display: grid; + grid-row-gap: 0.5rem; + grid-template-columns: 1fr 1fr; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + "main main main main" + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 100px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + background: var(--background); + padding-right: 4rem; + z-index: 1; + position: absolute; + } + + .import-export { + max-width: 50vw; + } + + .sub-list-controls { + grid-template-columns: repeat(8, auto); + max-height: 1rem; + grid-column-gap: 0.5rem; + justify-content: center; + align-items: center; + } + + /* playlist */ + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 100px); + } + .play-clean > button { + padding-left: 0px; + padding-right: 0px; + padding-bottom: 6px; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .video-container { + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-row-gap: 1rem; + grid-column-gap: 1rem; + } +} diff --git a/youtube/static/watch.css b/youtube/static/watch.css new file mode 100644 index 0000000..697961d --- /dev/null +++ b/youtube/static/watch.css @@ -0,0 +1,633 @@ +html { + font-family: "liberation serif", "times new roman", calibri, carlito, serif; + background: var(--background); + color: var(--text); +} + +body { + display: grid; + grid-gap: 20px; + grid-template-areas: + "header" + "main" + "footer"; + /* Fix height */ + height: 100vh; + grid-template-rows: auto 1fr auto; + /* fix top and bottom */ + margin-left: 1rem; + margin-right: 1rem; +} + +img { + width: 100%; + height: auto; +} + +video { + width: 100%; + height: auto; + max-height: 480px; +} + +a:link { + color: var(--link); +} + +a:visited { + color: var(--link-visited); +} + +input[type="text"], +input[type="search"] { + background: var(--background); + border: 1px solid var(--button-border); + border-radius: 5px; + padding: 0.4rem 0.4rem; + font-size: 15px; + color: var(--search-text); +} + +input[type='search'] { + border-bottom: 1px solid var(--button-border); + border-top: 0px; + border-left: 0px; + border-right: 0px; + border-radius: 0px; +} + +header { + display: grid; + grid-gap: 4px; + grid-template-areas: + "home" + "form" + "playlist"; + grid-area: header; +} + +.home { + grid-area: home; + margin-left: auto; + margin-right: auto; + margin-bottom: 1rem; + margin-top: 1rem; +} + +.form { + display: grid; + grid-gap: 4px; + grid-template-areas: + "search-box" + "search-button" + "dropdown"; + grid-area: form; +} + +.search-box { + grid-area: search-box; +} +.search-button { + grid-area: search-button; + + cursor: pointer; + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.search-button:hover { + background-color: var(--buttom-hover); +} + +.dropdown { + display: grid; + grid-gap: 1px; + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + z-index: 1; +} +.dropdown-label { + grid-area: dropdown-label; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.dropdown-label:hover { + background-color: var(--buttom-hover); +} + +.playlist { + display: grid; + grid-gap: 4px; + grid-template-areas: + "play-box" + "play-hidden" + "play-add" + "play-clean"; + grid-area: playlist; +} +.play-box { + grid-area: play-box; +} + +.play-hidden { + grid-area: play-hidden; +} + +.play-add { + grid-area: play-add; + cursor: pointer; + + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-add:hover { + background-color: var(--buttom-hover); +} + +.play-clean { + display: grid; + grid-area: play-clean; +} + +.play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + border: 1px solid var(--button-border); + color: var(--buttom-text); + border-radius: 5px; +} +.play-clean > button:hover { + background-color: var(--buttom-hover); +} + +/* ------------- Menu Mobile sin JS ---------------- */ +/* input hidden */ +.opt-box { + display: none; +} +.dropdown-content { + display: none; + grid-area: dropdown-content; +} +label[for=options-toggle-cbox] { + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#options-toggle-cbox:checked ~ .dropdown-content { + display: inline-grid; + white-space: nowrap; + padding-left: 1rem; +} +/*- ----------- End Menu Mobile sin JS ------------- */ + +.main { + grid-area: main; + margin: 0 auto; + + display: grid; + grid-grap: 1px; + grid-template-columns: 1fr; + grid-template-areas: + "sc-video" + "sc-info"; +} +.sc-video { grid-area: sc-video; } +.sc-info { + display: grid; + grid-template-columns: 1fr; + grid-gap: 1px; + grid-template-areas: + "video-info" + "side-videos" + "comments-area-outer"; + grid-area: sc-info; +} +.video-info { + grid-area: video-info; + + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: + "v-title v-title" + "v-uploaded v-views" + "v-published v-likes-dislikes" + "external-player-controls v-checkbox" + "v-download v-download" + "v-description v-description" + "v-music-list v-music-list" + "v-more-info v-more-info"; +} +.v-title { + grid-area: v-title; + margin: 0px; +} +.v-uploaded { grid-area: v-uploaded; } +.v-views { + grid-area: v-views; + justify-self: end; +} +.v-published { grid-area: v-published; } +.v-likes-dislikes { + grid-area: v-likes-dislikes; + justify-self: end; +} +.external-player-controls { + grid-area: external-player-controls; +} +.external-player-controls input.speed { + width: 65px; + text-align: center; +} +.v-checkbox { + grid-area: v-checkbox; + justify-self: end; +} +.v-download { grid-area: v-download; } +.v-download > ul.download-dropdown-content { + background: var(--secondary-background); + padding-left: 0px; +} +.v-download > ul.download-dropdown-content > li.download-format { + list-style: none; + padding: 0.4rem 0; + padding-left: 1rem; +} +.v-download > ul.download-dropdown-content > li.download-format a.download-link { + text-decoration: none; +} + +.v-description { + grid-area: v-description; + background-color: var(--secondary-background); + margin-top: 0.4rem; + white-space: pre-wrap; + word-wrap: break-word; + padding: 5px; +} + +.v-music-list { + grid-area: v-music-list; + padding-bottom: 1rem; +} +.v-music-list table,th,td{ + border: 1px solid; +} +.v-music-list th,td{ + padding-left:4px; + padding-right:5px; +} +.v-music-list caption{ + text-align:left; + font-weight:bold; + margin-bottom:5px; +} + +.v-more-info { + grid-area: v-more-info; +} +.v-more-info > .more-info-content { + background-color: var(--secondary-background); + padding: 5px; +} + +.side-videos { grid-area: side-videos; } + +/* playlist items */ +.side-videos .site-playlist { + border-style: solid; + border-width: 2px; + border-color: var(--secondary-focus); + margin-bottom: 1rem; +} + +.side-videos .site-playlist .playlist-header { + background-color: var(--secondary-background); + padding: 1rem; + border-bottom-style: solid; + border-bottom-width: 2px; + border-bottom-color: var(--secondary-focus); +} + +.side-videos .site-playlist .playlist-header h3 { + margin: 0px; + padding: 0px; +} + +.side-videos .site-playlist .playlist-header .playlist-metadata { + list-style: none; + display: grid; + justify-content: start; + padding: 0; + margin: 0px; + grid-template-columns: repeat(3, auto); + grid-column-gap: 1rem; +} + +.side-videos .site-playlist .playlist-videos { + display: grid; + grid-row-gap: 1rem; + height: 300px; + overflow-y: scroll; + padding-top: 1rem; +} + +.side-videos .site-playlist .playlist-videos article.item-box { + padding-left: 1rem; +} +/* /playlist items */ + +.comments-area-outer { grid-area: comments-area-outer; } + +.related-videos-inner { + padding-top: 10px; + display: grid; + grid-row-gap: 1rem; +} + +.item-box { + display: grid; + grid-template-columns: 1.9fr 0.1fr; + grid-template-rows: 1fr; + grid-gap: 1px; + grid-template-areas: + "item-video item-checkbox"; +} + +.item-video { + grid-area: item-video; + + display: grid; + grid-template-columns: auto; + grid-template-rows: repeat(4, auto); + grid-row-gap: 0.4rem; + grid-template-areas: + "thumbnail-box" + "info-box"; + align-items: center; + + font-size: 0.7rem; +} + +.item-video a { + text-decoration: none; + cursor: pointer; +} + +.thumbnail-box { + grid-area: thumbnail-box; + position: relative; +} + +.thumbnail { + padding: 28.125%; + position: relative; + box-sizing: border-box; +} + +.thumbnail-img { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + object-fit: cover; +} + +.length { + z-index: 100; + position: absolute; + background-color: rgba(35, 35, 35, 0.75); + color: #fff; + border-radius: 2px; + padding: 2px; + font-size: 16px; + right: 0.25em; + bottom: -0.75em; +} + +.info-box { + grid-area: info-box; + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto auto auto auto auto; + grid-gap: 1px; + grid-template-areas: + "." + "." + "." + "." + "."; +} + +.title { + font-size: 0.8rem; + margin: 0px; + font-weight: normal; + overflow: hidden; + text-overflow: ellipsis; +} + +.info-box address { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.thumbnail-info { + background-color: var(--time-background); + color: #fff; + padding: 2px 5px; + text-transform: uppercase; + font-weight: 700; + font-size: 12px; + position: absolute; + right: 0; + bottom: .2rem; +} + +.item-checkbox { + grid-area: item-checkbox; + justify-self: start; + align-self: center; + min-width: 30px; + margin: 0px; +} + +.stats { + display: flex; + justify-content: space-between; +} + +/* comments */ +.comments-area { + display: grid; + grid-row-gap: 0.5rem; +} + +.comments { + display: grid; + grid-row-gap: 0.5rem; +} + +.comment { + display: grid; + grid-template-columns: repeat(3, auto) 3fr; + grid-template-rows: repeat(4, auto); + grid-column-gap: 0.4rem; + grid-template-areas: + "author-avatar author-name permalink permalink" + "author-avatar comment-text comment-text comment-text" + ". comment-likes comment-likes comment-likes" + ". button-row button-row button-row"; + background: var(--secondary-background); +} + +.author-avatar { grid-area: author-avatar; } +.author-name { grid-area: author-name; } +.permalink { grid-area: permalink; } +.comment-text { grid-area: comment-text; } +.comment-likes { grid-area: comment-likes; } +.button-row { grid-area: button-row; } + +.more-comments { + justify-self: center; + margin-top: 10px; + margin-bottom: 10px; + background: var(--secondary-background); + padding: 5px; + + /* disable text selection */ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.footer { + grid-area: footer; +} + +.footer > p { + text-align: center; +} + +@media (min-width: 480px) { + .item-video { + font-size: 0.85rem; + } + .title { + font-size: 1rem; + } +} + +@media (min-width: 992px) { + body { + display: grid; + grid-template-columns: 0.3fr 2fr 1fr 0.3fr; + grid-template-rows: auto 1fr auto; + grid-template-areas: + "header header header header" + ". main main ." + "footer footer footer footer"; + } + .form { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". search-box search-button dropdown"; + grid-area: form; + position: relative; + } + .dropdown { + display: grid; + grid-gap: 1px; + grid-template-columns: minmax(50px, 120px); + grid-template-areas: + "dropdown-label" + "dropdown-content"; + grid-area: dropdown; + + background: var(--background); + padding-right: 4rem; + + z-index: 1; + position: absolute; + } + .playlist { + display: grid; + grid-gap: 1px; + grid-template-columns: 1fr 1.4fr 0.3fr 1.3fr; + grid-template-areas: ". play-box play-add play-clean"; + grid-area: playlist; + } + .play-clean { + grid-template-columns: minmax(50px, 120px); + } + .play-clean > button { + padding-bottom: 6px; + padding-left: .75em; + padding-right: .75em; + padding-top: 6px; + text-align: center; + white-space: nowrap; + background-color: var(--buttom); + color: var(--buttom-text); + border-radius: 5px; + cursor: pointer; + } + + .main { + grid-area: main; + margin: 0px; + } + + .sc-info { + display: grid; + grid-template-columns: 3fr 1fr; + grid-gap: 1px 40px; + grid-template-areas: + "video-info side-videos" + "comments-area-outer side-videos" + ". side-videos"; + grid-area: sc-info; + } +} diff --git a/youtube/templates/base.html b/youtube/templates/base.html index 3d9f1e9..4d03c05 100644 --- a/youtube/templates/base.html +++ b/youtube/templates/base.html @@ -1,203 +1,238 @@ <!DOCTYPE html> <html> <head> - <meta charset="utf-8"> + <meta charset="UTF-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"/> + <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; media-src 'self' https://*.googlevideo.com; {{ "img-src 'self' https://*.googleusercontent.com https://*.ggpht.com https://*.ytimg.com;" if not settings.proxy_images else "" }}"> <title>{{ page_title }}</title> - <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; media-src 'self' https://*.googlevideo.com; - {{ "img-src 'self' https://*.googleusercontent.com https://*.ggpht.com https://*.ytimg.com;" if not settings.proxy_images else "" }}"> - <link href="{{ theme_path }}" type="text/css" rel="stylesheet"> - <link href="/youtube.com/shared.css" type="text/css" rel="stylesheet"> - <link href="/youtube.com/static/comments.css" type="text/css" rel="stylesheet"> - <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"> - <link title="Youtube local" href="/youtube.com/opensearch.xml" rel="search" type="application/opensearchdescription+xml"> - <style type="text/css"> -{% block style %} -{{ style }} -{% endblock %} - </style> + <link title="Youtube local" href="/youtube.com/opensearch.xml" rel="search" type="application/opensearchdescription+xml"/> + <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"/> + <link href="/youtube.com/static/normalize.css" rel="stylesheet"/> + <link href="{{ theme_path }}" rel="stylesheet"/> + {% block style %} + {{ style }} + {% endblock %} </head> - <body> - <header> - <a href="/youtube.com" id="home-link">Home</a> - <form id="site-search" action="/youtube.com/search"> - <input type="search" name="query" class="search-box" value="{{ search_box_value }}" - {{ "autofocus" if request.path == "/" else "" }}> - <button type="submit" value="Search" class="search-button">Search</button> - <div class="dropdown"> - <button class="dropdown-label">Options</button> - <div class="css-sucks"> - <div class="dropdown-content"> - <h3>Sort by</h3> - <input type="radio" id="sort_relevance" name="sort" value="0"> - <label for="sort_relevance">Relevance</label> - - <input type="radio" id="sort_upload_date" name="sort" value="2"> - <label for="sort_upload_date">Upload date</label> - - <input type="radio" id="sort_view_count" name="sort" value="3"> - <label for="sort_view_count">View count</label> - - <input type="radio" id="sort_rating" name="sort" value="1"> - <label for="sort_rating">Rating</label> - - - <h3>Upload date</h3> - <input type="radio" id="time_any" name="time" value="0"> - <label for="time_any">Any</label> - - <input type="radio" id="time_last_hour" name="time" value="1"> - <label for="time_last_hour">Last hour</label> - - <input type="radio" id="time_today" name="time" value="2"> - <label for="time_today">Today</label> - - <input type="radio" id="time_this_week" name="time" value="3"> - <label for="time_this_week">This week</label> - - <input type="radio" id="time_this_month" name="time" value="4"> - <label for="time_this_month">This month</label> - - <input type="radio" id="time_this_year" name="time" value="5"> - <label for="time_this_year">This year</label> - <h3>Type</h3> - <input type="radio" id="type_any" name="type" value="0"> - <label for="type_any">Any</label> - - <input type="radio" id="type_video" name="type" value="1"> - <label for="type_video">Video</label> - - <input type="radio" id="type_channel" name="type" value="2"> - <label for="type_channel">Channel</label> - - <input type="radio" id="type_playlist" name="type" value="3"> - <label for="type_playlist">Playlist</label> - - <input type="radio" id="type_movie" name="type" value="4"> - <label for="type_movie">Movie</label> - - <input type="radio" id="type_show" name="type" value="5"> - <label for="type_show">Show</label> - - - <h3>Duration</h3> - <input type="radio" id="duration_any" name="duration" value="0"> - <label for="duration_any">Any</label> + <body> + <header class="header"> + <nav class="home"> + <a href="/youtube.com" id="home-link">YouTube Local</a> + </nav> + <form class="form" id="site-search" action="/youtube.com/search"> + <input type="search" name="query" class="search-box" value="{{ search_box_value }}" + {{ "autofocus" if request.path == "/" else "" }} placeholder="Type to search..."> + <button type="submit" value="Search" class="search-button">Search</button> + <!-- options --> + <div class="dropdown"> + <!-- hidden box --> + <input id="options-toggle-cbox" class="opt-box" role="button" type="checkbox"> + <!-- end hidden box --> + <label class="dropdown-label" for="options-toggle-cbox">Options</label> + <div class="dropdown-content"> + <h3>Sort by</h3> + <div class="option"> + <input type="radio" id="sort_relevance" name="sort" value="0"> + <label for="sort_relevance">Relevance</label> + </div> + <div class="option"> + <input type="radio" id="sort_upload_date" name="sort" value="2"> + <label for="sort_upload_date">Upload date</label> + </div> + <div class="option"> + <input type="radio" id="sort_view_count" name="sort" value="3"> + <label for="sort_view_count">View count</label> + </div> + <div class="option"> + <input type="radio" id="sort_rating" name="sort" value="1"> + <label for="sort_rating">Rating</label> + </div> - <input type="radio" id="duration_short" name="duration" value="1"> - <label for="duration_short">Short (< 4 minutes)</label> + <h3>Upload date</h3> + <div class="option"> + <input type="radio" id="time_any" name="time" value="0"> + <label for="time_any">Any</label> + </div> + <div class="option"> + <input type="radio" id="time_last_hour" name="time" value="1"> + <label for="time_last_hour">Last hour</label> + </div> + <div class="option"> + <input type="radio" id="time_today" name="time" value="2"> + <label for="time_today">Today</label> + </div> + <div class="option"> + <input type="radio" id="time_this_week" name="time" value="3"> + <label for="time_this_week">This week</label> + </div> + <div class="option"> + <input type="radio" id="time_this_month" name="time" value="4"> + <label for="time_this_month">This month</label> + </div> + <div class="option"> + <input type="radio" id="time_this_year" name="time" value="5"> + <label for="time_this_year">This year</label> + </div> - <input type="radio" id="duration_long" name="duration" value="2"> - <label for="duration_long">Long (> 20 minutes)</label> + <h3>Type</h3> + <div class="option"> + <input type="radio" id="type_any" name="type" value="0"> + <label for="type_any">Any</label> + </div> + <div class="option"> + <input type="radio" id="type_video" name="type" value="1"> + <label for="type_video">Video</label> + </div> + <div class="option"> + <input type="radio" id="type_channel" name="type" value="2"> + <label for="type_channel">Channel</label> + </div> + <div class="option"> + <input type="radio" id="type_playlist" name="type" value="3"> + <label for="type_playlist">Playlist</label> + </div> + <div class="option"> + <input type="radio" id="type_movie" name="type" value="4"> + <label for="type_movie">Movie</label> + </div> + <div class="option"> + <input type="radio" id="type_show" name="type" value="5"> + <label for="type_show">Show</label> + </div> - </div> + <h3>Duration</h3> + <div class="option"> + <input type="radio" id="duration_any" name="duration" value="0"> + <label for="duration_any">Any</label> + </div> + <div class="option"> + <input type="radio" id="duration_short" name="duration" value="1"> + <label for="duration_short">Short (< 4 minutes)</label> + </div> + <div class="option"> + <input type="radio" id="duration_long" name="duration" value="2"> + <label for="duration_long">Long (> 20 minutes)</label> </div> </div> - </form> + </div> + </form> {% if header_playlist_names is defined %} - <form id="playlist-edit" action="/youtube.com/edit_playlist" method="post" target="_self"> - <input name="playlist_name" id="playlist-name-selection" list="playlist-options" type="text"> - <datalist id="playlist-options"> - {% for playlist_name in header_playlist_names %} - <option value="{{ playlist_name }}">{{ playlist_name }}</option> - {% endfor %} + <form class="playlist" id="playlist-edit" action="/youtube.com/edit_playlist" method="post" target="_self"> + <input class="play-box" name="playlist_name" id="playlist-name-selection" list="playlist-options" type="search" placeholder="I added your playlist..."> + <datalist class="play-hidden" id="playlist-options"> + {% for playlist_name in header_playlist_names %} + <option value="{{ playlist_name }}">{{ playlist_name }}</option> + {% endfor %} </datalist> - <button type="submit" id="playlist-add-button" name="action" value="add">Add to playlist</button> - <button type="reset" id="item-selection-reset">Clear selection</button> + <button class="play-add" type="submit" id="playlist-add-button" name="action" value="add">+List</button> + <div class="play-clean"> + <button type="reset" id="item-selection-reset">Clear selection</button> + </div> </form> + <script> - /* Takes control of the form if javascript is enabled, so that adding stuff to a playlist will not cause things to stop loading, and will display a status message. If javascript is disabled, the form will still work using regular HTML methods, but causes things on the page (such as the video) to stop loading. */ - var playlistAddForm = document.getElementById('playlist-edit'); - - function setStyle(element, property, value){ - element.style[property] = value; - } - function removeMessage(messageBox){ - messageBox.parentNode.removeChild(messageBox); - } - - function displayMessage(text, error=false){ - let currentMessageBox = document.getElementById('message-box'); - if(currentMessageBox !== null){ - currentMessageBox.parentNode.removeChild(currentMessageBox); - } - let messageBox = document.createElement('div'); - if(error){ - messageBox.setAttribute('role', 'alert'); - } else { - messageBox.setAttribute('role', 'status'); - } - messageBox.setAttribute('id', 'message-box'); - let textNode = document.createTextNode(text); - messageBox.appendChild(textNode); - document.querySelector('main').appendChild(messageBox); - let currentstyle = window.getComputedStyle(messageBox); - let removalDelay; - if(error){ - removalDelay = 5000; - } else { - removalDelay = 1500; - } - window.setTimeout(setStyle, 20, messageBox, 'opacity', 1); - window.setTimeout(setStyle, removalDelay, messageBox, 'opacity', 0); - window.setTimeout(removeMessage, removalDelay+300, messageBox); - } - // https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript - function sendData(event){ - var clicked_button = document.activeElement; - if(clicked_button === null || clicked_button.getAttribute('type') !== 'submit' || clicked_button.parentElement != event.target){ - console.log('ERROR: clicked_button not valid'); - return; - } - if(clicked_button.getAttribute('value') !== 'add'){ - return; // video(s) are being removed from playlist, just let it refresh the page - } - event.preventDefault(); - var XHR = new XMLHttpRequest(); - var FD = new FormData(playlistAddForm); - - if(FD.getAll('video_info_list').length === 0){ - displayMessage('Error: No videos selected', true); - return; - } - - if(FD.get('playlist_name') === ""){ - displayMessage('Error: No playlist selected', true); - return; - } - - // https://stackoverflow.com/questions/48322876/formdata-doesnt-include-value-of-buttons - FD.append('action', 'add'); - - XHR.addEventListener('load', function(event){ - if(event.target.status == 204){ - displayMessage('Added videos to playlist "' + FD.get('playlist_name') + '"'); - } else { - displayMessage('Error adding videos to playlist: ' + event.target.status.toString(), true); - } - }); - - XHR.addEventListener('error', function(event){ - if(event.target.status == 0){ - displayMessage('XHR failed: Check that XHR requests are allowed', true); - } else { - displayMessage('XHR failed: Unknown error', true); - } - }); - - XHR.open('POST', playlistAddForm.getAttribute('action')); - XHR.send(FD); - } - - playlistAddForm.addEventListener('submit', sendData); + (function main() { + /* Takes control of the form if javascript is enabled, so that adding stuff to a playlist will not cause things to stop loading, and will display a status message. If javascript is disabled, the form will still work using regular HTML methods, but causes things on the page (such as the video) to stop loading. */ + const playlistAddForm = document.getElementById('playlist-edit'); + + function setStyle(element, property, value){ + element.style[property] = value; + } + function removeMessage(messageBox){ + messageBox.parentNode.removeChild(messageBox); + } + + function displayMessage(text, error=false){ + let currentMessageBox = document.getElementById('message-box'); + if(currentMessageBox !== null){ + currentMessageBox.parentNode.removeChild(currentMessageBox); + } + let messageBox = document.createElement('div'); + if(error){ + messageBox.setAttribute('role', 'alert'); + } else { + messageBox.setAttribute('role', 'status'); + } + messageBox.setAttribute('id', 'message-box'); + let textNode = document.createTextNode(text); + messageBox.appendChild(textNode); + document.querySelector('main').appendChild(messageBox); + let currentstyle = window.getComputedStyle(messageBox); + let removalDelay; + if(error){ + removalDelay = 5000; + } else { + removalDelay = 1500; + } + window.setTimeout(setStyle, 20, messageBox, 'opacity', 1); + window.setTimeout(setStyle, removalDelay, messageBox, 'opacity', 0); + window.setTimeout(removeMessage, removalDelay+300, messageBox); + } + // https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript + function sendData(event){ + var clicked_button = document.activeElement; + if(clicked_button === null || clicked_button.getAttribute('type') !== 'submit' || clicked_button.parentElement != event.target){ + console.log('ERROR: clicked_button not valid'); + return; + } + if(clicked_button.getAttribute('value') !== 'add'){ + return; // video(s) are being removed from playlist, just let it refresh the page + } + event.preventDefault(); + var XHR = new XMLHttpRequest(); + var FD = new FormData(playlistAddForm); + + if(FD.getAll('video_info_list').length === 0){ + displayMessage('Error: No videos selected', true); + return; + } + + if(FD.get('playlist_name') === ""){ + displayMessage('Error: No playlist selected', true); + return; + } + + // https://stackoverflow.com/questions/48322876/formdata-doesnt-include-value-of-buttons + FD.append('action', 'add'); + + XHR.addEventListener('load', function(event){ + if(event.target.status == 204){ + displayMessage('Added videos to playlist "' + FD.get('playlist_name') + '"'); + } else { + displayMessage('Error adding videos to playlist: ' + event.target.status.toString(), true); + } + }); + + XHR.addEventListener('error', function(event){ + if(event.target.status == 0){ + displayMessage('XHR failed: Check that XHR requests are allowed', true); + } else { + displayMessage('XHR failed: Unknown error', true); + } + }); + + XHR.open('POST', playlistAddForm.getAttribute('action')); + XHR.send(FD); + } + + playlistAddForm.addEventListener('submit', sendData); + }()); </script> {% endif %} + </header> - <main> -{% block main %} -{{ main }} -{% endblock %} + <main class="main"> + + {% block main %} + {{ main }} + {% endblock %} + </main> + <footer class="footer"> + <p>This site is Free/Libre Software</p> + <p>Current version: 3304bab @ master</p> + </footer> </body> + </html> diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html index dddbff1..25b0971 100644 --- a/youtube/templates/channel.html +++ b/youtube/templates/channel.html @@ -7,114 +7,30 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:grid; -{% if current_tab == 'about' %} - grid-template-rows: 0fr 0fr 1fr; - grid-template-columns: 0fr 1fr; -{% else %} - grid-template-rows: repeat(5, 0fr); - grid-template-columns: auto 1fr; -{% endif %} - } - main .avatar{ - grid-row:1; - grid-column:1; - height:200px; - width:200px; - } - main .summary{ - grid-row:1; - grid-column:2; - margin-left: 5px; - } - .summary subscribe-unsubscribe, .summary short-description{ - margin-top: 10px; - } - main .channel-tabs{ - grid-row:2; - grid-column: 1 / span 2; - - display:grid; - grid-auto-flow: column; - justify-content:start; - - background-color: var(--interface-color); - padding: 3px; - padding-left: 6px; - } - #links-metadata{ - display: grid; - grid-auto-flow: column; - grid-column-gap: 10px; - grid-column: 1/span 2; - justify-content: start; - padding-top: 8px; - padding-bottom: 8px; - padding-left: 6px; - margin-bottom: 10px; - } - #number-of-results{ - font-weight:bold; - } - .content{ - grid-row: 4; - grid-column: 1 / span 2; - } - .search-content{ - width: 800px; - margin-left: 10px; - } - .item-grid{ - padding-left: 20px; - } - .item-list{ - width:800px; - margin: auto; - } - .page-button-row{ - margin-left: auto; - margin-right: auto; - } - .next-previous-button-row{ - margin-left: auto; - margin-right: auto; - } - .tab{ - padding: 5px 75px; - } - .channel-info{ - grid-row: 3; - grid-column: 1 / span 3; - } - .channel-info ul{ - padding-left: 40px; - } - .channel-info h3{ - margin-left: 40px; - } - .channel-info .description{ - white-space: pre-wrap; - min-width: 0; - margin-left: 40px; - } - .medium-item img{ - max-width: 168px; - } + <link href="/youtube.com/static/channel.css" rel="stylesheet"> {% endblock style %} {% block main %} - <img class="avatar" src="{{ avatar }}"> - <div class="summary"> - <h2 class="title">{{ channel_name }}</h2> - <p class="short-description">{{ short_description }}</p> - <form method="POST" action="/youtube.com/subscriptions" class="subscribe-unsubscribe"> - <input type="submit" value="{{ 'Unsubscribe' if subscribed else 'Subscribe' }}"> - <input type="hidden" name="channel_id" value="{{ channel_id }}"> - <input type="hidden" name="channel_name" value="{{ channel_name }}"> - <input type="hidden" name="action" value="{{ 'unsubscribe' if subscribed else 'subscribe' }}"> - </form> + + <div class="author-container"> + <div class="author"> + <img alt="{{ channel_name }}" src="{{ avatar }}"/> + <h2>{{ channel_name }}</h2> + </div> + <div class="summary"> + <p>{{ short_description }}</p> + </div> + <div class="subscribe"> + <form method="POST" action="/youtube.com/subscriptions" class="subscribe-unsubscribe"> + <input class="btn-subscribe" type="submit" value="{{ 'Unsubscribe' if subscribed else 'Subscribe' }}"> + <input type="hidden" name="channel_id" value="{{ channel_id }}"> + <input type="hidden" name="channel_name" value="{{ channel_name }}"> + <input type="hidden" name="action" value="{{ 'unsubscribe' if subscribed else 'subscribe' }}"> + </form> + </div> </div> + <hr/> + <nav class="channel-tabs"> {% for tab_name in ('Videos', 'Playlists', 'About') %} {% if tab_name.lower() == current_tab %} @@ -153,8 +69,9 @@ </ul> </div> {% else %} - <div class="content {{ current_tab + '-content'}}"> - <div id="links-metadata"> + + <!-- new--> + <div id="links-metadata"> {% if current_tab == 'videos' %} {% set sorts = [('1', 'views'), ('2', 'oldest'), ('3', 'newest')] %} <div id="number-of-results">{{ number_of_videos }} videos</div> @@ -177,16 +94,18 @@ <a class="sort-button" href="{{ channel_url + '/' + current_tab + '?sort=' + sort_number }}">{{ 'Sort by ' + sort_name }}</a> {% endif %} {% endfor %} - </div> + </div> - <nav class="{{ 'item-list' if current_tab == 'search' else 'item-grid' }}"> + <div class="video-container {{ current_tab + '-content'}}"> {% for item_info in items %} {{ common_elements.item(item_info, include_author=false) }} {% endfor %} - </nav> + </div> + <hr/> + <footer class="pagination-container"> {% if current_tab == 'videos' %} - <nav class="page-button-row"> + <nav class="pagination-list"> {{ common_elements.page_buttons(number_of_pages, channel_url + '/' + current_tab, parameters_dictionary) }} </nav> {% elif current_tab == 'search' %} @@ -194,6 +113,8 @@ {{ common_elements.next_previous_buttons(is_last_page, channel_url + '/' + current_tab, parameters_dictionary) }} </nav> {% endif %} - </div> + </footer> + <!-- /new--> {% endif %} + {% endblock main %} diff --git a/youtube/templates/comments.html b/youtube/templates/comments.html index ad5ab02..a128b44 100644 --- a/youtube/templates/comments.html +++ b/youtube/templates/comments.html @@ -5,23 +5,24 @@ <div class="comment"> <a class="author-avatar" href="{{ comment['author_url'] }}" title="{{ comment['author'] }}"> {% if include_avatar %} - <img class="author-avatar-img" src="{{ comment['author_avatar'] }}"> + <img class="author-avatar-img" alt="{{ comment['author'] }}" src="{{ comment['author_avatar'] }}"> {% endif %} </a> - <address> + <address class="author-name"> <a class="author" href="{{ comment['author_url'] }}" title="{{ comment['author'] }}">{{ comment['author'] }}</a> </address> <a class="permalink" href="{{ comment['permalink'] }}" title="permalink"> - <time datetime="">{{ comment['time_published'] }}</time> + <time datetime="2012-01-14T15:23:44+02:00">{{ comment['time_published'] }}</time> </a> + {% if timestamp_links %} - <span class="text">{{ common_elements.text_runs(comment['text'])|timestamps|safe }}</span> + <span class="comment-text">{{ common_elements.text_runs(comment['text'])|timestamps|safe }}</span> {% else %} - <span class="text">{{ common_elements.text_runs(comment['text']) }}</span> + <span class="comment-text">{{ common_elements.text_runs(comment['text']) }}</span> {% endif %} - <span class="likes">{{ comment['likes_text'] if comment['like_count'] else ''}}</span> - <div class="bottom-row"> + <span class="comment-likes">{{ comment['likes_text'] if comment['like_count'] else ''}}</span> + <div class="button-row"> {% if settings.use_comments_js and comment['reply_count'] %} <details class="replies" src="{{ comment['replies_url'] }}"> <summary>{{ comment['view_replies_text'] }}</summary> @@ -33,7 +34,6 @@ {% endif %} </div> </div> - </div> {% endmacro %} @@ -57,8 +57,5 @@ <a class="page-button more-comments" href="{{ comments_info['more_comments_url'] }}">More comments</a> {% endif %} {% endif %} -{% endmacro %} - - - +{% endmacro %} diff --git a/youtube/templates/comments_page.html b/youtube/templates/comments_page.html index 2d914af..09230f5 100644 --- a/youtube/templates/comments_page.html +++ b/youtube/templates/comments_page.html @@ -3,16 +3,11 @@ {% if not slim %} {% extends "base.html" %} - {% block style %} - .comments-area{ - margin: auto; - width:640px; - } + <link href="/youtube.com/static/comments.css" rel="stylesheet"> {% endblock style %} {% endif %} - {% block main %} <section class="comments-area"> {% if not comments_info['is_replies'] %} @@ -27,7 +22,6 @@ </section> {% endif %} - {% if not comments_info['is_replies'] %} <div class="comment-links"> {% for link_text, link_url in comments_info['comment_links'] %} @@ -51,5 +45,3 @@ <script src="/youtube.com/static/js/comments.js"></script> {% endif %} {% endblock main %} - - diff --git a/youtube/templates/common_elements.html b/youtube/templates/common_elements.html index 0587ce3..fcd4125 100644 --- a/youtube/templates/common_elements.html +++ b/youtube/templates/common_elements.html @@ -15,60 +15,55 @@ {% endmacro %} {% macro item(info, description=false, horizontal=true, include_author=true, include_badges=true, lazy_load=false) %} - <div class="item-box {{ info['type'] + '-item-box' }} {{'horizontal-item-box' if horizontal else 'vertical-item-box'}} {{'has-description' if description else 'no-description'}}"> + <article class="item-box"> {% if info['error'] %} {{ info['error'] }} {% else %} - <div class="item {{ info['type'] + '-item' }}"> + <div class="item-video {{ info['type'] + '-item' }}"> <a class="thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - {% if lazy_load %} - <img class="thumbnail-img lazy" data-src="{{ info['thumbnail'] }}"> - {% else %} - <img class="thumbnail-img" src="{{ info['thumbnail'] }}"> - {% endif %} - {% if info['type'] != 'channel' %} - <div class="thumbnail-info"> - <span>{{ (info['video_count']|commatize + ' videos') if info['type'] == 'playlist' else info['duration'] }}</span> - </div> - {% endif %} - </a> + <div class="thumbnail {% if info['type'] == 'channel' %} channel {% endif %}"> + {% if lazy_load %} + <img class="thumbnail-img lazy" alt="thumbnail" data-src="{{ info['thumbnail'] }}"> + {% elif info['type'] == 'channel' %} + <img class="thumbnail-img channel" alt="thumbnail" src="{{ info['thumbnail'] }}"> + {% else %} + <img class="thumbnail-img" alt="thumbnail" src="{{ info['thumbnail'] }}"> + {% endif %} - <div class="title"><a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a></div> + {% if info['type'] != 'channel' %} + <p class="length">{{ (info['video_count']|commatize + ' videos') if info['type'] == 'playlist' else info['duration'] }}</p> + {% endif %} + </div> + </a> + <h4 class="title"><a href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a></h4> {% if include_author %} {% if info.get('author_url') %} - <address title="{{ info['author'] }}">By <a href="{{ info['author_url'] }}">{{ info['author'] }}</a></address> + <address title="{{ info['author'] }}"><b><a href="{{ info['author_url'] }}">{{ info['author'] }}</a></b></address> {% else %} <address title="{{ info['author'] }}"><b>{{ info['author'] }}</b></address> {% endif %} {% endif %} - <ul class="stats {{'horizontal-stats' if horizontal else 'vertical-stats'}}"> + + <div class="stats {{'horizontal-stats' if horizontal else 'vertical-stats'}}"> {% if info['type'] == 'channel' %} - <li><span>{{ info['approx_subscriber_count'] }} subscribers</span></li> - <li><span>{{ info['video_count']|commatize }} videos</span></li> + <div>{{ info['approx_subscriber_count'] }} subscribers</div> + <div>{{ info['video_count']|commatize }} videos</div> {% else %} - {% if info.get('approx_view_count') %} - <li><span class="views">{{ info['approx_view_count'] }} views</span></li> - {% endif %} {% if info.get('time_published') %} - <li><time>{{ info['time_published'] }}</time></li> + <time>{{ info['time_published'] }}</time> + {% endif %} + {% if info.get('approx_view_count') %} + <div class="views">{{ info['approx_view_count'] }} views</div> {% endif %} {% endif %} - </ul> - - {% if description %} - <span class="description">{{ text_runs(info.get('description', '')) }}</span> - {% endif %} - {% if include_badges %} - <span class="badges">{{ info['badges']|join(' | ') }}</span> - {% endif %} + </div> </div> {% if info['type'] == 'video' %} <input class="item-checkbox" type="checkbox" name="video_info_list" value="{{ info['video_info'] }}" form="playlist-edit"> {% endif %} {% endif %} - </div> - + </article> {% endmacro %} {% macro page_buttons(estimated_pages, url, parameters_dictionary) %} @@ -84,12 +79,12 @@ {% for page in range(page_start, page_end+1) %} {% if page == current_page %} - <div class="page-button">{{ page }}</div> + <div class="page-link is-current">{{ page }}</div> {% else %} {# IMPORTANT: Jinja SUCKS #} {# https://stackoverflow.com/questions/36886650/how-to-add-a-new-entry-into-a-dictionary-object-while-using-jinja2 #} {% set _ = parameters_dictionary.__setitem__('page', page) %} - <a class="page-button" href="{{ url + '?' + parameters_dictionary|urlencode }}">{{ page }}</a> + <a class="page-link" href="{{ url + '?' + parameters_dictionary|urlencode }}">{{ page }}</a> {% endif %} {% endfor %} @@ -101,11 +96,11 @@ {% if current_page != 1 %} {% set _ = parameters_dictionary.__setitem__('page', current_page - 1) %} - <a class="page-button previous-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Previous page</a> + <a class="page-link previous-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Previous page</a> {% endif %} {% if not is_last_page %} {% set _ = parameters_dictionary.__setitem__('page', current_page + 1) %} - <a class="page-button next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> + <a class="page-link next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> {% endif %} {% endmacro %} diff --git a/youtube/templates/error.html b/youtube/templates/error.html index c3f58b0..e02ecd9 100644 --- a/youtube/templates/error.html +++ b/youtube/templates/error.html @@ -4,9 +4,13 @@ {% extends "base.html" %} {% endif %} +{% block style %} + <link href="/youtube.com/static/home.css" rel="stylesheet"> +{% endblock style %} + {% block main %} {% if traceback %} - <div id="error-box"> + <div class="code-error" id="error-box"> <h1>500 Uncaught exception:</h1> <div class="code-box"><code>{{ traceback }}</code></div> <p>Please report this issue at <a href="https://github.com/user234683/youtube-local/issues" target="_blank">https://github.com/user234683/youtube-local/issues</a></p> @@ -16,4 +20,3 @@ <div id="error-message">{{ error_message }}</div> {% endif %} {% endblock %} - diff --git a/youtube/templates/home.html b/youtube/templates/home.html index 9890f5e..0adac56 100644 --- a/youtube/templates/home.html +++ b/youtube/templates/home.html @@ -1,16 +1,7 @@ {% set page_title = title %} {% extends "base.html" %} {% block style %} - ul { - background-color: var(--interface-color); - padding: 20px; - width: 400px; - margin: auto; - margin-top: 20px; - } - li { - margin-bottom: 10px; - } + <link href="/youtube.com/static/home.css" rel="stylesheet"> {% endblock style %} {% block main %} <ul> diff --git a/youtube/templates/local_playlist.html b/youtube/templates/local_playlist.html index 803c4dc..bbe5edd 100644 --- a/youtube/templates/local_playlist.html +++ b/youtube/templates/local_playlist.html @@ -2,41 +2,23 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main > *{ - width: 800px; - margin: auto; - } - - .playlist-metadata{ - display: flex; - flex-direction: row; - justify-content: space-between; - } - .playlist-title{ - } - #playlist-remove-button{ - align-self: center; - white-space: nowrap; - } - #results{ - display: grid; - grid-auto-rows: 0fr; - grid-row-gap: 10px; - } + <link href="/youtube.com/static/local_playlist.css" rel="stylesheet"> {% endblock style %} {% block main %} <div class="playlist-metadata"> - <h2 class="playlist-title">{{ playlist_name }}</h2> + <h2 class="play-title">{{ playlist_name }}</h2> <input type="hidden" name="playlist_page" value="{{ playlist_name }}" form="playlist-edit"> - <button type="submit" id="playlist-remove-button" name="action" value="remove" form="playlist-edit" formaction="">Remove from playlist</button> + <button class="play-action" type="submit" id="playlist-remove-button" name="action" value="remove" form="playlist-edit" formaction="">Remove from playlist</button> </div> - <div id="results"> + <div id="results" class="video-container"> {% for video_info in videos %} {{ common_elements.item(video_info) }} {% endfor %} </div> - <nav class="page-button-row"> - {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlists/' + playlist_name, parameters_dictionary) }} - </nav> + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlists/' + playlist_name, parameters_dictionary) }} + </nav> + </footer> {% endblock main %} diff --git a/youtube/templates/local_playlists_list.html b/youtube/templates/local_playlists_list.html index a9fccff..f400808 100644 --- a/youtube/templates/local_playlists_list.html +++ b/youtube/templates/local_playlists_list.html @@ -2,20 +2,7 @@ {% extends "base.html" %} {% block style %} -main{ - display: flex; - justify-content: center; -} - ul{ - background-color: var(--interface-color); - margin-top: 20px; - padding: 20px; - min-width: 400px; - align-self: start; - } - li{ - margin-bottom: 10px; - } + <link href="/youtube.com/static/home.css" rel="stylesheet"> {% endblock style %} {% block main %} @@ -25,9 +12,3 @@ main{ {% endfor %} </ul> {% endblock main %} - - - - - - diff --git a/youtube/templates/playlist.html b/youtube/templates/playlist.html index af86c82..8c88cc7 100644 --- a/youtube/templates/playlist.html +++ b/youtube/templates/playlist.html @@ -2,77 +2,39 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main > * { - width: 800px; - margin:auto; - } - - .playlist-metadata{ - display:grid; - grid-template-columns: 0fr 1fr; - } - .playlist-thumbnail{ - grid-row: 1 / span 5; - grid-column:1; - justify-self:start; - width:250px; - margin-right: 10px; - } - .playlist-title{ - grid-row: 1; - grid-column:2; - } - .playlist-author{ - grid-row:2; - grid-column:2; - } - .playlist-stats{ - grid-row:3; - grid-column:2; - } - - .playlist-description{ - grid-row:4; - grid-column:2; - min-width:0px; - white-space: pre-line; - } - - #results{ - margin-top:10px; - - display: grid; - grid-auto-rows: 0fr; - grid-row-gap: 10px; - - } + <link href="/youtube.com/static/playlist.css" rel="stylesheet"> {% endblock style %} {% block main %} + <div class="playlist-metadata"> - <img class="playlist-thumbnail" src="{{ thumbnail }}"> - <h2 class="playlist-title">{{ title }}</h2> - <a class="playlist-author" href="{{ author_url }}">{{ author }}</a> + <div class="author"> + <img alt="{{ title }}" src="{{ thumbnail }}"/> + <h2>{{ title }}</h2> + </div> + <div class="summary"> + <a class="playlist-author" href="{{ author_url }}">{{ author }}</a> + </div> <div class="playlist-stats"> <div>{{ video_count|commatize }} videos</div> <div>{{ view_count|commatize }} views</div> <div>Last updated {{ time_published }}</div> </div> - <div class="playlist-description">{{ common_elements.text_runs(description) }}</div> </div> + <hr/> - <div id="results"> + + <div id="results" class="video-container"> {% for info in video_list %} {{ common_elements.item(info) }} {% endfor %} </div> - <nav class="page-button-row"> - {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlist', parameters_dictionary) }} - </nav> -{% endblock main %} - - - - + <hr/> + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlist', parameters_dictionary) }} + </nav> + </footer> +{% endblock main %} diff --git a/youtube/templates/search.html b/youtube/templates/search.html index 8b803e7..e742099 100644 --- a/youtube/templates/search.html +++ b/youtube/templates/search.html @@ -3,44 +3,31 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main > * { - max-width: 800px; - margin: auto; - } - #result-info{ - margin-top: 10px; - margin-bottom: 10px; - padding-left: 10px; - padding-right: 10px; - } - #number-of-results{ - font-weight:bold; - } - .item-list{ - padding-left: 10px; - padding-right: 10px; - } - .badge{ - background-color:#cccccc; - } + <link href="/youtube.com/static/search.css" rel="stylesheet"> {% endblock style %} {% block main %} - <div id="result-info"> - <div id="number-of-results">Approximately {{ '{:,}'.format(estimated_results) }} results ({{ '{:,}'.format(estimated_pages) }} pages)</div> -{% if corrections['type'] == 'showing_results_for' %} - <div>Showing results for <a>{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> - <div>Search instead for <a href="{{ corrections['original_query_url'] }}">{{ corrections['original_query_text'] }}</a></div> -{% elif corrections['type'] == 'did_you_mean' %} - <div>Did you mean <a href="{{ corrections['corrected_query_url'] }}">{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> -{% endif %} - </div> - <div class="item-list"> - {% for info in results %} - {{ common_elements.item(info, description=true) }} - {% endfor %} - </div> - <nav class="page-button-row"> - {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/search', parameters_dictionary) }} - </nav> + <div class="result-info" id="result-info"> + <div id="number-of-results">Approximately {{ '{:,}'.format(estimated_results) }} results ({{ '{:,}'.format(estimated_pages) }} pages)</div> + {% if corrections['type'] == 'showing_results_for' %} + <div>Showing results for <a>{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> + <div>Search instead for <a href="{{ corrections['original_query_url'] }}">{{ corrections['original_query_text'] }}</a></div> + {% elif corrections['type'] == 'did_you_mean' %} + <div>Did you mean <a href="{{ corrections['corrected_query_url'] }}">{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> + {% endif %} + </div> + + <!-- video item --> + <div class="video-container"> + {% for info in results %} + {{ common_elements.item(info, description=true) }} + {% endfor %} + </div> + <hr/> + <!-- /video item --> + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/search', parameters_dictionary) }} + </nav> + </footer> {% endblock main %} diff --git a/youtube/templates/settings.html b/youtube/templates/settings.html index 19817dd..5385c68 100644 --- a/youtube/templates/settings.html +++ b/youtube/templates/settings.html @@ -1,28 +1,7 @@ {% set page_title = 'Settings' %} {% extends "base.html" %} -{% import "common_elements.html" as common_elements %} {% block style %} - .settings-form { - margin: auto; - width: 600px; - margin-top:10px; - padding: 10px; - display: block; - background-color: var(--interface-color); - } - .settings-list{ - list-style: none; - padding: 0px; - } - .setting-item{ - margin-bottom: 10px; - padding: 5px; - } - .setting-item label{ - display: inline-block; - width: 250px; - } - + <link href="/youtube.com/static/settings.css" rel="stylesheet"> {% endblock style %} {% block main %} @@ -30,37 +9,37 @@ {% for categ in categories %} <h2>{{ categ|capitalize }}</h2> <ul class="settings-list"> - {% for setting_name, setting_info, value in settings_by_category[categ] %} - {% if not setting_info.get('hidden', false) %} - <li class="setting-item"> - {% if 'label' is in(setting_info) %} - <label for="{{ 'setting_' + setting_name }}">{{ setting_info['label'] }}</label> - {% else %} - <label for="{{ 'setting_' + setting_name }}">{{ setting_name.replace('_', ' ')|capitalize }}</label> - {% endif %} - - {% if setting_info['type'].__name__ == 'bool' %} - <input type="checkbox" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" {{ 'checked' if value else '' }}> - {% elif setting_info['type'].__name__ == 'int' %} - {% if 'options' is in(setting_info) %} - <select id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}"> - {% for option in setting_info['options'] %} - <option value="{{ option[0] }}" {{ 'selected' if option[0] == value else '' }}>{{ option[1] }}</option> - {% endfor %} - </select> + {% for setting_name, setting_info, value in settings_by_category[categ] %} + {% if not setting_info.get('hidden', false) %} + <li class="setting-item"> + {% if 'label' is in(setting_info) %} + <label for="{{ 'setting_' + setting_name }}">{{ setting_info['label'] }}</label> {% else %} - <input type="number" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}" step="1"> + <label for="{{ 'setting_' + setting_name }}">{{ setting_name.replace('_', ' ')|capitalize }}</label> {% endif %} - {% elif setting_info['type'].__name__ == 'float' %} - {% elif setting_info['type'].__name__ == 'str' %} - <input type="text" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}"> - {% else %} - <span>Error: Unknown setting type: setting_info['type'].__name__</span> - {% endif %} - </li> - {% endif %} - {% endfor %} + {% if setting_info['type'].__name__ == 'bool' %} + <input type="checkbox" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" {{ 'checked' if value else '' }}> + {% elif setting_info['type'].__name__ == 'int' %} + {% if 'options' is in(setting_info) %} + <select id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}"> + {% for option in setting_info['options'] %} + <option value="{{ option[0] }}" {{ 'selected' if option[0] == value else '' }}>{{ option[1] }}</option> + {% endfor %} + </select> + {% else %} + <input type="number" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}" step="1"> + {% endif %} + {% elif setting_info['type'].__name__ == 'float' %} + + {% elif setting_info['type'].__name__ == 'str' %} + <input type="text" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}"> + {% else %} + <span>Error: Unknown setting type: setting_info['type'].__name__</span> + {% endif %} + </li> + {% endif %} + {% endfor %} </ul> {% endfor %} <input type="submit" value="Save settings"> diff --git a/youtube/templates/subscription_manager.html b/youtube/templates/subscription_manager.html index 4d798e0..3b8a5c0 100644 --- a/youtube/templates/subscription_manager.html +++ b/youtube/templates/subscription_manager.html @@ -1,81 +1,7 @@ {% set page_title = 'Subscription Manager' %} {% extends "base.html" %} {% block style %} - .import-export{ - display: flex; - flex-direction: row; - padding-left: 10px; - padding-top: 10px; - } - .subscriptions-import-form{ - background-color: var(--interface-color); - display: flex; - flex-direction: column; - align-items: flex-start; - max-width: 300px; - padding:10px; - } - .subscriptions-import-form h2{ - font-size: 1.25rem; - margin-bottom: 10px; - } - - .import-submit-button{ - margin-top:15px; - align-self: flex-end; - } - - - .subscriptions-export-links{ - margin: 0px 0px 0px 20px; - background-color: var(--interface-color); - list-style: none; - max-width: 300px; - padding:10px; - } - - .sub-list-controls{ - background-color: var(--interface-color); - padding:10px; - } - - - .tag-group-list{ - list-style: none; - margin-left: 10px; - margin-right: 10px; - padding: 0px; - } - .tag-group{ - border-style: solid; - margin-bottom: 10px; - } - - .sub-list{ - list-style: none; - padding:10px; - column-width: 300px; - column-gap: 40px; - } - .sub-list-item{ - display:flex; - margin-bottom: 10px; - break-inside:avoid; - } - .sub-list-item:not(.muted){ - background-color: var(--interface-color); - } - .tag-list{ - margin-left:15px; - font-weight:bold; - } - .sub-list-item-name{ - margin-left:15px; - } - .sub-list-checkbox{ - height: 1.5em; - min-width: 1.5em; // need min-width otherwise browser doesn't respect the width and squishes the checkbox down when there's too many tags - } + <link href="/youtube.com/static/subscription_manager.css" rel="stylesheet"> {% endblock style %} @@ -89,8 +15,6 @@ {% endfor %} {% endmacro %} - - {% block main %} <div class="import-export"> <form class="subscriptions-import-form" enctype="multipart/form-data" action="/youtube.com/import_subscriptions" method="POST"> diff --git a/youtube/templates/subscriptions.html b/youtube/templates/subscriptions.html index e2fb25c..9e48c6b 100644 --- a/youtube/templates/subscriptions.html +++ b/youtube/templates/subscriptions.html @@ -7,79 +7,21 @@ {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:flex; - flex-direction: row; - } - .video-section{ - flex-grow: 1; - padding-left: 10px; - padding-top: 10px; - } - .current-tag{ - margin-bottom:10px; - } - .video-section .page-button-row{ - justify-content: center; - } - .subscriptions-sidebar{ - flex-basis: 300px; - background-color: var(--interface-color); - border-left: 2px; - } - .sidebar-links{ - display:flex; - justify-content: space-between; - padding-left:10px; - padding-right: 10px; - } - - .sidebar-list{ - list-style: none; - padding-left:10px; - padding-right: 10px; - } - .sidebar-list-item{ - display:flex; - justify-content: space-between; - margin-bottom: 5px; - } - .sub-refresh-list .sidebar-item-name{ - text-overflow: clip; - white-space: nowrap; - overflow: hidden; - max-width: 200px; - } + <link href="/youtube.com/static/subscription.css" rel="stylesheet"> {% endblock style %} {% block main %} - <div class="video-section"> - {% if current_tag %} - <h2 class="current-tag">{{ current_tag }}</h2> - {% endif %} - <nav class="item-grid"> - {% for video_info in videos %} - {{ common_elements.item(video_info) }} - {% endfor %} - </nav> - - <nav class="page-button-row"> - {{ common_elements.page_buttons(num_pages, '/youtube.com/subscriptions', parameters_dictionary) }} - </nav> - </div> <div class="subscriptions-sidebar"> <div class="sidebar-links"> - <a href="/youtube.com/subscription_manager" class="sub-manager-link">Subscription Manager</a> - <form method="POST" class="refresh-all"> + <a class="sidebar-title" href="/youtube.com/subscription_manager" class="sub-manager-link">Subscription Manager</a> + <form class="sidebar-action" method="POST" class="refresh-all"> <input type="submit" value="Check All"> <input type="hidden" name="action" value="refresh"> <input type="hidden" name="type" value="all"> </form> </div> - <hr> - <ol class="sidebar-list tags"> {% if current_tag %} <li class="sidebar-list-item"> @@ -105,7 +47,6 @@ </ol> <hr> - <ol class="sidebar-list sub-refresh-list"> {% for subscription in subscription_list %} <li class="sidebar-list-item {{ 'muted' if subscription['muted'] else '' }}"> @@ -119,7 +60,23 @@ </li> {% endfor %} </ol> + </div> + + {% if current_tag %} + <h2 class="current-tag">{{ current_tag }}</h2> + {% endif %} + <div class="video-container"> + {% for video_info in videos %} + {{ common_elements.item(video_info) }} + {% endfor %} </div> + <hr/> + + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(num_pages, '/youtube.com/subscriptions', parameters_dictionary) }} + </nav> + </footer> {% endblock main %} diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html index 36b2198..44fc5a7 100644 --- a/youtube/templates/watch.html +++ b/youtube/templates/watch.html @@ -3,378 +3,17 @@ {% import "common_elements.html" as common_elements %} {% import "comments.html" as comments with context %} {% block style %} - details > summary{ - background-color: var(--interface-color); - border-style: outset; - border-width: 2px; - font-weight: bold; - padding-bottom: 2px; - } - details > summary:hover{ - text-decoration: underline; - } - - .playability-error{ - height: 360px; - width: 640px; - grid-column: 2; - background-color: var(--video-background-color); - text-align:center; - } - .playability-error span{ - position: relative; - top: 50%; - transform: translate(-50%, -50%); - } - - .live-url-choices{ - height: 360px; - width: 640px; - grid-column: 2; - background-color: var(--video-background-color); - padding: 25px 0px 0px 25px; - } - .live-url-choices ol{ - list-style: none; - padding:0px; - margin:0px; - margin-top: 15px; - } - .live-url-choices input{ - width: 400px; - } - .url-choice-label{ - display: inline-block; - width: 150px; - } - - {% if settings.theater_mode %} - /* This is the constant aspect ratio trick - Percentages in padding-top declarations are based on the width of the - parent element. We can use this trick to achieve a constant aspect ratio - for video-container-inner by setting height to 0. - - So the video height will decrease if the browser window is narrow, - but it will keep same aspect ratio. Must use absolute positioning on - video to keep it inside its container since the container's height is 0. - - However, because we widen the video player longer than the video's - intrinsic width for long video to make scrubbing easier, we can't use - the aspect ratio to set the height. The height needs to be the - intrinsic height in these cases. So we use a media query so aspect - ratio trick is only used if page width is less than or equal to - intrinsic video width. - */ - #video-container{ - grid-column: 1 / span 5; - justify-self: center; - max-width: 100%; - width: {{ theater_video_target_width }}px; - margin-bottom: 10px; - } - #video-container-inner{ - height: {{ video_height}}px; - position: relative; - } - @media(max-width:{{ video_width }}px){ - #video-container-inner{ - padding-top: calc(100%*{{video_height}}/{{video_width}}); - height: 0px; - } - } - video{ - background-color: var(--video-background-color); - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: 100%; - } - .side-videos{ - grid-row: 2 /span 3; - width: 400px; - } - .video-info{ - width: 640px; - } - {% else %} - #video-container{ - grid-column: 2; - } - #video-container, #video-container-inner, video{ - height: 360px; - width: 640px; - } - .side-videos{ - grid-row: 1 /span 4; - } - {% endif %} - - main{ - display:grid; - grid-template-columns: 1fr 640px 40px 400px 1fr; - grid-template-rows: auto auto auto auto; - align-content: start; - } - - .video-info{ - grid-column: 2; - grid-row: 2; - display: grid; - grid-template-rows: 0fr 0fr 0fr 20px 0fr 0fr; - grid-template-columns: 1fr 1fr; - align-content: start; - } - .video-info > .title{ - grid-column: 1 / span 2; - min-width: 0; - } - .video-info > .labels{ - justify-self:start; - list-style: none; - padding: 0px; - margin: 5px 0px; - } - .video-info > .labels:empty{ - margin: 0px; - } - .labels > li{ - display: inline; - margin-right:5px; - background-color: var(--interface-color); - padding: 2px 5px; - border-style: solid; - border-width: 1px; - } - .video-info > address{ - grid-column: 1; - grid-row: 3; - justify-self: start; - } - .video-info > .views{ - grid-column: 2; - grid-row: 3; - justify-self:end; - } - .video-info > time{ - grid-column: 1; - grid-row: 4; - justify-self:start; - } - .video-info > .likes-dislikes{ - grid-column: 2; - grid-row: 4; - justify-self:end; - } - .video-info > .external-player-controls{ - justify-self: start; - grid-row: 5; - grid-column: 1; - margin-bottom: 8px; - } - #speed-control{ - width: 65px; - text-align: center; - background-color: var(--interface-color); - color: var(--text-color); - } - .video-info > .checkbox{ - justify-self:end; - align-self: start; - - grid-row: 5; - grid-column: 2; - } - .video-info > .download-dropdown{ - grid-column:1 / span 2; - grid-row: 6; - } - .video-info > .description{ - background-color:var(--interface-color); - margin-top:8px; - white-space: pre-wrap; - min-width: 0; - word-wrap: break-word; - grid-column: 1 / span 2; - grid-row: 7; - padding: 5px; - } - - .music-list{ - grid-row:8; - grid-column: 1 / span 2; - background-color: var(--interface-color); - padding-bottom: 7px; - } - .music-list table,th,td{ - border: 1px solid; - } - .music-list th,td{ - padding-left:4px; - padding-right:5px; - } - .music-list caption{ - text-align:left; - font-weight:bold; - margin-bottom:5px; - } - .more-info{ - grid-row: 9; - grid-column: 1 / span 2; - background-color: var(--interface-color); - } - .more-info > summary{ - font-weight: normal; - border-width: 1px 0px; - border-style: solid; - } - .more-info-content{ - padding: 5px; - } - .more-info-content p{ - margin: 8px 0px; - } - .comments-area-outer{ - grid-column: 2; - grid-row: 3; - margin-top:10px; - } - .comments-disabled{ - background-color: var(--interface-color); - padding: 5px; - font-weight: bold; - } - .comments-area-inner{ - padding-top: 10px; - } - .comment{ - width:640px; - } - - .side-videos{ - grid-column: 4; - max-width: 640px; - } - #transcript-details{ - margin-bottom: 10px; - } - table#transcript-table { - border-collapse: collapse; - width: 100%; - } - table#transcript-table td, th { - border: 1px solid #dddddd; - } - div#transcript-div { - background-color: var(--interface-color); - padding: 5px; - } - .playlist{ - border-style: solid; - border-width: 2px; - border-color: lightgray; - margin-bottom: 10px; - } - .playlist-header{ - background-color: var(--interface-color); - padding: 3px; - border-bottom-style: solid; - border-bottom-width: 2px; - border-bottom-color: lightgray; - } - .playlist-header h3{ - margin: 2px; - } - .playlist-metadata{ - list-style: none; - padding: 0px; - margin: 0px; - } - .playlist-metadata li{ - display: inline; - margin: 2px; - } - .playlist-videos{ - height: 300px; - overflow-y: scroll; - display: grid; - grid-auto-rows: 90px; - grid-row-gap: 10px; - padding-top: 10px; - } - .related-videos-inner{ - padding-top: 10px; - display: grid; - grid-auto-rows: 90px; - grid-row-gap: 10px; - } - .thumbnail-box{ /* overides rule in shared.css */ - height: 90px !important; - width: 120px !important; - } - - /* Put related vids below videos when window is too small */ - /* 1100px instead of 1080 because W3C is full of idiots who include scrollbar width */ - @media (max-width:1100px){ - main{ - grid-template-columns: 1fr 640px 40px 1fr; - } - .side-videos{ - margin-top: 10px; - grid-column: 2; - grid-row: 3; - width: initial; - } - .comments-area-outer{ - grid-row: 4; - } - } - - .download-dropdown-content{ - background-color: var(--interface-color); - padding: 10px; - list-style: none; - margin: 0px; - } - li.download-format{ - margin-bottom: 7px; - } - .format-attributes{ - list-style: none; - padding: 0px; - margin: 0px; - display: flex; - flex-direction: row; - } - .format-attributes li{ - white-space: nowrap; - max-height: 1.2em; - } - .format-ext{ - width: 60px; - } - .format-video-quality{ - width: 140px; - } - .format-audio-quality{ - width: 120px; - } - .format-file-size{ - width: 80px; - } - .format-codecs{ - width: 120px; - } + <link href="/youtube.com/static/watch.css" rel="stylesheet"> {% endblock style %} {% block main %} {% if playability_error %} <div class="playability-error"> <span>{{ 'Error: ' + playability_error }} - {% if invidious_reload_button %} - <a href="{{ video_url }}&use_invidious=0"><br> -Reload without invidious (for usage of new identity button).</a> - {% endif %} + {% if invidious_reload_button %} + <a href="{{ video_url }}&use_invidious=0"><br> + Reload without invidious (for usage of new identity button).</a> + {% endif %} </span> </div> {% elif (video_sources.__len__() == 0 or live) and hls_formats.__len__() != 0 %} @@ -387,143 +26,137 @@ Reload without invidious (for usage of new identity button).</a> </ol> </div> {% else %} - <div id="video-container"> - <div id="video-container-inner"> - <video controls autofocus class="video" height="{{ video_height }}px"> - {% for video_source in video_sources %} - <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}"> - {% endfor %} + <figure class="sc-video"> + <video id="js-video-player" playsinline controls> + {% for video_source in video_sources %} + <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}"> + {% endfor %} - {% for source in subtitle_sources %} - {% if source['on'] %} - <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default> - {% else %} - <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}"> - {% endif %} - {% endfor %} + {% for source in subtitle_sources %} + {% if source['on'] %} + <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default> + {% else %} + <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}"> + {% endif %} + {% endfor %} + </video> + </figure> - </video> - </div> - </div> {% if time_start != 0 %} <script> - document.querySelector('video').currentTime = {{ time_start|tojson }}; + document.querySelector('js-video-player').currentTime = {{ time_start|tojson }}; </script> {% endif %} {% endif %} - <div class="video-info"> - <h2 class="title">{{ title }}</h2> - <ul class="labels"> - {%- if unlisted -%} - <li class="is-unlisted">Unlisted</li> - {%- endif -%} - {%- if age_restricted -%} - <li class="age-restricted">Age-restricted</li> - {%- endif -%} - {%- if limited_state -%} - <li>Limited state</li> - {%- endif -%} - {%- if live -%} - <li>Live</li> - {%- endif -%} - </ul> - <address>Uploaded by <a href="{{ uploader_channel_url }}">{{ uploader }}</a></address> - <span class="views">{{ view_count }} views</span> - - - <time datetime="$upload_date">Published on {{ time_published }}</time> - <span class="likes-dislikes">{{ like_count }} likes {{ dislike_count }} dislikes</span> - - <div class="external-player-controls"> - <input id="speed-control" type="text"> - <script> - var video = document.querySelector('video'); - var speedInput = document.querySelector('#speed-control'); - speedInput.addEventListener('keyup', (event) => { - if (event.key === 'Enter') { - var speed = parseFloat(speedInput.value); - if(!isNaN(speed)){ - video.playbackRate = speed; - } - } - }); - </script> - </div> - <input class="checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> - - <details class="download-dropdown"> - <summary class="download-dropdown-label">Download</summary> - <ul class="download-dropdown-content"> - {% for format in download_formats %} - <li class="download-format"> - <a class="download-link" href="{{ format['url'] }}"> - <ol class="format-attributes"> - <li class="format-ext">{{ format['ext'] }}</li> - <li class="format-video-quality">{{ format['video_quality'] }}</li> - <li class="format-audio-quality">{{ format['audio_quality'] }}</li> - <li class="format-file-size">{{ format['file_size'] }}</li> - <li class="format-codecs">{{ format['codecs'] }}</li> - </ol> - </a> - </li> - {% endfor %} - {% for download in other_downloads %} - <li class="download-format"> - <a href="{{ download['url'] }}"> - <ol class="format-attributes"> - <li class="format-ext">{{ download['ext'] }}</li> - <li class="format-label">{{ download['label'] }}</li> - </ol> - </a> - </li> - {% endfor %} + <div class="sc-info"> + <div class="video-info"> + <h1 class="v-title">{{ title }}</h1> + + <ul class="labels"> + {%- if unlisted -%} + <li class="is-unlisted">Unlisted</li> + {%- endif -%} + {%- if age_restricted -%} + <li class="age-restricted">Age-restricted</li> + {%- endif -%} + {%- if limited_state -%} + <li>Limited state</li> + {%- endif -%} + {%- if live -%} + <li>Live</li> + {%- endif -%} </ul> - </details> + <address class="v-uploaded">Uploaded by <a href="{{ uploader_channel_url }}">{{ uploader }}</a></address> + <span class="v-views">{{ view_count }} views</span> + <time class="v-published" datetime="$upload_date">Published on {{ time_published }}</time> + <span class="v-likes-dislikes">{{ like_count }} likes {{ dislike_count }} dislikes</span> + + <div class="external-player-controls"> + <input class="speed" id="speed-control" type="text"> + <script> + (function main() { + 'use strict'; + const video = document.getElementById('js-video-player'); + const speedInput = document.getElementById('speed-control'); + speedInput.addEventListener('keyup', (event) => { + if (event.key === 'Enter') { + let speed = parseFloat(speedInput.value); + if(!isNaN(speed)){ + video.playbackRate = speed; + } + } + }); + }()); + </script> + </div> - <span class="description">{{ common_elements.text_runs(description)|escape|urlize|timestamps|safe }}</span> - <div class="music-list"> - {% if music_list.__len__() != 0 %} - <hr> - <table> - <caption>Music</caption> - <tr> - {% for attribute in music_attributes %} - <th>{{ attribute }}</th> - {% endfor %} - </tr> - {% for track in music_list %} + <input class="v-checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> + + <details class="v-download"> + <summary class="download-dropdown-label">Download</summary> + <ul class="download-dropdown-content"> + {% for format in download_formats %} + <li class="download-format"> + <a class="download-link" href="{{ format['url'] }}" download="{{ title }}.{{ format['ext'] }}"> + {{ format['ext'] }} {{ format['video_quality'] }} {{ format['audio_quality'] }} {{ format['file_size'] }} {{ format['codecs'] }} + </a> + </li> + {% endfor %} + {% for download in other_downloads %} + <li class="download-format"> + <a href="{{ download['url'] }}" download> + {{ download['ext'] }} {{ download['label'] }} + </a> + </li> + {% endfor %} + </ul> + </details> + <span class="v-description">{{ common_elements.text_runs(description)|escape|urlize|timestamps|safe }}</span> + + <div class="v-music-list"> + {% if music_list.__len__() != 0 %} + <hr> + <table> + <caption>Music</caption> <tr> {% for attribute in music_attributes %} - <td>{{ track.get(attribute.lower(), '') }}</td> + <th>{{ attribute }}</th> {% endfor %} </tr> - {% endfor %} - </table> - {% endif %} - </div> - <details class="more-info"> - <summary>More info</summary> - <div class="more-info-content"> - <p>Tor exit node: {{ ip_address }}</p> - {% if invidious_used %} - <p>Used Invidious as fallback.</p> - {% endif %} - <p class="allowed-countries">Allowed countries: {{ allowed_countries|join(', ') }}</p> - - {% if settings.use_sponsorblock_js %} - <ul class="more-actions"> - <li><label><input type=checkbox id=skip_sponsors checked>skip sponsors</label> <span id=skip_n></span> - </ul> + {% for track in music_list %} + <tr> + {% for attribute in music_attributes %} + <td>{{ track.get(attribute.lower(), '') }}</td> + {% endfor %} + </tr> + {% endfor %} + </table> {% endif %} </div> - </details> - </div> + <details class="v-more-info"> + <summary>More info</summary> + <div class="more-info-content"> + <p>Tor exit node: {{ ip_address }}</p> + {% if invidious_used %} + <p>Used Invidious as fallback.</p> + {% endif %} + <p class="allowed-countries">Allowed countries: {{ allowed_countries|join(', ') }}</p> + {% if settings.use_sponsorblock_js %} + <ul class="more-actions"> + <li><label><input type=checkbox id=skip_sponsors checked>skip sponsors</label> <span id=skip_n></span> + </ul> + {% endif %} + </div> + </details> + </div> + + <div class="side-videos"> - <div class="side-videos"> - {% if playlist %} - <div class="playlist"> + <!-- playlist --> + {% if playlist %} + <div class="site-playlist"> <div class="playlist-header"> <a href="{{ playlist['url'] }}" title="{{ playlist['title'] }}"><h3>{{ playlist['title'] }}</h3></a> <ul class="playlist-metadata"> @@ -550,167 +183,171 @@ Reload without invidious (for usage of new identity button).</a> </nav> {% if playlist['current_index'] is not none %} <script> - // from https://stackoverflow.com/a/6969486 - function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string - } - var playability_error = {{ 'true' if playability_error else 'false' }}; - var playlist_id = {{ playlist['id']|tojson }}; - playlist_id = escapeRegExp(playlist_id); - - // read cookies on whether to autoplay thru playlist - // pain in the ass: - // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie - var cookieValue = document.cookie.replace(new RegExp( - '(?:(?:^|.*;\\s*)autoplay_' + playlist_id + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); - var autoplayEnabled = 0; - if(cookieValue.length === 0){ - autoplayEnabled = 0; - } else { - autoplayEnabled = Number(cookieValue); - } - - // check the checkbox if autoplay is on - var checkbox = document.querySelector('#autoplay-toggle'); - if(autoplayEnabled){ - checkbox.checked = true; - } - - // listen for checkbox to turn autoplay on and off - checkbox.addEventListener( 'change', function() { - if(this.checked) { - autoplayEnabled = 1; - document.cookie = 'autoplay_' + playlist_id + '=1'; - } else { - autoplayEnabled = 0; - document.cookie = 'autoplay_' + playlist_id + '=0'; - } - }); - - if(!playability_error){ - // play the video if autoplay is on - var vid = document.querySelector('video'); - if(autoplayEnabled){ - vid.play(); - } - } - - var currentIndex = {{ playlist['current_index']|tojson }}; - {% if playlist['current_index']+1 == playlist['items']|length %} - var nextVideoUrl = null; - {% else %} - var nextVideoUrl = {{ (playlist['items'][playlist['current_index']+1]['url'])|tojson }}; - {% endif %} - var nextVideoDelay = 1000; - - // scroll playlist to proper position - var pl = document.querySelector('.playlist-videos'); - // item height + gap == 100 - pl.scrollTop = 100*currentIndex; - - // go to next video when video ends - // https://stackoverflow.com/a/2880950 - if(nextVideoUrl){ - if(playability_error){ - videoEnded(); - } else { - vid.addEventListener('ended', videoEnded, false); - } - function nextVideo(){ - if(autoplayEnabled){ - window.location.href = nextVideoUrl; - } - } - function videoEnded(e) { - window.setTimeout(nextVideo, nextVideoDelay); - } - } + (function main() { + // from https://stackoverflow.com/a/6969486 + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + } + let playability_error = {{ 'true' if playability_error else 'false' }}; + let playlist_id = {{ playlist['id']|tojson }}; + playlist_id = escapeRegExp(playlist_id); + + // read cookies on whether to autoplay thru playlist + // pain in the ass: + // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie + let cookieValue = document.cookie.replace(new RegExp( + '(?:(?:^|.*;\\s*)autoplay_' + playlist_id + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); + let autoplayEnabled = 0; + if(cookieValue.length === 0){ + autoplayEnabled = 0; + } else { + autoplayEnabled = Number(cookieValue); + } + + // check the checkbox if autoplay is on + let checkbox = document.querySelector('#autoplay-toggle'); + if(autoplayEnabled){ + checkbox.checked = true; + } + + // listen for checkbox to turn autoplay on and off + checkbox.addEventListener( 'change', function() { + if(this.checked) { + autoplayEnabled = 1; + document.cookie = 'autoplay_' + playlist_id + '=1'; + } else { + autoplayEnabled = 0; + document.cookie = 'autoplay_' + playlist_id + '=0'; + } + }); + + const vid = document.getElementById('js-video-player'); + if(!playability_error){ + if(autoplayEnabled){ + vid.play(); + } + } + + let currentIndex = {{ playlist['current_index']|tojson }}; + {% if playlist['current_index']+1 == playlist['items']|length %} + let nextVideoUrl = null; + {% else %} + let nextVideoUrl = {{ (playlist['items'][playlist['current_index']+1]['url'])|tojson }}; + {% endif %} + let nextVideoDelay = 1000; + + // scroll playlist to proper position + let pl = document.querySelector('.playlist-videos'); + // item height + gap == 100 + pl.scrollTop = 100*currentIndex; + + // go to next video when video ends + // https://stackoverflow.com/a/2880950 + if(nextVideoUrl){ + if(playability_error){ + videoEnded(); + } else { + vid.addEventListener('ended', videoEnded, false); + } + function nextVideo(){ + if(autoplayEnabled){ + window.location.href = nextVideoUrl; + } + } + function videoEnded(e) { + window.setTimeout(nextVideo, nextVideoDelay); + } + } + }()); </script> {% endif %} {% if playlist['id'] is not none %} <script> - // lazy load playlist images - // copied almost verbatim from - // https://css-tricks.com/tips-for-rolling-your-own-lazy-loading/ - // IntersectionObserver isn't supported in pre-quantum - // firefox versions, but the alternative of making it - // manually is a performance drain, so oh well - var observer = new IntersectionObserver(lazyLoad, { - - // where in relation to the edge of the viewport, we are observing - rootMargin: "100px", - - // how much of the element needs to have intersected - // in order to fire our loading function - threshold: 1.0 - - }); - - function lazyLoad(elements) { - elements.forEach(item => { - if (item.intersectionRatio > 0) { - - // set the src attribute to trigger a load - item.target.src = item.target.dataset.src; - - // stop observing this element. Our work here is done! - observer.unobserve(item.target); - }; - }); - }; - - // Tell our observer to observe all img elements with a "lazy" class - var lazyImages = document.querySelectorAll('img.lazy'); - lazyImages.forEach(img => { - observer.observe(img); - }); + (function main() { + // lazy load playlist images + // copied almost verbatim from + // https://css-tricks.com/tips-for-rolling-your-own-lazy-loading/ + // IntersectionObserver isn't supported in pre-quantum + // firefox versions, but the alternative of making it + // manually is a performance drain, so oh well + let observer = new IntersectionObserver(lazyLoad, { + // where in relation to the edge of the viewport, we are observing + rootMargin: "100px", + // how much of the element needs to have intersected + // in order to fire our loading function + threshold: 1.0 + }); + + function lazyLoad(elements) { + elements.forEach(item => { + if (item.intersectionRatio > 0) { + // set the src attribute to trigger a load + item.target.src = item.target.dataset.src; + // stop observing this element. Our work here is done! + observer.unobserve(item.target); + }; + }); + }; + + // Tell our observer to observe all img elements with a "lazy" class + let lazyImages = document.querySelectorAll('img.lazy'); + lazyImages.forEach(img => { + observer.observe(img); + }); + }()); </script> {% endif %} </div> - {% endif %} + {% endif %} + <!-- /playlist --> + + {% if subtitle_sources %} + <details id="transcript-details"> + <summary>Transcript</summary> + <div id="transcript-div"> + <select id="select-tt"> + {% for source in subtitle_sources %} + <option>{{ source['label'] }}</option> + {% endfor %} + </select> + <label for="transcript-use-table">Table view</label> + <input id="transcript-use-table" type="checkbox"> + <table id="transcript-table"></table> + </div> + </details> + {% endif %} + - {% if subtitle_sources %} - <details id="transcript-details"> - <summary>Transcript</summary> - <div id="transcript-div"> - <select id="select-tt"> - {% for source in subtitle_sources %} - <option>{{ source['label'] }}</option> + {% if settings.related_videos_mode != 0 %} + <details class="related-videos-outer" {{'open' if settings.related_videos_mode == 1 else ''}}> + <summary>Related Videos</summary> + <nav class="related-videos-inner"> + {% for info in related %} + {{ common_elements.item(info, include_badges=false) }} {% endfor %} - </select> - <label for="transcript-use-table">Table view</label> - <input type="checkbox" id="transcript-use-table"> - <table id="transcript-table"></table> - </div> - </details> - {% endif %} + </nav> + </details> + {% endif %} - {% if settings.related_videos_mode != 0 %} - <details class="related-videos-outer" {{'open' if settings.related_videos_mode == 1 else ''}}> - <summary>Related Videos</summary> - <nav class="related-videos-inner"> - {% for info in related %} - {{ common_elements.item(info, include_badges=false) }} - {% endfor %} - </nav> - </details> - {% endif %} - </div> + </div> - {% if settings.comments_mode != 0 %} - {% if comments_disabled %} - <div class="comments-area-outer comments-disabled">Comments disabled</div> - {% else %} - <details class="comments-area-outer" {{'open' if settings.comments_mode == 1 else ''}}> - <summary>{{ comment_count|commatize }} comment{{'s' if comment_count != 1 else ''}}</summary> - <section class="comments-area-inner comments-area"> - {% if comments_info %} - {{ comments.video_comments(comments_info) }} - {% endif %} - </section> - </details> + <!-- comments --> + {% if settings.comments_mode != 0 %} + {% if comments_disabled %} + <div class="comments-area-outer comments-disabled">Comments disabled</div> + {% else %} + <details class="comments-area-outer" {{'open' if settings.comments_mode == 1 else ''}}> + <summary>{{ comment_count|commatize }} comment{{'s' if comment_count != 1 else ''}}</summary> + <div class="comments-area-inner comments-area"> + {% if comments_info %} + {{ comments.video_comments(comments_info) }} + {% endif %} + </div> + </details> + {% endif %} {% endif %} - {% endif %} + + </div> <script> data = {{ js_data|tojson }} </script> <script src="/youtube.com/static/js/common.js"></script> |