diff options
author | Jesús <heckyel@hyperbola.info> | 2020-12-14 23:44:29 -0500 |
---|---|---|
committer | Jesús <heckyel@hyperbola.info> | 2020-12-14 23:44:29 -0500 |
commit | 7a765dc664d20f966e4e52abac1f5372045e166c (patch) | |
tree | 641f4de8cc11fd9f3234558f957b7bb6b8faf45b | |
parent | 9d0be82e7425641a58a551fefce26385dee2f9d3 (diff) | |
download | yt-local-7a765dc664d20f966e4e52abac1f5372045e166c.tar.lz yt-local-7a765dc664d20f966e4e52abac1f5372045e166c.tar.xz yt-local-7a765dc664d20f966e4e52abac1f5372045e166c.zip |
Change general design theme
30 files changed, 5443 insertions, 1521 deletions
@@ -1,20 +1,20 @@ -# youtube-local +# yt-local -![screenshot](https://user-images.githubusercontent.com/28744867/64483429-8a890780-d1b6-11e9-8423-6956ff7c588d.png) -youtube-local is a browser-based client written in Python for watching Youtube anonymously and without the lag of the slow page used by Youtube. One of the primary features is that all requests are routed through Tor, except for the video file at googlevideo.com. This is analogous to what HookTube (defunct) and Invidious do, except that you do not have to trust a third-party to respect your privacy. The assumption here is that Google won't put the effort in to incorporate the video file requests into their tracking, as it's not worth pursuing the incredibly small number of users who care about privacy (Tor video routing is also provided as an option). Tor has high latency, so this will not be as fast as regular Youtube. However, using Tor is optional; when not routing through Tor, video pages may load faster than they do with Youtube's page depending on your browser. +Fork of [youtube-local](https://github.com/user234683/youtube-local) + +yt-local is a browser-based client written in Python for watching Youtube anonymously and without the lag of the slow page used by Youtube. One of the primary features is that all requests are routed through Tor, except for the video file at googlevideo.com. This is analogous to what HookTube (defunct) and Invidious do, except that you do not have to trust a third-party to respect your privacy. The assumption here is that Google won't put the effort in to incorporate the video file requests into their tracking, as it's not worth pursuing the incredibly small number of users who care about privacy (Tor video routing is also provided as an option). Tor has high latency, so this will not be as fast as regular Youtube. However, using Tor is optional; when not routing through Tor, video pages may load faster than they do with Youtube's page depending on your browser. The Youtube API is not used, so no keys or anything are needed. It uses the same requests as the Youtube webpage. ## Screenshots -[Gray theme](https://user-images.githubusercontent.com/28744867/64483431-8e1c8e80-d1b6-11e9-999c-14d36ddd582f.png) -[Dark theme](https://user-images.githubusercontent.com/28744867/64483432-8fe65200-d1b6-11e9-90bd-32869542e32e.png) +[Light theme](https://pic.infini.fr/l7WINjzS/0Ru6MrhA.png) -[Non-Theater mode](https://user-images.githubusercontent.com/28744867/64483433-92e14280-d1b6-11e9-9b56-2ef5d64c372f.png) +[Gray theme](https://pic.infini.fr/znnQXWNc/hL78CRzo.png) -[Channel](https://user-images.githubusercontent.com/28744867/64483436-95dc3300-d1b6-11e9-8efc-b19b1f1f3bcf.png) +[Dark theme](https://pic.infini.fr/iXwFtTWv/mt2kS5bv.png) -[Downloads](https://user-images.githubusercontent.com/28744867/64483437-a2608b80-d1b6-11e9-9e5a-4114391b7304.png) +[Channel](https://pic.infini.fr/JsenWVYe/SbdIQlS6.png) ## Features * Standard pages of Youtube: search, channels, playlists @@ -86,9 +86,9 @@ To run the program on windows, open `run.bat`. On Linux/MacOS, run `python3 serv Access youtube URLs by prefixing them with `http://localhost:8080/`, For instance, `http://localhost:8080/https://www.youtube.com/watch?v=vBgulDeV2RU` -You can use an addon such as Redirector ([Firefox](https://addons.mozilla.org/en-US/firefox/addon/redirector/)|[Chrome](https://chrome.google.com/webstore/detail/redirector/ocgpenflpmgnfapjedencafcfakcekcd)) to automatically redirect Youtube URLs to youtube-local. I use the include pattern `^(https?://(?:[a-zA-Z0-9_-]*\.)?(?:youtube\.com|youtu\.be)/.*)` and the redirect pattern `http://localhost:8080/$1` (Make sure you're using regular expression mode). +You can use an addon such as Redirector ([Firefox](https://addons.mozilla.org/en-US/firefox/addon/redirector/)|[Chrome](https://chrome.google.com/webstore/detail/redirector/ocgpenflpmgnfapjedencafcfakcekcd)) to automatically redirect Youtube URLs to yt-local. I use the include pattern `^(https?://(?:[a-zA-Z0-9_-]*\.)?(?:youtube\.com|youtu\.be)/.*)` and the redirect pattern `http://localhost:8080/$1` (Make sure you're using regular expression mode). -youtube-local can be added as a search engine in firefox to make searching more convenient. See [here](https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox) for information on firefox search plugins. +yt-local can be added as a search engine in firefox to make searching more convenient. See [here](https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox) for information on firefox search plugins. ### Using Tor @@ -102,7 +102,7 @@ Pull requests and issues are welcome If you wish to route the video through Tor, set "Route Tor" to "On, including video". Because this is bandwidth-intensive, you are strongly encouraged to donate to the [consortium of Tor node operators](https://torservers.net/donate.html). For instance, donations to [NoiseTor](https://noisetor.net/) go straight towards funding nodes. Using their numbers for bandwidth costs, together with an average of 485 kbit/sec for a diverse sample of videos, and assuming n hours of video watched per day, gives $0.03n/month. A $1/month donation will be a very generous amount to not only offset losses, but help keep the network healthy. -In general, Tor video routing will be slower (for instance, moving around in the video is quite slow). I've never seen any signs that watch history in youtube-local affects on-site Youtube recommendations. It's likely that requests to googlevideo are logged for some period of time, but are not integrated into Youtube's larger advertisement/recommendation systems, since those presumably depend more heavily on in-page tracking through Javascript rather than CDN requests to googlevideo. +In general, Tor video routing will be slower (for instance, moving around in the video is quite slow). I've never seen any signs that watch history in yt-local affects on-site Youtube recommendations. It's likely that requests to googlevideo are logged for some period of time, but are not integrated into Youtube's larger advertisement/recommendation systems, since those presumably depend more heavily on in-page tracking through Javascript rather than CDN requests to googlevideo. ## License 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> |