Merge branch 'master' into ft-toast
|
@ -25,30 +25,75 @@ jobs:
|
|||
- run: npm ci
|
||||
- run: npm run lint
|
||||
- run: npm run build --if-present
|
||||
- name: Upload .deb Artifact
|
||||
- name: Build ARM64 with Node.js ${{ matrix.node-version}}
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: npm run build:arm --if-present
|
||||
- name: Upload Linux .zip x64 Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_portable_x64.zip
|
||||
path: build/freetube-vue-0.8.0.zip
|
||||
- name: Upload Linux .zip ARM Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_portable_arm64.zip
|
||||
path: build/freetube-vue-0.8.0-arm64.zip
|
||||
- name: Upload .deb x64 Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_amd64.deb
|
||||
path: build/freetube-vue_0.8.0_amd64.deb
|
||||
- name: Upload AppImage Artifact
|
||||
- name: Upload .deb ARM Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_arm64.deb
|
||||
path: build/freetube-vue_0.8.0_arm64.deb
|
||||
- name: Upload AppImage x64 Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_amd64.AppImage
|
||||
path: build/FreeTube-Vue-0.8.0.AppImage
|
||||
- name: Upload .rpm Artifact
|
||||
- name: Upload AppImage ARM Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_arm64.AppImage
|
||||
path: build/FreeTube-Vue-0.8.0-arm64.AppImage
|
||||
- name: Upload .rpm x64 Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_amd64.rpm
|
||||
path: build/freetube-vue-0.8.0.x86_64.rpm
|
||||
- name: Upload Linux .zip Artifact
|
||||
- name: Upload .rpm ARM Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_amd64.zip
|
||||
path: build/freetube-vue-0.8.0.zip
|
||||
name: freetube-vue_0.8.0_arm64.rpm
|
||||
path: build/freetube-vue-0.8.0.arm64.rpm
|
||||
- name: Upload Alpine .apk x64 Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_alpine_amd64.apk
|
||||
path: build/freetube-vue-0.8.0.apk
|
||||
- name: Upload Alpine .apk ARM Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_alpine_arm64.apk
|
||||
path: build/freetube-vue-0.8.0-arm64.apk
|
||||
- name: Upload Web Build
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
with:
|
||||
name: freetube-vue_0.8.0_static_web
|
||||
path: dist/web
|
||||
- name: Upload Windows .exe Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
|
@ -61,6 +106,12 @@ jobs:
|
|||
with:
|
||||
name: freetube-vue-0.8.0-setup-x64.exe
|
||||
path: build/FreeTube-Vue Setup 0.8.0.exe
|
||||
- name: Upload Windows Portable Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
with:
|
||||
name: freetube-vue-0.8.0-portable-x64.exe
|
||||
path: build/FreeTube-Vue 0.8.0.exe
|
||||
- name: Upload Mac .dmg Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
|
|
|
@ -7,6 +7,13 @@
|
|||
"type": "npm",
|
||||
"script": "dev",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "dev-runner",
|
||||
"problemMatcher": [],
|
||||
"label": "npm: dev-runner",
|
||||
"detail": "node _scripts/dev-runner.js"
|
||||
}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 5.2 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 526 B |
After Width: | Height: | Size: 785 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
|
@ -2,7 +2,9 @@ const os = require('os')
|
|||
const builder = require('electron-builder')
|
||||
|
||||
const Platform = builder.Platform
|
||||
const Arch = builder.Arch
|
||||
const { name, productName } = require('../package.json')
|
||||
const args = process.argv
|
||||
|
||||
let targets
|
||||
var platform = os.platform()
|
||||
|
@ -12,7 +14,13 @@ if (platform == 'darwin') {
|
|||
} else if (platform == 'win32') {
|
||||
targets = Platform.WINDOWS.createTarget()
|
||||
} else if (platform == 'linux') {
|
||||
targets = Platform.LINUX.createTarget()
|
||||
let arch = Arch.x64
|
||||
|
||||
if (args[2] === 'arm') {
|
||||
arch = Arch.arm64
|
||||
}
|
||||
|
||||
targets = Platform.LINUX.createTarget(['deb', 'zip', 'apk', 'rpm', 'AppImage'], arch)
|
||||
}
|
||||
|
||||
const config = {
|
||||
|
@ -47,7 +55,7 @@ const config = {
|
|||
linux: {
|
||||
category: 'Network',
|
||||
icon: '_icons/icon.png',
|
||||
target: ['deb', 'rpm', 'zip', 'AppImage'],
|
||||
target: ['deb', 'zip', 'apk', 'rpm', 'AppImage'],
|
||||
},
|
||||
mac: {
|
||||
category: 'public.app-category.utilities',
|
||||
|
|
|
@ -85,6 +85,13 @@ if (isDevMode) {
|
|||
ignore: ['.*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../src/renderer/assets/img'),
|
||||
to: path.join(__dirname, '../dist/images'),
|
||||
globOptions: {
|
||||
ignore: ['.*'],
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
),
|
||||
|
|
|
@ -132,6 +132,7 @@ const config = {
|
|||
'@': path.join(__dirname, '../src/'),
|
||||
src: path.join(__dirname, '../src/'),
|
||||
icons: path.join(__dirname, '../_icons/'),
|
||||
images: path.join(__dirname, '../src/renderer/assets/img/'),
|
||||
},
|
||||
extensions: ['.ts', '.js', '.vue', '.json'],
|
||||
},
|
||||
|
@ -171,6 +172,13 @@ if (isDevMode) {
|
|||
ignore: ['.*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../src/renderer/assets/img'),
|
||||
to: path.join(__dirname, '../dist/web/images'),
|
||||
globOptions: {
|
||||
ignore: ['.*'],
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
),
|
||||
|
|
|
@ -135,6 +135,7 @@ const config = {
|
|||
vue$: 'vue/dist/vue.esm.js',
|
||||
src: path.join(__dirname, '../src/'),
|
||||
icons: path.join(__dirname, '../_icons/'),
|
||||
images: path.join(__dirname, '../src/renderer/assets/img/'),
|
||||
},
|
||||
extensions: ['.js', '.vue', '.json', '.css'],
|
||||
},
|
||||
|
@ -174,6 +175,13 @@ if (isDevMode) {
|
|||
ignore: ['.*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: path.join(__dirname, '../src/renderer/assets/img'),
|
||||
to: path.join(__dirname, '../dist/web/images'),
|
||||
globOptions: {
|
||||
ignore: ['.*'],
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
),
|
||||
|
|
64
package.json
|
@ -8,14 +8,14 @@
|
|||
"url": "https://github.com/FreeTubeApp/FreeTube/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.29",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.1",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.10",
|
||||
"@silvermine/videojs-quality-selector": "^1.2.4",
|
||||
"autolinker": "^3.14.1",
|
||||
"bulma-pro": "^0.2.0",
|
||||
"dateformat": "^3.0.3",
|
||||
"electron-context-menu": "^2.0.1",
|
||||
"electron-context-menu": "^2.2.0",
|
||||
"jquery": "^3.5.1",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
|
@ -32,42 +32,42 @@
|
|||
"vue": "^2.6.11",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.3.4",
|
||||
"vuex": "^3.4.0",
|
||||
"vuex": "^3.5.1",
|
||||
"xml2json": "^0.12.0",
|
||||
"youtube-chat": "^1.1.0",
|
||||
"youtube-comments-fetch": "^1.0.1",
|
||||
"youtube-comments-task": "^1.3.15",
|
||||
"youtube-suggest": "^1.1.0",
|
||||
"yt-channel-info": "git+https://github.com/FreeTubeApp/yt-channel-info.git",
|
||||
"yt-channel-info": "^1.0.1",
|
||||
"yt-xml2vtt": "^1.1.1",
|
||||
"ytdl-core": "^3.1.1",
|
||||
"ytpl": "^0.1.21",
|
||||
"ytsr": "^0.1.15"
|
||||
"ytdl-core": "^3.2.0",
|
||||
"ytpl": "^0.2.3",
|
||||
"ytsr": "^0.1.20"
|
||||
},
|
||||
"description": "A private YouTube client",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.10.3",
|
||||
"@babel/preset-env": "^7.10.3",
|
||||
"@babel/preset-typescript": "^7.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^3.3.0",
|
||||
"@typescript-eslint/parser": "^3.3.0",
|
||||
"@babel/core": "^7.10.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.10.4",
|
||||
"@babel/preset-env": "^7.10.4",
|
||||
"@babel/preset-typescript": "^7.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "^3.7.1",
|
||||
"@typescript-eslint/parser": "^3.7.1",
|
||||
"acorn": "^7.3.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"copy-webpack-plugin": "^6.0.2",
|
||||
"css-loader": "^3.6.0",
|
||||
"copy-webpack-plugin": "^6.0.3",
|
||||
"css-loader": "^4.1.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^9.0.4",
|
||||
"electron-builder": "^22.7.0",
|
||||
"electron-builder-squirrel-windows": "^22.7.0",
|
||||
"electron": "^9.1.2",
|
||||
"electron-builder": "^22.8.0",
|
||||
"electron-builder-squirrel-windows": "^22.8.1",
|
||||
"electron-debug": "^3.1.0",
|
||||
"electron-rebuild": "^1.11.0",
|
||||
"eslint": "^7.3.0",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-import": "^2.21.2",
|
||||
"eslint-plugin-import": "^2.22.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
|
@ -76,28 +76,28 @@
|
|||
"fast-glob": "^3.2.4",
|
||||
"file-loader": "^6.0.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"jest": "^26.0.1",
|
||||
"jest": "^26.1.0",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-abi": "^2.18.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"node-loader": "^1.0.1",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.5",
|
||||
"sass": "^1.26.8",
|
||||
"sass-loader": "^8.0.2",
|
||||
"sass": "^1.26.10",
|
||||
"sass-loader": "^9.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"tree-kill": "1.2.2",
|
||||
"typescript": "^3.9.5",
|
||||
"typescript": "^3.9.7",
|
||||
"url-loader": "^4.1.0",
|
||||
"vue-devtools": "^5.1.3",
|
||||
"vue-devtools": "^5.1.4",
|
||||
"vue-eslint-parser": "^7.1.0",
|
||||
"vue-loader": "^15.9.2",
|
||||
"vue-loader": "^15.9.3",
|
||||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack": "^4.44.0",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "./dist/main.js",
|
||||
"name": "freetube-vue",
|
||||
"private": true,
|
||||
|
@ -108,7 +108,9 @@
|
|||
},
|
||||
"scripts": {
|
||||
"build": "run-s rebuild:electron pack build-release",
|
||||
"build:arm": "run-s rebuild:electron pack build-release:arm",
|
||||
"build-release": "node _scripts/build.js",
|
||||
"build-release:arm": "node _scripts/build.js arm",
|
||||
"debug": "run-s rebuild:electron debug-runner",
|
||||
"debug-runner": "node _scripts/dev-runner.js --remote-debug",
|
||||
"dev": "run-s rebuild:electron dev-runner",
|
||||
|
|
|
@ -64,6 +64,7 @@ function createWindow () {
|
|||
backgroundColor: '#fff',
|
||||
width: 960,
|
||||
height: 540,
|
||||
autoHideMenuBar: true,
|
||||
// useContentSize: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
|
|
After Width: | Height: | Size: 7.8 KiB |
|
@ -0,0 +1,11 @@
|
|||
import Vue from 'vue'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'FtAutoGrid',
|
||||
props: {
|
||||
grid: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,10 @@
|
|||
.ft-auto-grid
|
||||
&.grid
|
||||
display: grid
|
||||
grid-template-columns: repeat(auto-fill, 240px)
|
||||
justify-content: space-evenly
|
||||
grid-gap: 5px
|
||||
|
||||
&.list
|
||||
display: grid
|
||||
grid-gap: 16px
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div
|
||||
class="ft-auto-grid"
|
||||
:class="{
|
||||
grid: grid,
|
||||
list: !grid
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ft-auto-grid.js" />
|
||||
<style scoped lang="sass" src="./ft-auto-grid.sass" />
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
||||
import FtGrid from '../ft-grid/ft-grid.vue'
|
||||
import FtAutoGrid from '../ft-auto-grid/ft-auto-grid.vue'
|
||||
import FtListVideo from '../ft-list-video/ft-list-video.vue'
|
||||
import FtListChannel from '../ft-list-channel/ft-list-channel.vue'
|
||||
import FtListPlaylist from '../ft-list-playlist/ft-list-playlist.vue'
|
||||
|
@ -9,7 +9,7 @@ export default Vue.extend({
|
|||
name: 'FtElementList',
|
||||
components: {
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-grid': FtGrid,
|
||||
'ft-auto-grid': FtAutoGrid,
|
||||
'ft-list-video': FtListVideo,
|
||||
'ft-list-channel': FtListChannel,
|
||||
'ft-list-playlist': FtListPlaylist
|
||||
|
|
|
@ -1,49 +1,30 @@
|
|||
<template>
|
||||
<span>
|
||||
<ft-flex-box
|
||||
v-if="listType === 'list'"
|
||||
<ft-auto-grid
|
||||
:grid="listType !== 'list'"
|
||||
>
|
||||
<template
|
||||
v-for="(result, index) in data"
|
||||
>
|
||||
<span
|
||||
v-for="(result, index) in data"
|
||||
<ft-list-channel
|
||||
v-if="result.type === 'channel'"
|
||||
:key="index"
|
||||
class="maxWidth"
|
||||
>
|
||||
<ft-list-channel
|
||||
v-if="result.type === 'channel'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-video
|
||||
v-if="result.type === 'video' || result.type === 'shortVideo'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-playlist
|
||||
v-if="result.type === 'playlist'"
|
||||
:data="result"
|
||||
/>
|
||||
</span>
|
||||
</ft-flex-box>
|
||||
<ft-grid
|
||||
v-else
|
||||
>
|
||||
<span
|
||||
v-for="(result, index) in data"
|
||||
appearance="result"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-video
|
||||
v-if="result.type === 'video' || result.type === 'shortVideo'"
|
||||
:key="index"
|
||||
>
|
||||
<ft-list-channel
|
||||
v-if="result.type === 'channel'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-video
|
||||
v-if="result.type === 'video' || result.type === 'shortVideo'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-playlist
|
||||
v-if="result.type === 'playlist'"
|
||||
:data="result"
|
||||
/>
|
||||
</span>
|
||||
</ft-grid>
|
||||
</span>
|
||||
appearance="result"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-playlist
|
||||
v-if="result.type === 'playlist'"
|
||||
:key="index"
|
||||
appearance="result"
|
||||
:data="result"
|
||||
/>
|
||||
</template>
|
||||
</ft-auto-grid>
|
||||
</template>
|
||||
|
||||
<script src="./ft-element-list.js" />
|
||||
|
|
|
@ -19,6 +19,14 @@ export default Vue.extend({
|
|||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
padding: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
forceDropdown: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
flex-flow: row wrap
|
||||
justify-content: space-evenly
|
||||
position: relative
|
||||
user-select: none
|
||||
|
||||
.iconButton
|
||||
width: 1em
|
||||
height: 1em
|
||||
padding: 10px
|
||||
font-size: 20px
|
||||
border-radius: 50%
|
||||
cursor: pointer
|
||||
transition: background 0.15s ease-out
|
||||
|
@ -59,10 +58,10 @@
|
|||
user-select: none
|
||||
|
||||
&.left
|
||||
right: calc(50% - 20px)
|
||||
right: calc(50% - 10px)
|
||||
|
||||
&.right
|
||||
left: calc(50% - 20px)
|
||||
left: calc(50% - 10px)
|
||||
|
||||
.list
|
||||
margin: 0
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
secondary: theme === 'secondary',
|
||||
shadow: useShadow
|
||||
}"
|
||||
:style="{
|
||||
padding: padding + 'px',
|
||||
fontSize: size + 'px'
|
||||
}"
|
||||
@click="handleIconClick"
|
||||
/>
|
||||
<div
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
.channelThumbnail {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelThumbnail img {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 200px 200px 200px 200px;
|
||||
-webkit-border-radius: 200px 200px 200px 200px;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
font-weight: bold;
|
||||
color: var(--title-color);
|
||||
cursor: pointer;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.subscriberCount {
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.videoCount {
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 240px;
|
||||
height: 250px;
|
||||
padding: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grid .channelThumbnail {
|
||||
width: 100%;
|
||||
height: 140px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.grid .channelThumbnail img {
|
||||
position: relative;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.grid .channelName {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.list {
|
||||
height: 140px;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
margin-top: 15px;
|
||||
border-bottom: 1px solid var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.list .channelThumbnail {
|
||||
float: left;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
.list .channelThumbnail img {
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
left: 60px;
|
||||
}
|
||||
|
||||
.list .channelName {
|
||||
margin-left: 250px;
|
||||
width: 275px;
|
||||
}
|
||||
|
||||
.list .subscriberCount {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.list .videoCount {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.list .description {
|
||||
margin-left: 250px;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
height: 35px;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -6,6 +6,10 @@ export default Vue.extend({
|
|||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
appearance: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -32,10 +36,6 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
goToChannel: function () {
|
||||
this.$router.push({ path: `/channel/${this.id}` })
|
||||
},
|
||||
|
||||
parseLocalData: function () {
|
||||
this.thumbnail = this.data.avatar
|
||||
this.channelName = this.data.name
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
@use "../../sass-partials/_ft-list-item"
|
|
@ -1,40 +1,50 @@
|
|||
<template>
|
||||
<div
|
||||
class="ft-list-channel"
|
||||
:class="{ list: listType === 'list', grid: listType === 'grid' }"
|
||||
class="ft-list-channel ft-list-item"
|
||||
:class="{
|
||||
list: listType === 'list',
|
||||
grid: listType === 'grid',
|
||||
[appearance]: true
|
||||
}"
|
||||
>
|
||||
<div class="channelThumbnail">
|
||||
<img
|
||||
:src="thumbnail"
|
||||
@click="goToChannel(id)"
|
||||
<router-link
|
||||
:to="`/channel/${id}`"
|
||||
>
|
||||
<img
|
||||
:src="thumbnail"
|
||||
class="channelImage"
|
||||
>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="info">
|
||||
<router-link
|
||||
class="title"
|
||||
:to="`/channel/${id}`"
|
||||
>
|
||||
{{ channelName }}
|
||||
</router-link>
|
||||
<div class="infoLine">
|
||||
<span
|
||||
class="subscriberCount"
|
||||
>
|
||||
{{ subscriberCount }} subscribers
|
||||
</span>
|
||||
<span
|
||||
class="videoCount"
|
||||
>
|
||||
- {{ videoCount }} videos
|
||||
</span>
|
||||
</div>
|
||||
<p
|
||||
v-if="listType !== 'grid'"
|
||||
class="description"
|
||||
>
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="channelName"
|
||||
@click="goToChannel(id)"
|
||||
>
|
||||
{{ channelName }}
|
||||
</p>
|
||||
<span
|
||||
class="subscriberCount"
|
||||
@click="goToChannel(id)"
|
||||
>
|
||||
{{ subscriberCount }} subscribers
|
||||
</span>
|
||||
<span
|
||||
class="videoCount"
|
||||
@click="goToChannel(id)"
|
||||
>
|
||||
- {{ videoCount }} videos
|
||||
</span>
|
||||
<p
|
||||
v-if="listType !== 'grid'"
|
||||
class="description"
|
||||
>
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ft-list-channel.js" />
|
||||
<style scoped src="./ft-list-channel.css" />
|
||||
<style scoped lang="sass" src="./ft-list-channel.sass" />
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
.videoThumbnail {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoCountContainer {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
width: 120px;
|
||||
background-color: rgba(0,0,0,0.6);
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.videoCountContainer span {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 45px;
|
||||
}
|
||||
|
||||
.playlistTitle {
|
||||
font-weight: bold;
|
||||
color: var(--title-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 240px;
|
||||
height: 250px;
|
||||
padding: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grid .videoThumbnail {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.grid .videoThumbnail img {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.grid .videoCountContainer {
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.grid .playlistTitle {
|
||||
max-height: 75px;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.grid .channelName {
|
||||
width: 275px;
|
||||
}
|
||||
|
||||
.list {
|
||||
height: 140px;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
margin-top: 15px;
|
||||
border-bottom: 1px solid var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.list .videoThumbnail {
|
||||
float: left;
|
||||
width: 240px;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.list .videoThumbnail img {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.list .videoCountContainer {
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.list .playlistTitle {
|
||||
margin-left: 250px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.list .channelName {
|
||||
margin-left: 250px;
|
||||
width: 275px;
|
||||
}
|
||||
|
||||
.list .description {
|
||||
margin-left: 285px;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
height: 35px;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -6,6 +6,10 @@ export default Vue.extend({
|
|||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
appearance: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -58,14 +62,6 @@ export default Vue.extend({
|
|||
this.channelLink = this.data.author.ref
|
||||
this.playlistLink = this.data.link
|
||||
this.videoCount = parseInt(this.data.length.split(' ')[0])
|
||||
},
|
||||
|
||||
goToPlaylist: function (id) {
|
||||
this.$router.push({ path: `/playlist/${id}` })
|
||||
},
|
||||
|
||||
goToChannel: function (id) {
|
||||
console.log(id)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
@use "../../sass-partials/_ft-list-item"
|
|
@ -1,38 +1,45 @@
|
|||
<template>
|
||||
<div
|
||||
class="ft-list-video"
|
||||
class="ft-list-video ft-list-item"
|
||||
:appearance="appearance"
|
||||
:class="{ list: listType === 'list', grid: listType === 'grid' }"
|
||||
>
|
||||
<div class="videoThumbnail">
|
||||
<router-link
|
||||
class="videoThumbnail"
|
||||
:to="`/playlist/${playlistId}`"
|
||||
>
|
||||
<img
|
||||
:src="thumbnail"
|
||||
@click="goToPlaylist(playlistId)"
|
||||
class="thumbnailImage"
|
||||
>
|
||||
<div
|
||||
class="videoCountContainer"
|
||||
@click="goToPlaylist(playlistId)"
|
||||
>
|
||||
<span>
|
||||
{{ videoCount }}
|
||||
<br>
|
||||
<font-awesome-icon icon="list" />
|
||||
</span>
|
||||
<div class="background" />
|
||||
<div class="inner">
|
||||
<div>{{ videoCount }}</div>
|
||||
<div><font-awesome-icon icon="list" /></div>
|
||||
</div>
|
||||
</div>
|
||||
</router-link>
|
||||
<div class="info">
|
||||
<router-link
|
||||
class="title"
|
||||
:to="`/playlist/${playlistId}`"
|
||||
>
|
||||
{{ title }}
|
||||
</router-link>
|
||||
<div class="infoLine">
|
||||
<router-link
|
||||
class="channelName"
|
||||
:to="`/channel/${channelId}`"
|
||||
>
|
||||
{{ channelName }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="playlistTitle"
|
||||
@click="goToPlaylist(playlistId)"
|
||||
>
|
||||
{{ title }}
|
||||
</p>
|
||||
<p
|
||||
class="channelName"
|
||||
@click="goToChannel(channelId)"
|
||||
>
|
||||
{{ channelName }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ft-list-playlist.js" />
|
||||
<style scoped src="./ft-list-playlist.css" />
|
||||
<style scoped lang="sass" src="./ft-list-playlist.sass" />
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
.videoThumbnail {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoThumbnail:hover .videoWatched {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.favoritesIcon {
|
||||
position: absolute;
|
||||
font-size: 15px;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
color: #FFFFFF;
|
||||
padding: 5px;
|
||||
background-color: #000000;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.favorited {
|
||||
color: yellow;
|
||||
}
|
||||
|
||||
.videoDuration {
|
||||
position: absolute;
|
||||
font-size: 13px;
|
||||
bottom: -7px;
|
||||
right: 0px;
|
||||
color: #FFFFFF;
|
||||
padding: 2px;
|
||||
background-color: #000000;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoWatched {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
color: #FFFFFF;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.watchedProgressBar {
|
||||
background-color: var(--red-500);
|
||||
opacity: 0.8;
|
||||
height: 3px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.videoTitle {
|
||||
color: var(--title-color);
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.channelName {
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.viewCount {
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.uploadedTime {
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.liveText {
|
||||
color: var(--red-500);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/deep/ .iconButton {
|
||||
font-size: 15px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/deep/ .iconDropdown {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 240px;
|
||||
height: 250px;
|
||||
padding: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.grid .videoThumbnail {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.grid .videoThumbnail img {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.grid .videoWatched {
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
.grid .optionsButton {
|
||||
margin-top: 10px;
|
||||
margin-left: 210px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.grid .videoTitle {
|
||||
max-height: 55px;
|
||||
width: 210px;
|
||||
overflow-y: hidden;
|
||||
margin-bottom: -15px;
|
||||
}
|
||||
|
||||
.grid .channelName {
|
||||
width: 220px;
|
||||
height: 17px;
|
||||
margin-bottom: 10px;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.grid .liveText {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.list {
|
||||
height: 140px;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.list .videoThumbnail {
|
||||
float: left;
|
||||
width: 240px;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.list .videoThumbnail img {
|
||||
width: 100%;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.list .videoWatched {
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.list .optionsButton {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.list .videoTitle {
|
||||
margin-left: 250px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
.list .channelName {
|
||||
margin-left: 250px;
|
||||
max-width: 275px;
|
||||
}
|
||||
|
||||
.list .viewCount {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.list .liveText {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.list .description {
|
||||
margin-left: 250px;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
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;
|
||||
}
|
|
@ -18,6 +18,10 @@ export default Vue.extend({
|
|||
forceListType: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
appearance: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -121,31 +125,6 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
play: function () {
|
||||
const playlistInfo = {
|
||||
playlistId: this.playlistId
|
||||
}
|
||||
console.log('playlist info')
|
||||
console.log(playlistInfo)
|
||||
|
||||
if (this.playlistId !== null) {
|
||||
console.log('Sending playlist info')
|
||||
this.$router.push(
|
||||
{
|
||||
path: `/watch/${this.id}`,
|
||||
query: playlistInfo
|
||||
}
|
||||
)
|
||||
} else {
|
||||
console.log('no playlist found')
|
||||
this.$router.push({ path: `/watch/${this.id}` })
|
||||
}
|
||||
},
|
||||
|
||||
goToChannel: function () {
|
||||
this.$router.push({ path: `/channel/${this.channelId}` })
|
||||
},
|
||||
|
||||
toggleSave: function () {
|
||||
console.log('TODO: ft-list-video method toggleSave')
|
||||
},
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
@use "../../sass-partials/_ft-list-item"
|
|
@ -1,27 +1,39 @@
|
|||
<template>
|
||||
<div
|
||||
class="ft-list-video"
|
||||
class="ft-list-video ft-list-item"
|
||||
:class="{
|
||||
list: (listType === 'list' || forceListType === 'list') && forceListType !== 'grid',
|
||||
grid: (listType === 'grid' || forceListType === 'list') && forceListType !== 'list'
|
||||
grid: (listType === 'grid' || forceListType === 'list') && forceListType !== 'list',
|
||||
[appearance]: true
|
||||
}"
|
||||
>
|
||||
<div class="videoThumbnail">
|
||||
<img
|
||||
:src="thumbnail"
|
||||
@click="play(id)"
|
||||
<div
|
||||
class="videoThumbnail"
|
||||
>
|
||||
<router-link
|
||||
class="thumbnailLink"
|
||||
:to="{
|
||||
path: `/watch/${id}`,
|
||||
query: playlistId ? {playlistId} : {}
|
||||
}"
|
||||
>
|
||||
<p
|
||||
v-if="!isLive"
|
||||
<img
|
||||
:src="thumbnail"
|
||||
class="thumbnailImage"
|
||||
>
|
||||
</router-link>
|
||||
<div
|
||||
class="videoDuration"
|
||||
@click="play(id)"
|
||||
:class="{ live: isLive }"
|
||||
>
|
||||
{{ duration }}
|
||||
</p>
|
||||
<font-awesome-icon
|
||||
{{ isLive ? "Live" : duration }}
|
||||
</div>
|
||||
<ft-icon-button
|
||||
v-if="!isLive"
|
||||
icon="star"
|
||||
class="favoritesIcon"
|
||||
:padding="appearance === `watchPlaylistItem` ? 5 : 6"
|
||||
:size="appearance === `watchPlaylistItem` ? 14 : 18"
|
||||
:class="{ favorited: isFavorited }"
|
||||
@click="toggleSave(id)"
|
||||
/>
|
||||
|
@ -29,7 +41,7 @@
|
|||
v-if="watched"
|
||||
class="videoWatched"
|
||||
>
|
||||
WATCHED
|
||||
Watched
|
||||
</div>
|
||||
<div
|
||||
v-if="watched"
|
||||
|
@ -37,65 +49,56 @@
|
|||
:style="{width: progressPercentage + '%'}"
|
||||
/>
|
||||
</div>
|
||||
<ft-icon-button
|
||||
class="optionsButton"
|
||||
title="More Options"
|
||||
theme="base"
|
||||
:use-shadow="false"
|
||||
dropdown-position-x="left"
|
||||
:dropdown-names="optionsNames"
|
||||
:dropdown-values="optionsValues"
|
||||
@click="handleOptionsClick"
|
||||
/>
|
||||
<p
|
||||
class="videoTitle"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ title }}
|
||||
</p>
|
||||
<p
|
||||
class="channelName"
|
||||
@click="goToChannel"
|
||||
>
|
||||
{{ channelName }}
|
||||
</p>
|
||||
<span
|
||||
v-if="!isLive && !hideViews"
|
||||
class="viewCount"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ viewCount }} views
|
||||
</span>
|
||||
<span
|
||||
v-if="uploadedTime !== '' && !isLive"
|
||||
class="uploadedTime"
|
||||
@click="play(id)"
|
||||
>
|
||||
- {{ uploadedTime }}
|
||||
</span>
|
||||
<span
|
||||
v-if="isLive"
|
||||
class="viewCount"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ viewCount }} watching
|
||||
</span>
|
||||
<p
|
||||
v-if="listType !== 'grid'"
|
||||
class="description"
|
||||
@click="play(id)"
|
||||
>
|
||||
{{ description }}
|
||||
</p>
|
||||
<span
|
||||
v-if="isLive"
|
||||
class="liveText"
|
||||
@click="play(id)"
|
||||
>
|
||||
LIVE NOW
|
||||
</span>
|
||||
<div class="info">
|
||||
<ft-icon-button
|
||||
class="optionsButton"
|
||||
title="More Options"
|
||||
theme="base"
|
||||
:size="16"
|
||||
:use-shadow="false"
|
||||
dropdown-position-x="left"
|
||||
:dropdown-names="optionsNames"
|
||||
:dropdown-values="optionsValues"
|
||||
@click="handleOptionsClick"
|
||||
/>
|
||||
<router-link
|
||||
class="title"
|
||||
:to="{
|
||||
path: `/watch/${id}`,
|
||||
query: playlistId ? {playlistId} : {}
|
||||
}"
|
||||
>
|
||||
{{ title }}
|
||||
</router-link>
|
||||
<div class="infoLine">
|
||||
<router-link
|
||||
class="channelName"
|
||||
:to="`/channel/${channelId}`"
|
||||
>
|
||||
<span>{{ channelName }}</span>
|
||||
</router-link>
|
||||
<span
|
||||
v-if="!isLive && !hideViews"
|
||||
class="viewCount"
|
||||
>• {{ viewCount }} views</span>
|
||||
<span
|
||||
v-if="uploadedTime !== '' && !isLive"
|
||||
class="uploadedTime"
|
||||
>• {{ uploadedTime }}</span>
|
||||
<span
|
||||
v-if="isLive"
|
||||
class="viewCount"
|
||||
>• {{ viewCount }} watching</span>
|
||||
</div>
|
||||
<p
|
||||
v-if="listType !== 'grid' && appearance === 'result'"
|
||||
class="description"
|
||||
>
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./ft-list-video.js" />
|
||||
<style scoped src="./ft-list-video.css" />
|
||||
<style scoped src="./ft-list-video.sass" lang="sass" />
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.maxWidth {
|
||||
width: 100%;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import Vue from 'vue'
|
||||
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
|
||||
import FtGrid from '../ft-grid/ft-grid.vue'
|
||||
import FtListVideo from '../ft-list-video/ft-list-video.vue'
|
||||
import FtListChannel from '../ft-list-channel/ft-list-channel.vue'
|
||||
import FtListPlaylist from '../ft-list-playlist/ft-list-playlist.vue'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'WatchPlaylist',
|
||||
components: {
|
||||
'ft-flex-box': FtFlexBox,
|
||||
'ft-grid': FtGrid,
|
||||
'ft-list-video': FtListVideo,
|
||||
'ft-list-channel': FtListChannel,
|
||||
'ft-list-playlist': FtListPlaylist
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
test: 'hello'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
listType: function () {
|
||||
return this.$store.getters.getListType
|
||||
}
|
||||
}
|
||||
})
|
|
@ -1,50 +0,0 @@
|
|||
<template>
|
||||
<span>
|
||||
<ft-flex-box
|
||||
v-if="listType === 'list'"
|
||||
>
|
||||
<span
|
||||
v-for="(result, index) in data"
|
||||
:key="index"
|
||||
class="maxWidth"
|
||||
>
|
||||
<ft-list-channel
|
||||
v-if="result.type === 'channel'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-video
|
||||
v-if="result.type === 'video' || result.type === 'shortVideo'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-playlist
|
||||
v-if="result.type === 'playlist'"
|
||||
:data="result"
|
||||
/>
|
||||
</span>
|
||||
</ft-flex-box>
|
||||
<ft-grid
|
||||
v-else
|
||||
>
|
||||
<span
|
||||
v-for="(result, index) in data"
|
||||
:key="index"
|
||||
>
|
||||
<ft-list-channel
|
||||
v-if="result.type === 'channel'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-video
|
||||
v-if="result.type === 'video' || result.type === 'shortVideo'"
|
||||
:data="result"
|
||||
/>
|
||||
<ft-list-playlist
|
||||
v-if="result.type === 'playlist'"
|
||||
:data="result"
|
||||
/>
|
||||
</span>
|
||||
</ft-grid>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./watch-playlist.js" />
|
||||
<style scoped src="./watch-playlist.css" />
|
|
@ -37,36 +37,34 @@
|
|||
.playlistItems {
|
||||
overflow-y: auto;
|
||||
height: 395px;
|
||||
margin-top: -15px;
|
||||
margin-left: -16px;
|
||||
margin-right: -16px;
|
||||
}
|
||||
|
||||
.playlistItem {
|
||||
height: 75px;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 30px 1fr;
|
||||
align-items: center;
|
||||
transition: background 0.2s ease-out;
|
||||
}
|
||||
|
||||
.playlistItem:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.playlistItem:hover {
|
||||
background-color: var(--side-nav-hover-color);
|
||||
transition: background 0.2s ease-in;
|
||||
}
|
||||
|
||||
.videoIndexContainer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.videoIndex {
|
||||
float: left;
|
||||
position: relative;
|
||||
top: 15px;
|
||||
left: 10px;
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.videoIndexIcon {
|
||||
float: left;
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
top: 32px;
|
||||
left: 10px;
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
@click="playNextVideo"
|
||||
/>
|
||||
</p>
|
||||
<ft-flex-box
|
||||
<div
|
||||
v-if="!isLoading"
|
||||
class="playlistItems"
|
||||
>
|
||||
|
@ -56,25 +56,27 @@
|
|||
:key="index"
|
||||
class="playlistItem"
|
||||
>
|
||||
<font-awesome-icon
|
||||
v-if="item.videoId === videoId"
|
||||
class="videoIndexIcon"
|
||||
icon="play"
|
||||
/>
|
||||
<p
|
||||
v-else
|
||||
class="videoIndex"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</p>
|
||||
<div class="videoIndexContainer">
|
||||
<font-awesome-icon
|
||||
v-if="item.videoId === videoId"
|
||||
class="videoIndexIcon"
|
||||
icon="play"
|
||||
/>
|
||||
<p
|
||||
v-else
|
||||
class="videoIndex"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</p>
|
||||
</div>
|
||||
<ft-list-video
|
||||
:data="item"
|
||||
:playlist-id="playlistId"
|
||||
appearance="watchPlaylistItem"
|
||||
force-list-type="list"
|
||||
class="videoInfo"
|
||||
/>
|
||||
</div>
|
||||
</ft-flex-box>
|
||||
</div>
|
||||
</div>
|
||||
</ft-card>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.videoRecommendation {
|
||||
margin-bottom: -15px;
|
||||
.watchVideoRecommendations {
|
||||
display: grid;
|
||||
grid-gap: 8px;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<ft-card class="relative">
|
||||
<ft-card class="relative watchVideoRecommendations">
|
||||
<h3>
|
||||
Up Next
|
||||
</h3>
|
||||
|
@ -7,8 +7,8 @@
|
|||
v-for="(video, index) in data"
|
||||
:key="index"
|
||||
:data="video"
|
||||
appearance="recommendation"
|
||||
force-list-type="list"
|
||||
class="videoRecommendation"
|
||||
/>
|
||||
</ft-card>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
$thumbnail-overlay-opacity: 0.85
|
||||
|
||||
@mixin is-result
|
||||
@at-root
|
||||
.result#{&}
|
||||
@content
|
||||
|
||||
@mixin is-watch-playlist-item
|
||||
@at-root
|
||||
.watchPlaylistItem#{&}
|
||||
@content
|
||||
|
||||
@mixin is-recommendation
|
||||
@at-root
|
||||
.recommendation#{&}
|
||||
@content
|
||||
|
||||
@mixin is-sidebar-item
|
||||
@at-root
|
||||
.watchPlaylistItem#{&}, .recommendation#{&}
|
||||
@content
|
||||
|
||||
.ft-list-item
|
||||
.videoThumbnail
|
||||
display: flex
|
||||
position: relative
|
||||
|
||||
.thumbnailLink
|
||||
display: flex
|
||||
|
||||
.thumbnailImage
|
||||
height: 130px
|
||||
|
||||
@include is-sidebar-item
|
||||
height: 75px
|
||||
|
||||
@include is-recommendation
|
||||
width: 163px
|
||||
height: auto
|
||||
|
||||
.videoDuration
|
||||
position: absolute
|
||||
bottom: 4px
|
||||
right: 4px
|
||||
padding: 3px 4px
|
||||
line-height: 1.2
|
||||
font-size: 15px
|
||||
border-radius: 5px
|
||||
margin: 0
|
||||
opacity: $thumbnail-overlay-opacity
|
||||
color: var(--primary-text-color)
|
||||
background-color: var(--card-bg-color)
|
||||
pointer-events: none
|
||||
|
||||
&.live
|
||||
background-color: #f22
|
||||
color: #fff
|
||||
|
||||
@include is-watch-playlist-item
|
||||
font-size: 12px
|
||||
|
||||
.favoritesIcon
|
||||
position: absolute
|
||||
top: 3px
|
||||
right: 3px
|
||||
font-size: 17px
|
||||
opacity: $thumbnail-overlay-opacity
|
||||
|
||||
.videoCountContainer
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
bottom: 0
|
||||
width: 60px
|
||||
font-size: 20px
|
||||
|
||||
.background, .inner
|
||||
position: absolute
|
||||
top: 0
|
||||
bottom: 0
|
||||
left: 0
|
||||
right: 0
|
||||
|
||||
.background
|
||||
background-color: var(--bg-color)
|
||||
opacity: 0.9
|
||||
|
||||
.inner
|
||||
display: flex
|
||||
flex-direction: column
|
||||
justify-content: center
|
||||
align-items: center
|
||||
color: var(--primary-text-color)
|
||||
|
||||
.channelThumbnail
|
||||
display: flex
|
||||
justify-content: center
|
||||
|
||||
.channelImage
|
||||
height: 130px
|
||||
border-radius: 50%
|
||||
|
||||
.info
|
||||
flex: 1
|
||||
position: relative
|
||||
|
||||
.optionsButton
|
||||
float: right // ohhhh man, float was finally the right choice for something
|
||||
|
||||
.title
|
||||
font-size: 20px
|
||||
color: var(--primary-text-color)
|
||||
text-decoration: none
|
||||
|
||||
@include is-sidebar-item
|
||||
font-size: 15px
|
||||
|
||||
.infoLine
|
||||
margin-top: 5px
|
||||
font-size: 14px
|
||||
|
||||
@include is-sidebar-item
|
||||
font-size: 12px
|
||||
|
||||
&, .channelName
|
||||
color: var(--secondary-text-color)
|
||||
|
||||
.description
|
||||
font-size: 14px
|
||||
color: var(--secondary-text-color)
|
||||
|
||||
&.list
|
||||
display: flex
|
||||
align-items: flex-start
|
||||
|
||||
.videoThumbnail, .channelThumbnail
|
||||
margin-right: 20px
|
||||
|
||||
.channelThumbnail
|
||||
width: 231px
|
||||
|
||||
@include is-sidebar-item
|
||||
.videoThumbnail
|
||||
margin-right: 10px
|
||||
|
||||
&.grid
|
||||
display: flex
|
||||
flex-direction: column
|
||||
min-height: 230px
|
||||
padding-bottom: 20px
|
||||
|
||||
.videoThumbnail, .channelThumbnail
|
||||
margin-bottom: 12px
|
||||
|
||||
.thumbnailImage
|
||||
width: 100%
|
||||
|
||||
.title
|
||||
font-size: 18px
|
||||
|
||||
.infoLine
|
||||
margin-top: 8px
|
||||
font-size: 13px
|
||||
|
||||
.videoWatched, .live
|
||||
text-transform: uppercase
|
|
@ -10,6 +10,8 @@
|
|||
--link-visited-color: var(--accent-color-visited);
|
||||
--card-bg-color: #FFFFFF;
|
||||
--secondary-card-bg-color: #eeeeee;
|
||||
--scrollbar-color: #CCCCCC;
|
||||
--scrollbar-color-hover: #BDBDBD;
|
||||
--side-nav-color: #FFFFFF;
|
||||
--side-nav-hover-color: #e0e0e0;
|
||||
--side-nav-active-color: #757575;
|
||||
|
@ -32,6 +34,8 @@
|
|||
--link-visited-color: var(--accent-color-visited);
|
||||
--card-bg-color: #303030;
|
||||
--secondary-card-bg-color: rgba(0, 0, 0, 0.75);
|
||||
--scrollbar-color: #414141;
|
||||
--scrollbar-color-hover: #757575;
|
||||
--side-nav-color: #262626;
|
||||
--side-nav-hover-color: #212121;
|
||||
--side-nav-active-color: #303030;
|
||||
|
@ -53,12 +57,14 @@
|
|||
--link-visited-color: var(--accent-color-visited);
|
||||
--card-bg-color: #000000;
|
||||
--secondary-card-bg-color: rgba(0, 0, 0, 0.75);
|
||||
--scrollbar-color: #515151;
|
||||
--scrollbar-color-hover: #424242;
|
||||
--side-nav-color: #000000;
|
||||
--side-nav-hover-color: #212121;
|
||||
--side-nav-active-color: #303030;
|
||||
--search-bar-color: #262626;
|
||||
--instance-menu-color: var(--search-bar-color);
|
||||
--logo-icon: url("~../../_icons/iconColorSmall.png");
|
||||
--logo-icon: url("~../../_icons/iconColorSmall.png");
|
||||
--logo-text: url("~../../_icons/textColorSmall.png");
|
||||
}
|
||||
|
||||
|
@ -442,9 +448,24 @@ body {
|
|||
color: var(--primary-text-color);
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
a:link {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--link-visited-color);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-color);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scrollbar-color-hover);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,16 @@
|
|||
max-height: 300px;
|
||||
}
|
||||
|
||||
.defaultChannelBanner {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 200px;
|
||||
background-color: black;
|
||||
background-image: url("~images/defaultBanner.png");
|
||||
}
|
||||
|
||||
.channelInfoContainer {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
|
@ -116,6 +126,10 @@
|
|||
margin-top: 200px;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: var(--tertiary-text-color);
|
||||
}
|
||||
|
||||
.getNextPage {
|
||||
background-color: var(--search-bar-color);
|
||||
width: 100%;
|
||||
|
@ -123,4 +137,5 @@
|
|||
line-height: 45px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
|
|
@ -88,6 +88,28 @@ export default Vue.extend({
|
|||
|
||||
formattedSubCount: function () {
|
||||
return this.subCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
},
|
||||
|
||||
showFetchMoreButton: function () {
|
||||
switch (this.currentTab) {
|
||||
case 'videos':
|
||||
if (this.videoContinuationString !== '' && this.videoContinuationString !== null) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
case 'playlists':
|
||||
if (this.playlistContinuationString !== '' && this.playlistContinuationString !== null) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
case 'search':
|
||||
if (this.searchContinuationString !== '' && this.searchContinuationString !== null) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -132,13 +154,11 @@ export default Vue.extend({
|
|||
} else {
|
||||
switch (this.backendPreference) {
|
||||
case 'local':
|
||||
this.apiUsed = 'local'
|
||||
this.getChannelInfoLocal()
|
||||
this.getChannelVideosLocal()
|
||||
this.getPlaylistsLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
this.apiUsed = 'invidious'
|
||||
this.getChannelInfoInvidious()
|
||||
this.getPlaylistsInvidious()
|
||||
break
|
||||
|
@ -147,17 +167,37 @@ export default Vue.extend({
|
|||
},
|
||||
methods: {
|
||||
getChannelInfoLocal: function () {
|
||||
this.apiUsed = 'local'
|
||||
ytch.getChannelInfo(this.id).then((response) => {
|
||||
this.id = response.authorId
|
||||
this.channelName = response.author
|
||||
this.subCount = response.subscriberCount
|
||||
this.thumbnailUrl = response.authorThumbnails[2].url
|
||||
this.bannerUrl = `https://${response.authorBanners[response.authorBanners.length - 1].url}`
|
||||
this.channelDescription = response.description
|
||||
this.relatedChannels = response.relatedChannels
|
||||
|
||||
if (response.authorBanners !== null) {
|
||||
const bannerUrl = response.authorBanners[response.authorBanners.length - 1].url
|
||||
|
||||
if (!bannerUrl.includes('https')) {
|
||||
this.bannerUrl = `https://${bannerUrl}`
|
||||
} else {
|
||||
this.bannerUrl = bannerUrl
|
||||
}
|
||||
} else {
|
||||
this.bannerUrl = null
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'local' && this.backendFallback) {
|
||||
console.log('Falling back to Invidious API')
|
||||
this.getChannelInfoInvidious()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -167,20 +207,30 @@ export default Vue.extend({
|
|||
this.latestVideos = response.items
|
||||
this.videoContinuationString = response.continuation
|
||||
this.isElementListLoading = false
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'local' && this.backendFallback) {
|
||||
console.log('Falling back to Invidious API')
|
||||
this.getChannelInfoInvidious()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
channelLocalNextPage: function () {
|
||||
console.log(this.videoContinuationString)
|
||||
ytch.getChannelVideosMore(this.id, this.videoContinuationString).then((response) => {
|
||||
ytch.getChannelVideosMore(this.videoContinuationString).then((response) => {
|
||||
this.latestVideos = this.latestVideos.concat(response.items)
|
||||
this.videoContinuationString = response.continuation
|
||||
console.log(this.videoContinuationString)
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
|
||||
getChannelInfoInvidious: function () {
|
||||
this.isLoading = true
|
||||
this.apiUsed = 'invidious'
|
||||
|
||||
this.$store.dispatch('invidiousGetChannelInfo', this.id).then((response) => {
|
||||
console.log(response)
|
||||
|
@ -188,10 +238,14 @@ export default Vue.extend({
|
|||
this.id = response.authorId
|
||||
this.subCount = response.subCount
|
||||
this.thumbnailUrl = response.authorThumbnails[3].url
|
||||
this.bannerUrl = response.authorBanners[0].url
|
||||
this.channelDescription = response.description
|
||||
this.relatedChannels = response.relatedChannels
|
||||
this.latestVideos = response.latestVideos
|
||||
|
||||
if (typeof (response.authorBanners) !== 'undefined') {
|
||||
this.bannerUrl = response.authorBanners[0].url
|
||||
}
|
||||
|
||||
this.isLoading = false
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
|
@ -222,14 +276,25 @@ export default Vue.extend({
|
|||
this.latestPlaylists = response.items
|
||||
this.playlistContinuationString = response.continuation
|
||||
this.isElementListLoading = false
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'local' && this.backendFallback) {
|
||||
console.log('Falling back to Invidious API')
|
||||
this.getPlaylistsInvidious()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getPlaylistsLocalMore: function () {
|
||||
ytch.getChannelPlaylistsMore(this.id, this.playlistContinuationString).then((response) => {
|
||||
ytch.getChannelPlaylistsMore(this.playlistContinuationString).then((response) => {
|
||||
console.log(response)
|
||||
this.latestPlaylists = this.latestPlaylists.concat(response.items)
|
||||
this.playlistContinuationString = response.continuation
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -252,8 +317,15 @@ export default Vue.extend({
|
|||
this.playlistContinuationString = response.continuation
|
||||
this.latestPlaylists = this.latestPlaylists.concat(response.playlists)
|
||||
this.isElementListLoading = false
|
||||
}).catch((error) => {
|
||||
console.log(error)
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'invidious' && this.backendFallback) {
|
||||
console.log('Falling back to Local API')
|
||||
this.getPlaylistsLocal()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -324,13 +396,24 @@ export default Vue.extend({
|
|||
this.searchResults = response.items
|
||||
this.isElementListLoading = false
|
||||
this.searchContinuationString = response.continuation
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'local' && this.backendFallback) {
|
||||
console.log('Falling back to Invidious API')
|
||||
this.searchChannelInvidious()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ytch.searchChannelMore(this.id, this.searchContinuationString).then((response) => {
|
||||
ytch.searchChannelMore(this.searchContinuationString).then((response) => {
|
||||
console.log(response)
|
||||
this.searchResults = this.searchResults.concat(response.items)
|
||||
this.isElementListLoading = false
|
||||
this.searchContinuationString = response.continuation
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@ -349,6 +432,15 @@ export default Vue.extend({
|
|||
this.searchResults = this.searchResults.concat(response)
|
||||
this.isElementListLoading = false
|
||||
this.searchPage++
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
if (this.backendPreference === 'invidious' && this.backendFallback) {
|
||||
console.log('Falling back to Local API')
|
||||
this.searchChannelLocal()
|
||||
} else {
|
||||
this.isLoading = false
|
||||
// TODO: Show toast with error message
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,14 @@
|
|||
class="card"
|
||||
>
|
||||
<img
|
||||
v-if="bannerUrl !== null"
|
||||
class="channelBanner"
|
||||
:src="bannerUrl"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
class="defaultChannelBanner"
|
||||
>
|
||||
<div class="channelInfoContainer">
|
||||
<div class="channelInfo">
|
||||
<img
|
||||
|
@ -102,8 +107,14 @@
|
|||
v-html="channelDescription"
|
||||
/>
|
||||
<br>
|
||||
<h2>Featured Channels</h2>
|
||||
<ft-flex-box>
|
||||
<h2
|
||||
v-if="relatedChannels.length > 0"
|
||||
>
|
||||
Featured Channels
|
||||
</h2>
|
||||
<ft-flex-box
|
||||
v-if="relatedChannels.length > 0"
|
||||
>
|
||||
<ft-channel-bubble
|
||||
v-for="(channel, index) in relatedChannels"
|
||||
:key="index"
|
||||
|
@ -124,15 +135,37 @@
|
|||
v-show="currentTab === 'videos'"
|
||||
:data="latestVideos"
|
||||
/>
|
||||
<ft-flex-box
|
||||
v-if="currentTab === 'videos' && latestVideos.length === 0"
|
||||
>
|
||||
<p class="message">
|
||||
This channel does not currently have any videos
|
||||
</p>
|
||||
</ft-flex-box>
|
||||
<ft-element-list
|
||||
v-show="currentTab === 'playlists'"
|
||||
:data="latestPlaylists"
|
||||
/>
|
||||
<ft-flex-box
|
||||
v-if="currentTab === 'playlists' && latestPlaylists.length === 0"
|
||||
>
|
||||
<p class="message">
|
||||
This channel does not currently have any playlists
|
||||
</p>
|
||||
</ft-flex-box>
|
||||
<ft-element-list
|
||||
v-show="currentTab === 'search'"
|
||||
:data="searchResults"
|
||||
/>
|
||||
<ft-flex-box
|
||||
v-if="currentTab === 'search' && searchResults.length === 0"
|
||||
>
|
||||
<p class="message">
|
||||
Your search results have returned 0 results
|
||||
</p>
|
||||
</ft-flex-box>
|
||||
<div
|
||||
v-if="showFetchMoreButton"
|
||||
class="getNextPage"
|
||||
@click="handleFetchMore"
|
||||
>
|
||||
|
|
|
@ -12,4 +12,7 @@
|
|||
.playlistItems {
|
||||
float: right;
|
||||
width: 60%;
|
||||
padding: 10px;
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
}
|
||||
|
|
|
@ -13,16 +13,14 @@
|
|||
v-if="!isLoading"
|
||||
class="playlistItems"
|
||||
>
|
||||
<ft-flex-box>
|
||||
<ft-list-video
|
||||
v-for="(item, index) in playlistItems"
|
||||
:key="index"
|
||||
:data="item"
|
||||
:playlist-id="playlistId"
|
||||
force-list-type="list"
|
||||
class="playlistItem"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
<ft-list-video
|
||||
v-for="(item, index) in playlistItems"
|
||||
:key="index"
|
||||
:data="item"
|
||||
:playlist-id="playlistId"
|
||||
appearance="result"
|
||||
force-list-type="list"
|
||||
/>
|
||||
</ft-card>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
line-height: 45px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
|
|
|
@ -214,14 +214,20 @@ export default Vue.extend({
|
|||
this.videoDislikeCount = result.videoDetails.dislikes
|
||||
this.isLive = result.player_response.videoDetails.isLive
|
||||
|
||||
if (this.videoDislikeCount === null) {
|
||||
this.videoDislikeCount = 0
|
||||
}
|
||||
|
||||
const subCount = result.videoDetails.author.subscriber_count
|
||||
|
||||
if (subCount >= 1000000) {
|
||||
this.channelSubscriptionCountText = `${subCount / 1000000}M`
|
||||
} else if (subCount >= 10000) {
|
||||
this.channelSubscriptionCountText = `${subCount / 1000}K`
|
||||
} else {
|
||||
this.channelSubscriptionCountText = subCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
if (typeof (subCount) !== 'undefined') {
|
||||
if (subCount >= 1000000) {
|
||||
this.channelSubscriptionCountText = `${subCount / 1000000}M`
|
||||
} else if (subCount >= 10000) {
|
||||
this.channelSubscriptionCountText = `${subCount / 1000}K`
|
||||
} else {
|
||||
this.channelSubscriptionCountText = subCount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isLive) {
|
||||
|
|
|
@ -50,8 +50,10 @@
|
|||
min-width: 380px
|
||||
|
||||
.watchVideoPlaylist, .watchVideoSidebar, .theatrePlaylist
|
||||
height: 500px
|
||||
margin: 0 8px 16px
|
||||
|
||||
.watchVideoSidebar, .theatrePlaylist
|
||||
height: 500px
|
||||
|
||||
.watchVideoRecommendations, .theatreRecommendations
|
||||
margin: 0 8px 16px
|
||||
|
|