Merge pull request #30 from cloudrac3r/rewrite-watch-video-info

Rewrite watch-video-info
This commit is contained in:
Preston 2020-06-19 12:55:16 -04:00 committed by GitHub
commit 6a81f662bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 875 additions and 746 deletions

12
package-lock.json generated
View File

@ -10119,7 +10119,7 @@
},
"finalhandler": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
"integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
"dev": true,
"requires": {
@ -11167,7 +11167,7 @@
},
"got": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz",
"resolved": "http://registry.npmjs.org/got/-/got-9.6.0.tgz",
"integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==",
"dev": true,
"requires": {
@ -11511,7 +11511,7 @@
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
"integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
"dev": true,
"requires": {
@ -14687,7 +14687,7 @@
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
"dev": true
},
@ -17927,7 +17927,7 @@
"dependencies": {
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"dev": true,
"requires": {
@ -19052,7 +19052,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="129.158" height="129.158" viewBox="0 0 34.173 34.173"><g transform="translate(26.909 -78.793)" paint-order="fill markers stroke"><circle cx="-9.822" cy="95.88" r="16.557" fill="none" stroke="#ddd" stroke-width="1.058" stroke-linecap="round" stroke-linejoin="round"/><path style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" d="M-10.713 89.306l-.743 2.64 6.893 13.75h2.034zm-.743 2.64l-3.976 13.423.508.15 4.49-15.177zm-4.933 13.228v.53h2.813v-.53z" color="#000" font-weight="400" font-family="sans-serif" overflow="visible" fill="#ddd"/><circle cx="-10.763" cy="87.186" r="1.105" fill="#00b6f0"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="129.158" height="129.158" viewBox="0 0 34.173 34.173"><g transform="translate(26.909 -78.793)" paint-order="fill markers stroke"><circle cx="-9.822" cy="95.88" r="16.557" fill="none" stroke="#212121" stroke-width="1.058" stroke-linecap="round" stroke-linejoin="round"/><path style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" d="M-10.713 89.306l-.743 2.64 6.893 13.75h2.034zm-.743 2.64l-3.976 13.423.508.15 4.49-15.177zm-4.933 13.228v.53h2.813v-.53z" color="#000" font-weight="400" font-family="sans-serif" overflow="visible" fill="#212121"/><circle cx="-10.763" cy="87.186" r="1.105" fill="#00b6f0"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -17,7 +17,7 @@
font-weight: 500;
vertical-align: middle;
margin: 5px;
box-shadow: 0 0 2px -2px rgba(29, 39, 231, .1), 0 0 3px 0 rgba(29, 39, 231, .1), 0 0 5px 0 rgba(29, 39, 231, .1), 0 2px 2px -4px rgba(29, 39, 231, .1), 0 4px 8px 0 rgba(29, 39, 231, .1), 0 2px 15px 0 rgba(29, 39, 231, .1);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
.ripple {

View File

@ -15,7 +15,7 @@ export default Vue.extend({
props: {
label: {
type: String,
required: true
default: ""
},
textColor: {
type: String,

View File

@ -8,7 +8,9 @@
}"
@click="$emit('click')"
>
{{ label }}
<slot>
{{ label }}
</slot>
</button>
</template>

View File

@ -1,121 +0,0 @@
.ftIconButton {
display: flex;
flex-flow: row wrap;
justify-content: space-evenly;
position: relative;
}
.iconButton {
width: 1em;
height: 1em;
padding: 10px;
font-size: 20px;
border-radius: 50%;
cursor: pointer;
-moz-transition: background 0.2s ease-out;
-o-transition: background 0.2s ease-out;
transition: background 0.2s ease-out;
}
.shadow {
box-shadow: 0 1px 2px rgba(0,0,0,.5);
}
.iconButton:hover {
-moz-transition: background 0.2s ease-in;
-o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
.base {
background-color: var(--card-bg-color);
color: var(--primary-text-color);
}
.base:hover {
background-color: var(--side-nav-hover-color);
}
.base:active {
background-color: var(--side-nav-active-color);
}
.primary {
background-color: var(--primary-color);
color: var(--text-with-main-color);
}
.primary:hover {
background-color: var(--primary-color-hover);
}
.primary:active {
background-color: var(--primary-color-active);
}
.secondary {
background-color: var(--accent-color);
color: var(--text-with-accent-color);
}
.secondary:hover {
background-color: var(--accent-color-hover);
}
.secondary:active {
background-color: var(--accent-color-active);
}
.iconDropdown {
position: absolute;
text-align: center;
list-style-type: none;
z-index: 100;
margin-top: 45px;
font-size: 12px;
box-shadow: 0 1px 2px rgba(0,0,0,.5);
background-color: var(--card-bg-color);
color: var(--secondary-text-color);
}
.iconDropdown .dropdownItem {
padding: 10px;
margin: 0;
white-space: nowrap;
cursor: pointer;
-moz-transition: background 0.2s ease-out;
-o-transition: background 0.2s ease-out;
transition: background 0.2s ease-out;
}
.iconDropdown .dropdownItem:hover {
background-color: var(--side-nav-hover-color);
-moz-transition: background 0.2s ease-in;
-o-transition: background 0.2s ease-in;
transition: background 0.2s ease-in;
}
.iconDropdown .dropdownItem:active {
background-color: var(--side-nav-active-color);
-moz-transition: background 0.1s ease-in;
-o-transition: background 0.1s ease-in;
transition: background 0.1s ease-in;
}
.iconDropdown .dropdownItem a {
text-decoration: none;
color: inherit;
}
.left {
right: 50%;
}
.right {
left: 50%;
}
.top {
margin-top: 0px;
bottom: 60px;
}

View File

@ -0,0 +1,85 @@
.ftIconButton
display: flex
flex-flow: row wrap
justify-content: space-evenly
position: relative
.iconButton
width: 1em
height: 1em
padding: 10px
font-size: 20px
border-radius: 50%
cursor: pointer
transition: background 0.15s ease-out
&.shadow
box-shadow: 0 1px 2px rgba(0,0,0,.5)
&.base
background-color: var(--card-bg-color)
color: var(--primary-text-color)
&:hover
background-color: var(--side-nav-hover-color)
&:active
background-color: var(--side-nav-active-color)
&.primary
background-color: var(--primary-color)
color: var(--text-with-main-color)
&:hover
background-color: var(--primary-color-hover)
&:active
background-color: var(--primary-color-active)
&.secondary
background-color: var(--accent-color)
color: var(--text-with-accent-color)
&:hover
background-color: var(--accent-color-hover)
&:active
background-color: var(--accent-color-active)
.iconDropdown
position: absolute
text-align: center
list-style-type: none
z-index: 3
margin-top: 45px
font-size: 12px
box-shadow: 0 1px 2px rgba(0,0,0,.5)
background-color: var(--side-nav-color)
color: var(--secondary-text-color)
user-select: none
&.left
right: calc(50% - 20px)
&.right
left: calc(50% - 20px)
.list
margin: 0
padding: 0
list-style-type: none
.listItem
padding: 10px
margin: 0
white-space: nowrap
cursor: pointer
transition: background 0.2s ease-out
&:hover
background-color: var(--side-nav-hover-color)
transition: background 0.2s ease-in
&:active
background-color: var(--side-nav-active-color)
transition: background 0.1s ease-in

View File

@ -24,22 +24,23 @@
}"
>
<slot>
<div
<ul
class="list"
v-if="dropdownNames.length > 0"
>
<p
<li
v-for="(label, index) in dropdownNames"
:key="index"
class="dropdownItem
class="listItem"
@click="handleDropdownClick(index)"
>
{{ label }}
</p>
</div>
</li>
</ul>
</slot>
</div>
</div>
</template>
<script src="./ft-icon-button.js" />
<style scoped src="./ft-icon-button.css" />
<style scoped lang="sass" src="./ft-icon-button.sass" />

View File

@ -190,3 +190,58 @@
height: 35px;
overflow: hidden;
}
.videoRecommendation.list {
height: 110px;
}
.videoRecommendation.list .videoThumbnail {
width: 180px;
height: 100px;
}
.videoRecommendation.list .videoThumbnail img {
height: 100px;
}
.videoRecommendation.list .videoTitle {
font-size: 12px;
margin-left: 185px;
}
.videoRecommendation.list .channelName {
margin-left: 185px;
}
.videoRecommendation.list .viewCount {
margin-left: 5px;
}
.playlistItem .list {
height: 60px;
width: calc(100% - 30px);
}
.playlistItem .list .videoThumbnail {
width: 100px;
height: 60px;
}
.playlistItem .list .videoThumbnail img {
height: 60px;
}
.playlistItem .list .videoTitle {
font-size: 12px;
margin-left: 105px;
margin-right: 30px;
}
.playlistItem .list .channelName {
margin-left: 105px;
margin-right: 30px;
}
.playlistItem .list .viewCount {
margin-left: 5px;
}

View File

@ -0,0 +1,96 @@
import Vue from 'vue'
import FtIconButton from '../ft-icon-button/ft-icon-button.vue'
import FtButton from '../ft-button/ft-button.vue'
export default Vue.extend({
name: 'FtShareButton',
components: {
'ft-icon-button': FtIconButton,
'ft-button': FtButton
},
props: {
id: {
type: String,
required: true
}
},
computed: {
invidiousInstance: function () {
return this.$store.getters.getInvidiousInstance
},
usingElectron: function () {
return this.$store.getters.getUsingElectron
},
invidiousURL() {
return `${this.invidiousInstance}/watch?v=${this.id}`
},
invidiousEmbedURL() {
return `${this.invidiousInstance}/embed/${this.id}`
},
youtubeURL() {
return `https://www.youtube.com/watch?v=${this.id}`
},
youtubeEmbedURL() {
return `https://www.youtube-nocookie.com/embed/${this.id}`
},
},
methods: {
copy(text) {
navigator.clipboard.writeText(text)
},
open(url) {
if (this.usingElectron) {
const shell = require('electron').shell
shell.openExternal(url)
}
},
openInvidious() {
this.open(this.invidiousURL)
this.$refs.iconButton.toggleDropdown()
},
copyInvidious() {
this.copy(this.invidiousURL)
this.$refs.iconButton.toggleDropdown()
},
openYoutube() {
this.open(this.youtubeURL)
this.$refs.iconButton.toggleDropdown()
},
copyYoutube() {
this.copy(this.youtubeURL)
this.$refs.iconButton.toggleDropdown()
},
openYoutubeEmbed() {
this.open(this.youtubeEmbedURL)
this.$refs.iconButton.toggleDropdown()
},
copyYoutubeEmbed() {
this.copy(this.youtubeEmbedURL)
this.$refs.iconButton.toggleDropdown()
},
openInvidiousEmbed() {
this.open(this.invidiousEmbedURL)
this.$refs.iconButton.toggleDropdown()
},
copyInvidiousEmbed() {
this.copy(this.invidiousEmbedURL)
this.$refs.iconButton.toggleDropdown()
},
}
})

View File

@ -0,0 +1,55 @@
.shareLinks
display: grid
grid-template-rows: auto auto
grid-auto-flow: column
padding: 12px
width: max-content
.header
font-size: 18px
font-weight: bold
margin: 4px 0px 8px
color: var(--primary-text-color)
.buttons
display: flex
flex-direction: column
.action
padding: 6px
.divider
grid-row: span 3
margin: 0px 12px
width: 1px
background: var(--tertiary-text-color)
.youtubeLogo
height: 18px
width: auto
@at-root
.dark &
filter: brightness(0.868)
.light &
filter: invert(0.87)
.invidious
display: flex
justify-content: center
letter-spacing: -0.4px
.invidiousLogo
display: inline-block
width: 20px
height: 20px
background-size: cover
margin-right: 2px
@at-root
.dark &
background-image: url(~../../assets/img/invidious-logo-dark.svg)
.light &
background-image: url(~../../assets/img/invidious-logo-light.svg)

View File

@ -0,0 +1,89 @@
<template>
<ft-icon-button
ref="iconButton"
title="Share Video"
theme="secondary"
icon="share-alt"
dropdown-position-x="left"
:forceDropdown="true"
>
<div class="shareLinks">
<div class="header">
<img class="youtubeLogo" src="~../../assets/img/yt_logo_mono_dark.png" alt="YouTube" width="794" height="178">
</div>
<div class="buttons">
<ft-button
class="action"
@click="copyYoutube()"
>
<font-awesome-icon icon="copy"/>
Copy link
</ft-button>
<ft-button
class="action"
@click="openYoutube()"
>
<font-awesome-icon icon="globe"/>
Open link
</ft-button>
<ft-button
class="action"
backgroundColor="var(--accent-color-active)"
@click="copyYoutubeEmbed()"
>
<font-awesome-icon icon="copy"/>
Copy embed
</ft-button>
<ft-button
class="action"
backgroundColor="var(--accent-color-active)"
@click="openYoutubeEmbed()"
>
<font-awesome-icon icon="globe"/>
Open embed
</ft-button>
</div>
<div class="divider"></div>
<div class="header invidious"><span class="invidiousLogo"/>Invidious</div>
<div class="buttons">
<ft-button
class="action"
@click="copyInvidious()"
>
<font-awesome-icon icon="copy"/>
Copy link
</ft-button>
<ft-button
class="action"
@click="openInvidious()"
>
<font-awesome-icon icon="globe"/>
Open link
</ft-button>
<ft-button
class="action"
backgroundColor="var(--accent-color-active)"
@click="copyInvidiousEmbed()"
>
<font-awesome-icon icon="copy"/>
Copy embed
</ft-button>
<ft-button
class="action"
backgroundColor="var(--accent-color-active)"
@click="openInvidiousEmbed()"
>
<font-awesome-icon icon="globe"/>
Open embed
</ft-button>
</div>
</div>
</ft-icon-button>
</template>
<script src="./ft-share-button.js" />
<style scoped lang="sass" src="./ft-share-button.sass" />

View File

@ -1,9 +1,7 @@
.relative {
position: relative;
width: 85%;
}
.ftVideoPlayer {
width: 85%;
max-height: 50vh;
}

View File

@ -2,19 +2,30 @@
display: block;
height: calc(100vh - 60px);
width: 200px;
overflow-y: auto;
overflow-x: hidden;
position: fixed;
left: 0px;
top: 0px;
z-index: 1;
margin-top: 60px;
-webkit-box-shadow: 1px -1px 1px -1px var(--primary-shadow-color);
box-shadow: 1px -1px 1px -1px var(--primary-shadow-color);
background-color: var(--side-nav-color);
transition-property: width;
transition-duration: 150ms;
transition-timing-function: ease-in-out;
}
.inner {
height: 100%;
width: 200px;
overflow-y: auto;
overflow-x: hidden;
}
.closed .inner {
width: 80px;
}
.topNavOption {
margin-top: 10px;
}
@ -97,6 +108,10 @@
}
@media only screen and (max-width: 680px) {
.inner {
display: contents; /* sunglasses emoji */
}
hr, .mobileHidden, .refreshIcon {
display: none;
}
@ -117,7 +132,7 @@
width: 100%;
bottom: 0px;
top: auto;
overflow-y: inherit;
overflow-y: hidden;
}
.navOption, .closed .navOption {

View File

@ -4,99 +4,101 @@
class="sideNav"
:class="{closed: !isOpen}"
>
<div
class="navOption topNavOption mobileShow"
@click="navigate('subscriptions')"
>
<font-awesome-icon
icon="rss"
class="navIcon"
/>
<p class="navLabel">
Subscriptions
</p>
<font-awesome-icon
class="refreshIcon"
icon="sync"
<div class="inner">
<div
class="navOption topNavOption mobileShow"
@click="navigate('subscriptions')"
>
<font-awesome-icon
icon="rss"
class="navIcon"
/>
<p class="navLabel">
Subscriptions
</p>
<font-awesome-icon
class="refreshIcon"
icon="sync"
/>
</div>
<div
class="navOption mobileHidden"
@click="navigate('trending')"
>
<font-awesome-icon
icon="fire"
class="navIcon"
/>
<p class="navLabel">
Trending
</p>
</div>
<div
class="navOption mobileHidden"
@click="navigate('popular')"
>
<font-awesome-icon
icon="users"
class="navIcon"
/>
<p class="navLabel">
Most Popular
</p>
</div>
<div
class="navOption mobileShow"
@click="navigate('userplaylists')"
>
<font-awesome-icon
icon="bookmark"
class="navIcon"
/>
<p class="navLabel">
Playlists
</p>
</div>
<side-nav-more-options
@navigate="navigate"
/>
<div
class="navOption mobileShow"
@click="navigate('history')"
>
<font-awesome-icon
icon="history"
class="navIcon"
/>
<p class="navLabel">
History
</p>
</div>
<hr>
<div
class="navOption mobileShow"
@click="navigate('settings')"
>
<font-awesome-icon
icon="sliders-h"
class="navIcon"
/>
<p class="navLabel">
Settings
</p>
</div>
<div
class="navOption mobileHidden"
@click="navigate('about')"
>
<font-awesome-icon
icon="info-circle"
class="navIcon"
/>
<p class="navLabel">
About
</p>
</div>
<hr>
</div>
<div
class="navOption mobileHidden"
@click="navigate('trending')"
>
<font-awesome-icon
icon="fire"
class="navIcon"
/>
<p class="navLabel">
Trending
</p>
</div>
<div
class="navOption mobileHidden"
@click="navigate('popular')"
>
<font-awesome-icon
icon="users"
class="navIcon"
/>
<p class="navLabel">
Most Popular
</p>
</div>
<div
class="navOption mobileShow"
@click="navigate('userplaylists')"
>
<font-awesome-icon
icon="bookmark"
class="navIcon"
/>
<p class="navLabel">
Playlists
</p>
</div>
<side-nav-more-options
@navigate="navigate"
/>
<div
class="navOption mobileShow"
@click="navigate('history')"
>
<font-awesome-icon
icon="history"
class="navIcon"
/>
<p class="navLabel">
History
</p>
</div>
<hr>
<div
class="navOption mobileShow"
@click="navigate('settings')"
>
<font-awesome-icon
icon="sliders-h"
class="navIcon"
/>
<p class="navLabel">
Settings
</p>
</div>
<div
class="navOption mobileHidden"
@click="navigate('about')"
>
<font-awesome-icon
icon="info-circle"
class="navIcon"
/>
<p class="navLabel">
About
</p>
</div>
<hr>
</ft-flex-box>
</template>

View File

@ -1,7 +1,10 @@
.videoDescription {
overflow-y: auto;
max-height: 300px;
}
.description {
font-family: 'Roboto', sans-serif;
font-size: 17px;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
}

View File

@ -23,15 +23,10 @@ export default Vue.extend({
},
data: function () {
return {
dateString: '',
shownDescription: ''
}
},
mounted: function () {
const date = new Date(this.published)
const dateSplit = date.toDateString().split(' ')
this.dateString = `${dateSplit[0]} ${dateSplit[1]} ${dateSplit[2]}, ${dateSplit[3]}`
if (this.descriptionHtml !== '') {
this.shownDescription = this.parseDescriptionHtml(this.descriptionHtml)
} else {

View File

@ -1,6 +1,5 @@
<template>
<ft-card class="videoDescription">
<h4>Published on {{ dateString }}</h4>
<p
class="description"
v-html="shownDescription"

View File

@ -1,117 +0,0 @@
.relative {
position: relative;
}
.watchVideoInfo {
min-height: 130px;
}
.videoTitle {
font-size: 22px;
max-width: 45%;
}
.channelInformation {
position: absolute;
bottom: 10px;
width: 350px;
}
.channelThumbnail {
cursor: pointer;
border-radius: 200px 200px 200px 200px;
-webkit-border-radius: 200px 200px 200px 200px;
}
.channelName {
position: absolute;
top: 0px;
left: 55px;
font-weight: bold;
font-size: 15px;
cursor: pointer;
}
.subscribeButton {
height: 20px;
position: absolute;
top: 20px;
left: 50px;
line-height: 1px;
font-size: 0.8rem;
}
.viewCount {
position: absolute;
right: 15px;
bottom: 30px;
}
.likeBarContainer {
position: absolute;
right: 15px;
bottom: 35px;
width: 300px;
height: 5px;
}
.likeBar {
background-color: var(--accent-color);
height: 100%;
position: absolute;
top: 0px;
left: 0px;
z-index: 1;
border-radius: 200px 200px 200px 200px;
-webkit-border-radius: 200px 200px 200px 200px;
}
.dislikeBar {
background-color: #9E9E9E;
height: 100%;
width: 100%;
position: absolute;
top: 0px;
left: 0px;
border-radius: 200px 200px 200px 200px;
-webkit-border-radius: 200px 200px 200px 200px;
}
.likeCountContainer {
position: absolute;
right: 15px;
bottom: 0px;
font-size: 12px;
color: var(--tertiary-text-color);
}
.videoOptions {
position: absolute;
right: 15px;
top: 20px;
width: 175px;
}
@media only screen and (max-width: 1500px) {
.videoOptions {
width: 175px;
}
.watchVideoInfo {
min-height: 150px;
}
}
@media only screen and (max-width: 1350px) {
.theatreModeButton {
display: none;
}
.watchVideoInfo {
min-height: 130px;
}
.videoOptions {
width: 120px;
}
}

View File

@ -4,6 +4,7 @@ import FtButton from '../ft-button/ft-button.vue'
import FtListDropdown from '../ft-list-dropdown/ft-list-dropdown.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtIconButton from '../ft-icon-button/ft-icon-button.vue'
import FtShareButton from '../ft-share-button/ft-share-button.vue'
// import { shell } from 'electron'
export default Vue.extend({
@ -13,7 +14,8 @@ export default Vue.extend({
'ft-button': FtButton,
'ft-list-dropdown': FtListDropdown,
'ft-flex-box': FtFlexBox,
'ft-icon-button': FtIconButton
'ft-icon-button': FtIconButton,
'ft-share-button': FtShareButton
},
props: {
id: {
@ -69,19 +71,6 @@ export default Vue.extend({
'dash',
'legacy',
'audio'
],
shareLabel: 'SHARE VIDEO',
shareNames: [
'COPY INVIDIOUS LINK',
'OPEN INVIDIOUS LINK',
'COPY YOUTUBE LINK',
'OPEN YOUTUBE LINK'
],
shareValues: [
'copyInvidious',
'openInvidious',
'copyYoutube',
'openYoutube'
]
}
},
@ -94,18 +83,6 @@ export default Vue.extend({
return this.$store.getters.getUsingElectron
},
invidiousUrl: function () {
return `${this.invidiousInstance}/watch?v=${this.id}`
},
youtubeUrl: function () {
return `https://www.youtube.com/watch?v=${this.id}`
},
youtubeEmbedUrl: function () {
return `https://www.youtube-nocookie.com/embed/${this.id}`
},
totalLikeCount: function () {
return this.likeCount + this.dislikeCount
},
@ -120,6 +97,12 @@ export default Vue.extend({
subscribedText: function () {
return `SUBSCRIBE ${this.subscriptionCountText}`
},
dateString() {
const date = new Date(this.published)
const dateSplit = date.toDateString().split(' ')
return `${dateSplit[1]} ${dateSplit[2]} ${dateSplit[3]}`
}
},
methods: {
@ -143,40 +126,6 @@ export default Vue.extend({
this.$parent.enableAudioFormat()
break
}
},
handleShare: function (method) {
console.log('Handling share')
switch (method) {
case 'copyYoutube':
navigator.clipboard.writeText(this.youtubeUrl)
break
case 'openYoutube':
if (this.usingElectron) {
const shell = require('electron').shell
shell.openExternal(this.youtubeUrl)
}
break
case 'copyYoutubeEmbed':
navigator.clipboard.writeText(this.youtubeEmbedUrl)
break
case 'openYoutubeEmbed':
if (this.usingElectron) {
const shell = require('electron').shell
shell.openExternal(this.youtubeEmbedUrl)
}
break
case 'copyInvidious':
navigator.clipboard.writeText(this.invidiousUrl)
break
case 'openInvidious':
if (this.usingElectron) {
const shell = require('electron').shell
shell.openExternal(this.invidiousUrl)
}
break
}
}
}
})

View File

@ -0,0 +1,87 @@
.watchVideoInfo
display: grid
grid-template-columns: 2fr 1fr
padding: 16px
@media screen and (max-width: 680px)
grid-template-columns: auto
.videoTitle
font-size: 22px
margin: 0 0 24px
.channelInformation
.profileRow
display: flex
.channelThumbnail
border-radius: 50%
margin-right: 10px
cursor: pointer
width: 56px
.channelName
margin-left: 6px
cursor: pointer
position: relative
top: -2px
.subscribeButton
margin-top: 6px
margin-left: 6px
padding: 6px
font-size: 14px
.viewCount, .datePublished
color: var(--secondary-text-color)
text-align: right
font-size: 15px
@media screen and (max-width: 680px)
text-align: left
.viewCount
margin: 18px 0px 0px
.datePublished
margin: 4px 0px 0px
@media screen and (max-width: 680px)
margin-top: 16px
.likeSection
margin-top: 4px
font-size: 12px
color: var(--tertiary-text-color)
display: flex
flex-direction: column
margin-left: auto
text-align: right
max-width: 210px
@media screen and (max-width: 680px)
margin-left: 0
text-align: left
.likeBar
height: 8px
border-radius: 4px
margin-bottom: 4px
.likeCount
margin-right: 6px
.videoOptions
margin-top: 16px
display: flex
justify-content: flex-end
.option:not(:first-child)
margin-left: 4px
@media screen and (max-width: 680px)
justify-content: flex-start
@media only screen and (max-width: 1350px)
.theatreModeButton
display: none

View File

@ -1,79 +1,87 @@
<template>
<ft-card class="relative watchVideoInfo">
<p
class="videoTitle"
>
{{ title }}
</p>
<div
class="channelInformation"
>
<img
:src="channelThumbnail"
class="channelThumbnail"
@click="goToChannel"
<ft-card class="watchVideoInfo">
<div>
<p
class="videoTitle"
>
<span
class="channelName"
@click="goToChannel"
>
{{ channelName }}
</span>
<ft-button
:label="subscribedText"
class="subscribeButton"
background-color="var(--primary-color)"
@click="handleSubscription"
/>
</div>
<ft-flex-box class="videoOptions">
<ft-icon-button
title="Toggle Theatre Mode"
class="theatreModeButton"
icon="expand-alt"
theme="secondary"
@click="$emit('theatreMode')"
/>
<ft-icon-button
title="Change Video Formats"
theme="secondary"
icon="file-video"
:dropdown-names="formatTypeNames"
:dropdown-values="formatTypeValues"
@click="handleFormatChange"
/>
<ft-icon-button
title="Share Video"
theme="secondary"
icon="share-alt"
:dropdown-names="shareNames"
:dropdown-values="shareValues"
@click="handleShare"
/>
</ft-flex-box>
<p class="viewCount">
{{ parsedViewCount }}
</p>
<div class="likeBarContainer">
{{ title }}
</p>
<div
class="likeBar"
:style="{ width: likePercentageRatio + '%' }"
/>
<div class="dislikeBar" />
class="channelInformation"
>
<div
class="profileRow"
>
<div>
<img
:src="channelThumbnail"
class="channelThumbnail"
@click="goToChannel"
>
</div>
<div>
<div
class="channelName"
@click="goToChannel"
>
{{ channelName }}
</div>
<ft-button
:label="subscribedText"
class="subscribeButton"
background-color="var(--primary-color)"
@click="handleSubscription"
/>
</div>
</div>
</div>
</div>
<div>
<div class="datePublished">
Published {{ dateString }}
</div>
<div class="viewCount">
{{ parsedViewCount }}
</div>
<div class="likeBarContainer">
<div
class="likeSection"
>
<div
class="likeBar"
:style="{ background: `linear-gradient(to right, var(--accent-color) ${likePercentageRatio}%, #9E9E9E ${likePercentageRatio}%` }"
></div>
<div>
<span class="likeCount"><font-awesome-icon icon="thumbs-up"/> {{ likeCount }}</span>
<span class="dislikeCount"><font-awesome-icon icon="thumbs-down"/> {{ dislikeCount }}</span>
</div>
</div>
</div>
<div class="videoOptions">
<ft-icon-button
title="Toggle Theatre Mode"
class="theatreModeButton option"
icon="expand-alt"
theme="secondary"
@click="$emit('theatreMode')"
/>
<ft-icon-button
title="Change Video Formats"
class="option"
theme="secondary"
icon="file-video"
:dropdown-names="formatTypeNames"
:dropdown-values="formatTypeValues"
@click="handleFormatChange"
/>
<ft-share-button
class="option"
:id="id"
/>
</div>
</div>
<p class="likeCountContainer">
<font-awesome-icon
icon="thumbs-up"
/>
{{ likeCount }}
&nbsp;
<font-awesome-icon
icon="thumbs-down"
/>
{{ dislikeCount }}
</p>
</ft-card>
</template>
<script src="./watch-video-info.js" />
<style scoped src="./watch-video-info.css" />
<style scoped src="./watch-video-info.sass" lang="sass" />

View File

@ -75,32 +75,3 @@
position: relative;
bottom: 7px;
}
/deep/ .list {
height: 60px;
width: calc(100% - 30px);
}
/deep/ .list .videoThumbnail {
width: 100px;
height: 60px;
}
/deep/ .list .videoThumbnail img {
height: 60px;
}
/deep/ .list .videoTitle {
font-size: 12px;
margin-left: 105px;
margin-right: 30px;
}
/deep/ .list .channelName {
margin-left: 105px;
margin-right: 30px;
}
/deep/ .list .viewCount {
margin-left: 5px;
}

View File

@ -5,29 +5,3 @@
.videoRecommendation {
margin-bottom: -15px;
}
/deep/ .list {
height: 110px;
}
/deep/ .list .videoThumbnail {
width: 180px;
height: 100px;
}
/deep/ .list .videoThumbnail img {
height: 100px;
}
/deep/ .list .videoTitle {
font-size: 12px;
margin-left: 185px;
}
/deep/ .list .channelName {
margin-left: 185px;
}
/deep/ .list .viewCount {
margin-left: 5px;
}

View File

@ -20,7 +20,7 @@
.dark {
--primary-text-color: #EEEEEE;
--secondary-text-color: #ddd;
--tertiary-text-color: #888;
--tertiary-text-color: #999;
--primary-input-color: rgba(0, 0, 0, 0.50);
--primary-shadow-color: rgba(0, 0, 0, 0.75);
--title-color: #EEEEEE;

View File

@ -166,10 +166,15 @@
font-family: VideoJS;
font-weight: normal;
font-style: normal;
position: relative;
top: -3px;
}
.vjs-icon-cog:before {
content: "\f110";
}
.video-js .vjs-icon-cog {
font-size: 2em;
}
.vjs-icon-circle, .vjs-seek-to-live-control .vjs-icon-placeholder, .video-js .vjs-volume-level, .video-js .vjs-play-progress {
font-family: VideoJS;
@ -335,7 +340,6 @@
.video-js {
display: block;
vertical-align: top;
box-sizing: border-box;
color: #fff;
background-color: #000;
@ -774,7 +778,7 @@ body.vjs-full-window {
}
.vjs-button > .vjs-icon-placeholder:before {
font-size: 1.8em;
font-size: 2em;
line-height: 1.67;
}
@ -810,6 +814,7 @@ body.vjs-full-window {
align-items: center;
min-width: 4em;
touch-action: none;
z-index: 1;
}
.video-js .vjs-progress-control.disabled {
@ -1012,7 +1017,7 @@ body.vjs-full-window {
transition: left 0s;
}
.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-hover, .video-js .vjs-volume-panel.vjs-volume-panel-horizontal:active, .video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-slider-active {
width: 10em;
width: 8em;
transition: width 0.1s;
}
.video-js .vjs-volume-panel.vjs-volume-panel-horizontal.vjs-mute-toggle-only {
@ -1057,6 +1062,8 @@ body.vjs-full-window {
.vjs-volume-bar.vjs-slider-horizontal {
width: 5em;
height: 0.3em;
position: relative;
top: -2px;
}
.vjs-volume-bar.vjs-slider-vertical {
@ -1237,6 +1244,8 @@ body.vjs-full-window {
.video-js .vjs-play-control {
cursor: pointer;
position: relative;
top: -1px;
}
.video-js .vjs-play-control .vjs-icon-placeholder {
@ -1285,15 +1294,19 @@ video::-webkit-media-text-track-display {
.video-js .vjs-picture-in-picture-control {
cursor: pointer;
flex: none;
position: relative;
top: -1px;
}
.video-js .vjs-fullscreen-control {
cursor: pointer;
flex: none;
position: relative;
top: -1px;
}
.vjs-playback-rate > .vjs-menu-button,
.vjs-playback-rate .vjs-playback-rate-value {
position: absolute;
top: 16px;
top: 12px;
left: 0;
width: 100%;
height: 100%;
@ -1760,7 +1773,10 @@ video::-webkit-media-text-track-display {
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
width: auto
width: auto;
font-size: 14px;
position: relative;
top: -6px;
}
.video-js .vjs-time-control.vjs-time-divider {
@ -1815,8 +1831,8 @@ video::-webkit-media-text-track-display {
}
.video-js .vjs-progress-control:hover {
height: 1em;
top: -1em
height: 1.25em;
top: -0.95em
}
.video-js .vjs-control-bar {
@ -1831,6 +1847,7 @@ video::-webkit-media-text-track-display {
visibility: visible;
opacity: 1;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transform: translateY(3em);
-moz-transform: translateY(3em);
-ms-transform: translateY(3em);
@ -1896,6 +1913,12 @@ video::-webkit-media-text-track-display {
width: 5.5em;
left: 1.5em;
padding-bottom: .5em;
z-index: 1;
bottom: 1.15em;
}
.video-js .vjs-menu-button-popup.vjs-http-source-selector .vjs-menu .vjs-menu-content {
bottom: 1.5em;
}
.video-js .vjs-menu-button-popup .vjs-menu .vjs-menu-item,.video-js .vjs-menu-button-popup .vjs-menu .vjs-menu-title {
@ -1988,3 +2011,13 @@ video::-webkit-media-text-track-display {
.video-js .vjs-http-source-selector {
top: 4px;
}
.vjs-subs-caps-button.vjs-control {
position: relative;
top: -1px;
}
.vjs-volume-panel .vjs-mute-control {
position: relative;
top: -1px;
}

View File

@ -1,123 +0,0 @@
.watchVideo {
width: 65%;
float: left;
margin-top: 0px;
margin-bottom: 10px;
}
.theatreWatchVideo {
float: none;
margin: 0 auto;
width: 85%;
margin-bottom: 10px;
}
.videoPlayer {
width: calc(65% + 30px);
float: left;
margin-top: 0px;
margin-left: 10px;
margin-bottom: 10px;
}
.theatrePlayer {
width: 100%;
float: none;
margin: 0 auto;
margin-bottom: 10px;
}
.watchVideoSideBar {
width: 27%;
max-width: 425px;
float: right;
margin-bottom: 10px;
position: absolute;
}
.watchVideoPlaylist {
right: 10px;
top: 70px;
height: 500px;
}
.theatrePlaylist {
float: none;
margin: 0 auto;
width: 85%;
height: 500px;
margin-bottom: 10px;
max-width: none;
position: static;
}
.watchVideoRecommendations {
right: 10px;
}
.watchVideoRecommendationsNoCard {
top: 70px;
}
.watchVideoRecommendationsLowerCard {
top: 600px;
}
.theatreRecommendations {
float: none;
margin: 0 auto;
width: 85%;
max-width: none;
position: static;
}
@media only screen and (max-width: 1500px) {
.watchVideo {
width: 63%;
}
.videoPlayer {
width: calc(63% + 30px);
}
.theatreWatchVideo {
width: 85%;
}
.theatrePlayer {
width: calc(85% + 30px);
}
}
@media only screen and (max-width: 1350px) {
.watchVideo {
float: none;
margin: 0 auto;
width: 85%;
margin-bottom: 10px;
}
.videoPlayer {
float: none;
margin: 0 auto;
width: calc(85% + 30px);
margin-bottom: 10px;
}
.watchVideoPlaylist {
float: none;
margin: 0 auto;
margin-bottom: 10px;
width: 85%;
max-width: none;
position: static;
}
.watchVideoRecommendations {
float: none;
margin: 0 auto;
width: 85%;
max-width: none;
position: static;
}
}

View File

@ -0,0 +1,57 @@
=dual-column-template
grid-template: "video video sidebar" 0fr "info info sidebar" auto "info info sidebar" auto / 1fr 1fr 1fr
=theatre-mode-template
grid-template: "video video video" auto "info info sidebar" auto "info info sidebar" auto / 1fr 1fr 1fr
=single-column-template
grid-template: "video" auto "info" auto "sidebar" auto / auto
.videoLayout
display: grid
align-items: start
+dual-column-template
@media only screen and (max-width: 1350px)
+theatre-mode-template
@media only screen and (min-width: 901px)
&.useTheatreMode
+theatre-mode-template
@media only screen and (max-width: 900px)
+single-column-template
&.isLoading
+single-column-template
.videoArea
grid-area: video
.videoAreaMargin
margin: 0px 8px 16px
.videoPlayer
grid-column: 1
max-width: calc(80vh * 1.78)
margin: 0 auto
.watchVideo
margin: 0px 8px 16px
grid-column: 1
.infoArea
grid-area: info
.sidebarArea
grid-area: sidebar
@media only screen and (min-width: 901px)
min-width: 380px
.watchVideoPlaylist, .watchVideoSidebar, .theatrePlaylist
height: 500px
margin: 0 8px 16px
.watchVideoRecommendations, .theatreRecommendations
margin: 0 8px 16px

View File

@ -1,81 +1,95 @@
<template>
<div>
<div
class="videoLayout"
:class="{
isLoading,
useTheatreMode
}"
>
<ft-loader
v-if="isLoading"
:fullscreen="true"
/>
<ft-video-player
v-if="!isLoading && !hidePlayer"
:dash-src="dashSrc"
:source-list="activeSourceList"
:caption-list="captionSourceList"
:storyboard-src="videoStoryboardSrc"
:format="activeFormat"
:thumbnail="thumbnail"
class="videoPlayer"
:class="{ theatrePlayer: useTheatreMode }"
ref="videoPlayer"
@ended="handleVideoEnded"
@error="handleVideoError"
/>
<watch-video-info
v-if="!isLoading"
:id="videoId"
:title="videoTitle"
:channel-id="channelId"
:channel-name="channelName"
:channel-thumbnail="channelThumbnail"
:published="videoPublished"
:subscription-count-text="channelSubscriptionCountText"
:like-count="videoLikeCount"
:dislike-count="videoDislikeCount"
:view-count="videoViewCount"
@theatreMode="toggleTheatreMode"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
/>
<watch-video-description
v-if="!isLoading"
:published="videoPublished"
:description="videoDescription"
:description-html="videoDescriptionHtml"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
/>
<watch-video-comments
v-if="!isLoading && !isLive"
:id="videoId"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
/>
<watch-video-live-chat
v-if="!isLoading && isLive"
:video-id="videoId"
:channel-name="channelName"
class="watchVideoSideBar watchVideoPlaylist"
:class="{ theatrePlaylist: useTheatreMode }"
/>
<watch-video-playlist
v-if="watchingPlaylist"
v-show="!isLoading"
:playlist-id="playlistId"
:video-id="videoId"
ref="watchVideoPlaylist"
class="watchVideoSideBar watchVideoPlaylist"
:class="{ theatrePlaylist: useTheatreMode }"
/>
<watch-video-recommendations
v-if="!isLoading"
:data="recommendedVideos"
class="watchVideoSideBar watchVideoRecommendations"
:class="{
theatreRecommendations: useTheatreMode,
watchVideoRecommendationsLowerCard: watchingPlaylist || isLive,
watchVideoRecommendationsNoCard: !watchingPlaylist || !isLive
}"
/>
<div class="videoArea">
<div class="videoAreaMargin">
<ft-video-player
v-if="!isLoading && !hidePlayer"
:dash-src="dashSrc"
:source-list="activeSourceList"
:caption-list="captionSourceList"
:storyboard-src="videoStoryboardSrc"
:format="activeFormat"
:thumbnail="thumbnail"
class="videoPlayer"
:class="{ theatrePlayer: useTheatreMode }"
ref="videoPlayer"
@ended="handleVideoEnded"
@error="handleVideoError"
/>
</div>
</div>
<div class="infoArea">
<watch-video-info
v-if="!isLoading"
:id="videoId"
:title="videoTitle"
:channel-id="channelId"
:channel-name="channelName"
:channel-thumbnail="channelThumbnail"
:published="videoPublished"
:subscription-count-text="channelSubscriptionCountText"
:like-count="videoLikeCount"
:dislike-count="videoDislikeCount"
:view-count="videoViewCount"
@theatreMode="toggleTheatreMode"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
/>
<watch-video-description
v-if="!isLoading"
:published="videoPublished"
:description="videoDescription"
:description-html="videoDescriptionHtml"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
/>
<watch-video-comments
v-if="!isLoading && !isLive"
:id="videoId"
class="watchVideo"
:class="{ theatreWatchVideo: useTheatreMode }"
/>
</div>
<div class="sidebarArea">
<watch-video-live-chat
v-if="!isLoading && isLive"
:video-id="videoId"
:channel-name="channelName"
class="watchVideoSideBar watchVideoPlaylist"
:class="{ theatrePlaylist: useTheatreMode }"
/>
<watch-video-playlist
v-if="watchingPlaylist"
v-show="!isLoading"
:playlist-id="playlistId"
:video-id="videoId"
ref="watchVideoPlaylist"
class="watchVideoSideBar watchVideoPlaylist"
:class="{ theatrePlaylist: useTheatreMode }"
/>
<watch-video-recommendations
v-if="!isLoading"
:data="recommendedVideos"
class="watchVideoSideBar watchVideoRecommendations"
:class="{
theatreRecommendations: useTheatreMode,
watchVideoRecommendationsLowerCard: watchingPlaylist || isLive,
watchVideoRecommendationsNoCard: !watchingPlaylist || !isLive
}"
/>
</div>
</div>
</template>
<script src="./Watch.js" />
<style scoped src="./Watch.css" />
<style scoped src="./Watch.sass" lang="sass" />