Merge branch 'master' into ft-toast
This commit is contained in:
commit
f31bf4f881
|
@ -6,8 +6,6 @@ name: Build
|
|||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -25,6 +23,7 @@ jobs:
|
|||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
- run: npm run build --if-present
|
||||
- name: Upload .deb Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Linter
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
lint:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js 12.X
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12.X
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
File diff suppressed because it is too large
Load Diff
43
package.json
43
package.json
|
@ -8,9 +8,9 @@
|
|||
"url": "https://github.com/FreeTubeApp/FreeTube/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.28",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"@fortawesome/fontawesome-svg-core": "^1.2.29",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.13.1",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.10",
|
||||
"@silvermine/videojs-quality-selector": "^1.2.4",
|
||||
"autolinker": "^3.14.1",
|
||||
"bulma-pro": "^0.2.0",
|
||||
|
@ -31,56 +31,58 @@
|
|||
"videojs-vtt-thumbnails": "0.0.13",
|
||||
"vue": "^2.6.11",
|
||||
"vue-electron": "^1.0.6",
|
||||
"vue-router": "^3.3.2",
|
||||
"vue-router": "^3.3.4",
|
||||
"vuex": "^3.4.0",
|
||||
"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-xml2vtt": "^1.0.1",
|
||||
"yt-channel-info": "git+https://github.com/FreeTubeApp/yt-channel-info.git",
|
||||
"yt-xml2vtt": "^1.1.1",
|
||||
"ytdl-core": "^3.1.1",
|
||||
"ytpl": "^0.1.21",
|
||||
"ytsr": "^0.1.15"
|
||||
},
|
||||
"description": "A private YouTube client",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.2",
|
||||
"@babel/core": "^7.10.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.10.1",
|
||||
"@babel/preset-env": "^7.10.2",
|
||||
"@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.1.0",
|
||||
"@typescript-eslint/parser": "^3.1.0",
|
||||
"acorn": "^7.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.3.0",
|
||||
"@typescript-eslint/parser": "^3.3.0",
|
||||
"acorn": "^7.3.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"copy-webpack-plugin": "^6.0.2",
|
||||
"css-loader": "^3.5.3",
|
||||
"css-loader": "^3.6.0",
|
||||
"devtron": "^1.4.0",
|
||||
"electron": "^8.3.0",
|
||||
"electron": "^9.0.4",
|
||||
"electron-builder": "^22.7.0",
|
||||
"electron-builder-squirrel-windows": "^22.7.0",
|
||||
"electron-debug": "^3.1.0",
|
||||
"electron-rebuild": "^1.11.0",
|
||||
"eslint": "^7.1.0",
|
||||
"eslint": "^7.3.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-import": "^2.21.2",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"fast-glob": "^3.2.2",
|
||||
"fast-glob": "^3.2.4",
|
||||
"file-loader": "^6.0.0",
|
||||
"html-webpack-plugin": "^4.3.0",
|
||||
"jest": "^26.0.1",
|
||||
"mini-css-extract-plugin": "^0.9.0",
|
||||
"node-abi": "^2.18.0",
|
||||
"node-loader": "^0.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.0.5",
|
||||
"sass": "^1.26.7",
|
||||
"sass": "^1.26.8",
|
||||
"sass-loader": "^8.0.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"tree-kill": "1.2.2",
|
||||
|
@ -92,7 +94,7 @@
|
|||
"vue-style-loader": "^4.1.2",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-cli": "^3.3.12",
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
@ -116,7 +118,8 @@
|
|||
"jest": "jest",
|
||||
"jest:coverage": "jest --collect-coverage",
|
||||
"jest:watch": "jest --watch",
|
||||
"lint": "eslint --fix --ext .js,.ts,.vue ./",
|
||||
"lint-fix": "eslint --fix --ext .js,.ts,.vue ./",
|
||||
"lint": "eslint --ext .js,.ts,.vue ./",
|
||||
"pack": "run-p pack:main pack:renderer pack:web pack:workers",
|
||||
"pack:main": "webpack --mode=production --env.NODE_ENV=production --hide-modules --config _scripts/webpack.main.config.js",
|
||||
"pack:renderer": "webpack --mode=production --env.NODE_ENV=production --hide-modules --config _scripts/webpack.renderer.config.js",
|
||||
|
|
|
@ -14,30 +14,36 @@ app.setName(productName)
|
|||
// disable electron warning
|
||||
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock()
|
||||
// const gotTheLock = app.requestSingleInstanceLock()
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
const isDebug = process.argv.includes('--debug')
|
||||
let mainWindow
|
||||
|
||||
// CORS somehow gets re-enabled in Electron v9.0.4
|
||||
// This line disables it.
|
||||
// This line can possible be removed if the issue is fixed upstream
|
||||
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors')
|
||||
|
||||
// TODO: Uncomment if needed
|
||||
// only allow single instance of application
|
||||
if (!isDev) {
|
||||
if (gotTheLock) {
|
||||
app.on('second-instance', () => {
|
||||
// Someone tried to run a second instance, we should focus our window.
|
||||
if (mainWindow && mainWindow.isMinimized()) {
|
||||
mainWindow.restore()
|
||||
}
|
||||
mainWindow.focus()
|
||||
})
|
||||
} else {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
} else {
|
||||
require('electron-debug')({
|
||||
showDevTools: !(process.env.RENDERER_REMOTE_DEBUGGING === 'true')
|
||||
})
|
||||
}
|
||||
// if (!isDev) {
|
||||
// if (gotTheLock) {
|
||||
// app.on('second-instance', () => {
|
||||
// // Someone tried to run a second instance, we should focus our window.
|
||||
// if (mainWindow && mainWindow.isMinimized()) {
|
||||
// mainWindow.restore()
|
||||
// }
|
||||
// mainWindow.focus()
|
||||
// })
|
||||
// } else {
|
||||
// app.quit()
|
||||
// process.exit(0)
|
||||
// }
|
||||
// } else {
|
||||
// require('electron-debug')({
|
||||
// showDevTools: !(process.env.RENDERER_REMOTE_DEBUGGING === 'true')
|
||||
// })
|
||||
// }
|
||||
|
||||
async function installDevTools () {
|
||||
try {
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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 {
|
||||
|
|
|
@ -15,7 +15,7 @@ export default Vue.extend({
|
|||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
default: ''
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
}"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
{{ label }}
|
||||
<slot>
|
||||
{{ label }}
|
||||
</slot>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,116 +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 p {
|
||||
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 p: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 p: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 p a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.left {
|
||||
right: 50%;
|
||||
}
|
||||
|
||||
.right {
|
||||
left: 50%;
|
||||
}
|
|
@ -19,10 +19,18 @@ export default Vue.extend({
|
|||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
dropdownPosition: {
|
||||
forceDropdown: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dropdownPositionX: {
|
||||
type: String,
|
||||
default: 'center'
|
||||
},
|
||||
dropdownPositionY: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
dropdownNames: {
|
||||
type: Array,
|
||||
default: () => { return [] }
|
||||
|
@ -43,7 +51,7 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
handleIconClick: function () {
|
||||
if (this.dropdownNames.length > 0 && this.dropdownValues.length > 0) {
|
||||
if (this.forceDropdown || (this.dropdownNames.length > 0 && this.dropdownValues.length > 0)) {
|
||||
this.toggleDropdown()
|
||||
} else {
|
||||
this.$emit('click')
|
||||
|
|
|
@ -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
|
|
@ -13,24 +13,34 @@
|
|||
@click="handleIconClick"
|
||||
/>
|
||||
<div
|
||||
v-if="dropdownNames.length > 0 && showDropdown"
|
||||
v-if="showDropdown"
|
||||
class="iconDropdown"
|
||||
:class="{
|
||||
left: dropdownPosition === 'left',
|
||||
right: dropdownPosition === 'right',
|
||||
center: dropdownPosition === 'center'
|
||||
left: dropdownPositionX === 'left',
|
||||
right: dropdownPositionX === 'right',
|
||||
center: dropdownPositionX === 'center',
|
||||
bottom: dropdownPositionY === 'bottom',
|
||||
top: dropdownPositionY === 'top'
|
||||
}"
|
||||
>
|
||||
<p
|
||||
v-for="(label, index) in dropdownNames"
|
||||
:key="index"
|
||||
@click="handleDropdownClick(index)"
|
||||
>
|
||||
{{ label }}
|
||||
</p>
|
||||
<slot>
|
||||
<ul
|
||||
v-if="dropdownNames.length > 0"
|
||||
class="list"
|
||||
>
|
||||
<li
|
||||
v-for="(label, index) in dropdownNames"
|
||||
:key="index"
|
||||
class="listItem"
|
||||
@click="handleDropdownClick(index)"
|
||||
>
|
||||
{{ label }}
|
||||
</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" />
|
||||
|
|
|
@ -49,6 +49,7 @@ export default Vue.extend({
|
|||
},
|
||||
mounted: function () {
|
||||
this.id = this._uid
|
||||
this.inputData = this.value
|
||||
|
||||
setTimeout(this.addListener, 200)
|
||||
},
|
||||
|
@ -57,9 +58,8 @@ export default Vue.extend({
|
|||
this.$emit('click', this.inputData)
|
||||
},
|
||||
|
||||
handleInput: function (input) {
|
||||
this.inputData = input
|
||||
this.$emit('input', input)
|
||||
handleInput: function () {
|
||||
this.$emit('input', this.inputData)
|
||||
},
|
||||
|
||||
addListener: function () {
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
</label>
|
||||
<input
|
||||
:id="id"
|
||||
v-model="inputData"
|
||||
:list="idDataList"
|
||||
:value="value"
|
||||
class="ft-input"
|
||||
type="text"
|
||||
:placeholder="placeholder"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ export default Vue.extend({
|
|||
|
||||
if (typeof (this.data.descriptionHtml) !== 'undefined' ||
|
||||
typeof (this.data.index) !== 'undefined' ||
|
||||
typeof (this.data.authorId) !== 'undefined' ||
|
||||
typeof (this.data.publishedText) !== 'undefined' ||
|
||||
typeof (this.data.authorThumbnails) === 'object'
|
||||
) {
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
title="More Options"
|
||||
theme="base"
|
||||
:use-shadow="false"
|
||||
dropdown-position="left"
|
||||
dropdown-position-x="left"
|
||||
:dropdown-names="optionsNames"
|
||||
:dropdown-values="optionsValues"
|
||||
@click="handleOptionsClick"
|
||||
|
|
|
@ -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()
|
||||
},
|
||||
}
|
||||
})
|
|
@ -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)
|
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<ft-icon-button
|
||||
ref="iconButton"
|
||||
title="Share Video"
|
||||
theme="secondary"
|
||||
icon="share-alt"
|
||||
dropdown-position-x="left"
|
||||
:force-dropdown="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"
|
||||
background-color="var(--accent-color-active)"
|
||||
@click="copyYoutubeEmbed()"
|
||||
>
|
||||
<font-awesome-icon icon="copy" />
|
||||
Copy embed
|
||||
</ft-button>
|
||||
<ft-button
|
||||
class="action"
|
||||
background-color="var(--accent-color-active)"
|
||||
@click="openYoutubeEmbed()"
|
||||
>
|
||||
<font-awesome-icon icon="globe" />
|
||||
Open embed
|
||||
</ft-button>
|
||||
</div>
|
||||
|
||||
<div class="divider" />
|
||||
|
||||
<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"
|
||||
background-color="var(--accent-color-active)"
|
||||
@click="copyInvidiousEmbed()"
|
||||
>
|
||||
<font-awesome-icon icon="copy" />
|
||||
Copy embed
|
||||
</ft-button>
|
||||
<ft-button
|
||||
class="action"
|
||||
background-color="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" />
|
|
@ -4,19 +4,19 @@
|
|||
>
|
||||
<input
|
||||
:id="id"
|
||||
v-model.number="currentValue"
|
||||
type="range"
|
||||
:min="minValue"
|
||||
:max="maxValue"
|
||||
:step="step"
|
||||
v-model.number="currentValue"
|
||||
@change="$emit('change', $event.target.value)"
|
||||
>
|
||||
<span>
|
||||
{{ label }} -
|
||||
<span>
|
||||
{{ label }} -
|
||||
<span>
|
||||
{{ displayLabel }}
|
||||
</span>
|
||||
{{ displayLabel }}
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
<input
|
||||
type="checkbox"
|
||||
:id="id"
|
||||
v-model="currentValue"
|
||||
type="checkbox"
|
||||
name="set-name"
|
||||
class="switch-input"
|
||||
:checked='currentValue'
|
||||
v-model="currentValue"
|
||||
:checked="currentValue"
|
||||
@change="$emit('change', currentValue)"
|
||||
>
|
||||
<label
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
.relative {
|
||||
position: relative;
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.ftVideoPlayer {
|
||||
width: 85%;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
|
|
@ -221,7 +221,11 @@ export default Vue.extend({
|
|||
qualitySelector(videojs, { showQualitySelectionLabelInControlBar: true })
|
||||
}
|
||||
|
||||
this.player = videojs(videoPlayer)
|
||||
this.player = videojs(videoPlayer, {
|
||||
userActions: {
|
||||
hotkeys: this.keyboardShortcutHandler
|
||||
}
|
||||
})
|
||||
|
||||
this.player.volume(this.volume)
|
||||
this.player.playbackRate(this.defaultPlayback)
|
||||
|
@ -245,7 +249,7 @@ export default Vue.extend({
|
|||
}, 200)
|
||||
}
|
||||
|
||||
$(document).on('keydown', this.keyboardShortcutHandler)
|
||||
// $(document).on('keydown', this.keyboardShortcutHandler)
|
||||
|
||||
this.player.on('mousemove', this.hideMouseTimeout)
|
||||
this.player.on('mouseleave', this.removeMouseTimeout)
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
:type="source.type || source.mimeType"
|
||||
:label="source.qualityLabel"
|
||||
:selected="source.qualityLabel === selectedDefaultQuality"
|
||||
/>
|
||||
>
|
||||
<track
|
||||
v-for="(caption, index) in captionList"
|
||||
:key="index + '_caption'"
|
||||
|
|
|
@ -552,6 +552,9 @@ export default Vue.extend({
|
|||
invidiousInstance: function () {
|
||||
return this.$store.getters.getInvidiousInstance
|
||||
},
|
||||
enableSearchSuggestions: function () {
|
||||
return this.$store.getters.getEnableSearchSuggestions
|
||||
},
|
||||
backendFallback: function () {
|
||||
return this.$store.getters.getBackendFallback
|
||||
},
|
||||
|
@ -616,6 +619,7 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
...mapActions([
|
||||
'updateEnableSearchSuggestions',
|
||||
'updateBackendFallback',
|
||||
'updateCheckForUpdates',
|
||||
'updateBarColor',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<ft-card
|
||||
class="card">
|
||||
class="card"
|
||||
>
|
||||
<h3
|
||||
class="videoTitle"
|
||||
>
|
||||
|
@ -12,6 +13,11 @@
|
|||
:default-value="backendFallback"
|
||||
@change="updateBackendFallback"
|
||||
/>
|
||||
<ft-toggle-switch
|
||||
label="Enable Search Suggestions"
|
||||
:default-value="enableSearchSuggestions"
|
||||
@change="updateEnableSearchSuggestions"
|
||||
/>
|
||||
<ft-toggle-switch
|
||||
v-if="false"
|
||||
label="Check for Updates"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<ft-card
|
||||
class="relative card">
|
||||
class="relative card"
|
||||
>
|
||||
<h3
|
||||
class="videoTitle"
|
||||
>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<ft-card
|
||||
class="relative card">
|
||||
class="relative card"
|
||||
>
|
||||
<h3
|
||||
class="videoTitle"
|
||||
>
|
||||
|
|
|
@ -21,11 +21,13 @@ export default Vue.extend({
|
|||
currentSecColor: '',
|
||||
baseThemeNames: [
|
||||
'Light',
|
||||
'Dark'
|
||||
'Dark',
|
||||
'Black'
|
||||
],
|
||||
baseThemeValues: [
|
||||
'light',
|
||||
'dark'
|
||||
'dark',
|
||||
'black'
|
||||
],
|
||||
colorNames: [
|
||||
'Red',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<ft-card
|
||||
class="relative card">
|
||||
class="relative card"
|
||||
>
|
||||
<h3>
|
||||
{{ title }}
|
||||
</h3>
|
||||
|
|
|
@ -17,11 +17,14 @@ export default Vue.extend({
|
|||
component: this,
|
||||
windowWidth: 0,
|
||||
showFilters: false,
|
||||
searchValue: '',
|
||||
searchSuggestionsDataList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
enableSearchSuggestions: function () {
|
||||
return this.$store.getters.getEnableSearchSuggestions
|
||||
},
|
||||
|
||||
searchSettings: function () {
|
||||
return this.$store.getters.getSearchSettings
|
||||
},
|
||||
|
@ -103,7 +106,9 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
getSearchSuggestionsDebounce: function (query) {
|
||||
this.debounceSearchResults(query)
|
||||
if (this.enableSearchSuggestions) {
|
||||
this.debounceSearchResults(query)
|
||||
}
|
||||
},
|
||||
|
||||
getSearchSuggestions: function (query) {
|
||||
|
@ -120,20 +125,17 @@ export default Vue.extend({
|
|||
getSearchSuggestionsLocal: function (query) {
|
||||
if (query === '') {
|
||||
this.searchSuggestionsDataList = []
|
||||
this.searchValue = ''
|
||||
return
|
||||
}
|
||||
|
||||
ytSuggest(query).then((results) => {
|
||||
this.searchSuggestionsDataList = results
|
||||
this.searchValue = query
|
||||
})
|
||||
},
|
||||
|
||||
getSearchSuggestionsInvidious: function (query) {
|
||||
if (query === '') {
|
||||
this.searchSuggestionsDataList = []
|
||||
this.searchValue = ''
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -147,7 +149,6 @@ export default Vue.extend({
|
|||
|
||||
this.$store.dispatch('invidiousAPICall', searchPayload).then((results) => {
|
||||
this.searchSuggestionsDataList = results.suggestions
|
||||
this.searchValue = query
|
||||
}).error((err) => {
|
||||
console.log(err)
|
||||
if (this.backendFallback) {
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
class="searchInput"
|
||||
:is-search="true"
|
||||
:data-list="searchSuggestionsDataList"
|
||||
:value="searchValue"
|
||||
@input="getSearchSuggestionsDebounce"
|
||||
@click="goToSearch"
|
||||
/>
|
||||
|
|
|
@ -60,7 +60,9 @@
|
|||
class="commentMoreReplies"
|
||||
@click="getCommentReplies(index)"
|
||||
>
|
||||
View {{ comment.numReplies }} replies
|
||||
<span v-if="!comment.showReplies">View</span>
|
||||
<span v-else>Hide</span>
|
||||
{{ comment.numReplies }} replies
|
||||
</p>
|
||||
<div
|
||||
v-if="comment.showReplies"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<ft-card class="videoDescription">
|
||||
<h4>Published on {{ dateString }}</h4>
|
||||
<p
|
||||
class="description"
|
||||
v-html="shownDescription"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -4,7 +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 FtToastEvents from '../ft-toast/ft-toast-events'
|
||||
import FtShareButton from '../ft-share-button/ft-share-button.vue'
|
||||
// import { shell } from 'electron'
|
||||
|
||||
export default Vue.extend({
|
||||
|
@ -14,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: {
|
||||
|
@ -37,6 +38,10 @@ export default Vue.extend({
|
|||
type: String,
|
||||
required: true
|
||||
},
|
||||
published: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
viewCount: {
|
||||
type: Number,
|
||||
required: true
|
||||
|
@ -66,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'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -91,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
|
||||
},
|
||||
|
@ -117,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: {
|
||||
|
@ -140,43 +126,6 @@ export default Vue.extend({
|
|||
this.$parent.enableAudioFormat()
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
handleShare: function (method) {
|
||||
console.log('Handling share')
|
||||
|
||||
switch (method) {
|
||||
case 'copyYoutube':
|
||||
FtToastEvents.$emit('toast.open', "YouTube URL copied to clipboard")
|
||||
navigator.clipboard.writeText(this.youtubeUrl)
|
||||
break
|
||||
case 'openYoutube':
|
||||
if (this.usingElectron) {
|
||||
const shell = require('electron').shell
|
||||
shell.openExternal(this.youtubeUrl)
|
||||
}
|
||||
break
|
||||
case 'copyYoutubeEmbed':
|
||||
FtToastEvents.$emit('toast.open', "YouTube Embed URL copied to clipboard")
|
||||
navigator.clipboard.writeText(this.youtubeEmbedUrl)
|
||||
break
|
||||
case 'openYoutubeEmbed':
|
||||
if (this.usingElectron) {
|
||||
const shell = require('electron').shell
|
||||
shell.openExternal(this.youtubeEmbedUrl)
|
||||
}
|
||||
break
|
||||
case 'copyInvidious':
|
||||
FtToastEvents.$emit('toast.open', "Invidious URL copied to clipboard")
|
||||
navigator.clipboard.writeText(this.invidiousUrl)
|
||||
break
|
||||
case 'openInvidious':
|
||||
if (this.usingElectron) {
|
||||
const shell = require('electron').shell
|
||||
shell.openExternal(this.invidiousUrl)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
.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
|
||||
|
||||
::v-deep .iconDropdown
|
||||
left: calc(50% - 20px)
|
||||
right: auto
|
||||
|
||||
@media only screen and (max-width: 1350px)
|
||||
.theatreModeButton
|
||||
display: none
|
|
@ -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>
|
||||
<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
|
||||
:id="id"
|
||||
class="option"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="likeCountContainer">
|
||||
<font-awesome-icon
|
||||
icon="thumbs-up"
|
||||
/>
|
||||
{{ likeCount }}
|
||||
|
||||
<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" />
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="comments.length === 0"
|
||||
class="messageContainer liveChatMessage"
|
||||
v-else-if="comments.length === 0"
|
||||
class="messageContainer liveChatMessage"
|
||||
>
|
||||
<p
|
||||
class="message"
|
||||
|
@ -53,7 +53,7 @@
|
|||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
>
|
||||
<p
|
||||
class="superChatContent"
|
||||
:style="{ color: 'var(--text-with-main-color)' }"
|
||||
|
@ -67,8 +67,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="openedSuperChat"
|
||||
v-if="showSuperChat"
|
||||
class="openedSuperChat"
|
||||
:class="superChat.superchat.colorClass"
|
||||
@click="showSuperChat = false"
|
||||
>
|
||||
|
@ -82,7 +82,7 @@
|
|||
<img
|
||||
:src="superChat.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
>
|
||||
<p
|
||||
class="channelName"
|
||||
>
|
||||
|
@ -95,11 +95,10 @@
|
|||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="chatMessage"
|
||||
v-if="superChat.message.length > 0"
|
||||
class="chatMessage"
|
||||
v-html="superChat.messageHtml"
|
||||
>
|
||||
</p>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -107,9 +106,11 @@
|
|||
:style="{ height: chatHeight }"
|
||||
@mousewheel="e => onScroll(e)"
|
||||
>
|
||||
<div v-for="(comment, index) in comments"
|
||||
:key="index"
|
||||
class="comment">
|
||||
<div
|
||||
v-for="(comment, index) in comments"
|
||||
:key="index"
|
||||
class="comment"
|
||||
>
|
||||
<div
|
||||
v-if="typeof (comment.superchat) !== 'undefined'"
|
||||
class="superChatMessage"
|
||||
|
@ -121,7 +122,7 @@
|
|||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
>
|
||||
<p
|
||||
class="channelName"
|
||||
>
|
||||
|
@ -134,50 +135,48 @@
|
|||
</p>
|
||||
</div>
|
||||
<p
|
||||
class="chatMessage"
|
||||
v-if="comment.message.length > 0"
|
||||
class="chatMessage"
|
||||
v-html="comment.messageHtml"
|
||||
>
|
||||
</p>
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
/>
|
||||
<p
|
||||
class="chatContent"
|
||||
>
|
||||
<span
|
||||
class="channelName"
|
||||
:class="{
|
||||
member: typeof (comment.author.badge) !== 'undefined' || comment.membership,
|
||||
moderator: comment.isOwner,
|
||||
owner: comment.author.name === channelName
|
||||
}"
|
||||
<img
|
||||
:src="comment.author.thumbnail.url"
|
||||
class="channelThumbnail"
|
||||
>
|
||||
{{ comment.author.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="typeof (comment.author.badge) !== 'undefined'"
|
||||
class="badge"
|
||||
<p
|
||||
class="chatContent"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.badge.thumbnail.url"
|
||||
:alt="comment.author.badge.thumbnail.alt"
|
||||
:title="comment.author.badge.thumbnail.alt"
|
||||
class="badgeImage"
|
||||
<span
|
||||
class="channelName"
|
||||
:class="{
|
||||
member: typeof (comment.author.badge) !== 'undefined' || comment.membership,
|
||||
moderator: comment.isOwner,
|
||||
owner: comment.author.name === channelName
|
||||
}"
|
||||
>
|
||||
{{ comment.author.name }}
|
||||
</span>
|
||||
<span
|
||||
v-if="typeof (comment.author.badge) !== 'undefined'"
|
||||
class="badge"
|
||||
>
|
||||
<img
|
||||
:src="comment.author.badge.thumbnail.url"
|
||||
:alt="comment.author.badge.thumbnail.alt"
|
||||
:title="comment.author.badge.thumbnail.alt"
|
||||
class="badgeImage"
|
||||
>
|
||||
</span>
|
||||
<span
|
||||
v-if="comment.message.length > 0"
|
||||
class="chatMessage"
|
||||
v-html="comment.messageHtml"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
class="chatMessage"
|
||||
v-if="comment.message.length > 0"
|
||||
v-html="comment.messageHtml"
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ const state = {
|
|||
thumbnailPreference: '',
|
||||
invidiousInstance: 'https://invidio.us',
|
||||
barColor: false,
|
||||
enableSearchSuggestions: true,
|
||||
rememberHistory: true,
|
||||
autoplayVideos: true,
|
||||
autoplayPlaylists: true,
|
||||
|
@ -72,6 +73,10 @@ const getters = {
|
|||
return state.barColor
|
||||
},
|
||||
|
||||
getEnableSearchSuggestions: () => {
|
||||
return state.enableSearchSuggestions
|
||||
},
|
||||
|
||||
getBackendPreference: () => {
|
||||
return state.backendPreference
|
||||
},
|
||||
|
@ -169,6 +174,9 @@ const actions = {
|
|||
case 'checkForUpdates':
|
||||
commit('setCheckForUpdates', result.value)
|
||||
break
|
||||
case 'enableSearchSuggestions':
|
||||
commit('setEnableSearchSuggestions', result.value)
|
||||
break
|
||||
case 'backendPreference':
|
||||
commit('setBackendPreference', result.value)
|
||||
break
|
||||
|
@ -255,6 +263,14 @@ const actions = {
|
|||
})
|
||||
},
|
||||
|
||||
updateEnableSearchSuggestions ({ commit }, enableSearchSuggestions) {
|
||||
settingsDb.update({ _id: 'enableSearchSuggestions' }, { _id: 'enableSearchSuggestions', value: enableSearchSuggestions }, { upsert: true }, (err, numReplaced) => {
|
||||
if (!err) {
|
||||
commit('setEnableSearchSuggestions', enableSearchSuggestions)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
updateBackendPreference ({ commit }, backendPreference) {
|
||||
settingsDb.update({ _id: 'backendPreference' }, { _id: 'backendPreference', value: backendPreference }, { upsert: true }, (err, numReplaced) => {
|
||||
if (!err) {
|
||||
|
@ -440,6 +456,9 @@ const mutations = {
|
|||
setBarColor (state, barColor) {
|
||||
state.barColor = barColor
|
||||
},
|
||||
setEnableSearchSuggestions (state, enableSearchSuggestions) {
|
||||
state.enableSearchSuggestions = enableSearchSuggestions
|
||||
},
|
||||
setRememberHistory (state, rememberHistory) {
|
||||
state.rememberHistory = rememberHistory
|
||||
},
|
||||
|
|
|
@ -53,18 +53,37 @@ const actions = {
|
|||
return state.colorClasses[randomInt]
|
||||
},
|
||||
|
||||
getVideoIdFromUrl ({ state }, url) {
|
||||
console.log('checking for id')
|
||||
console.log(url)
|
||||
const rx = /^.*(?:(?:(you|hook)tu\.?be\/|v\/|vi\/|u\/\w\/|embed\/)|(?:(?:watch)?\?v(?:i)?=|\&v(?:i)?=))([^#\&\?]*).*/
|
||||
|
||||
const match = url.match(rx)
|
||||
|
||||
if (match) {
|
||||
return match[2]
|
||||
} else {
|
||||
getVideoIdFromUrl (_, url) {
|
||||
/** @type {URL} */
|
||||
let urlObject
|
||||
try {
|
||||
urlObject = new URL(url)
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
|
||||
const extractors = [
|
||||
// anything with /watch?v=
|
||||
function() {
|
||||
if (urlObject.pathname === '/watch' && urlObject.searchParams.has('v')) {
|
||||
return urlObject.searchParams.get('v')
|
||||
}
|
||||
},
|
||||
// youtu.be
|
||||
function() {
|
||||
if (urlObject.host === 'youtu.be' && urlObject.pathname.match(/^\/[A-Za-z0-9_-]+$/)) {
|
||||
return urlObject.pathname.slice(1)
|
||||
}
|
||||
},
|
||||
// cloudtube
|
||||
function() {
|
||||
if (urlObject.host.match(/^cadence\.(gq|moe)$/) && urlObject.pathname.match(/^\/cloudtube\/video\/[A-Za-z0-9_-]+$/)) {
|
||||
return urlObject.pathname.slice('/cloudtube/video/'.length)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
return extractors.reduce((a, c) => a || c(), null) || false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
--primary-shadow-color: rgba(232, 232, 232, 1);
|
||||
--title-color: #3f7ac6;
|
||||
--bg-color: #f1f1f1;
|
||||
--link-color: var(--accent-color);
|
||||
--link-visited-color: var(--accent-color-visited);
|
||||
--card-bg-color: #FFFFFF;
|
||||
--secondary-card-bg-color: #eeeeee;
|
||||
--side-nav-color: #FFFFFF;
|
||||
--side-nav-hover-color: #e0e0e0;
|
||||
--side-nav-active-color: #757575;
|
||||
--search-bar-color: #f5f5f5;
|
||||
--instance-menu-color: var(--search-bar-color);
|
||||
--logo-icon: url("~../../_icons/iconColorSmall.png");
|
||||
--logo-text: url("~../../_icons/textColorSmall.png");
|
||||
}
|
||||
|
@ -20,21 +23,44 @@
|
|||
.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;
|
||||
--bg-color: #212121;
|
||||
--link-color: var(--accent-color);
|
||||
--link-visited-color: var(--accent-color-visited);
|
||||
--card-bg-color: #303030;
|
||||
--secondary-card-bg-color: rgba(0, 0, 0, 0.75);
|
||||
--side-nav-color: #262626;
|
||||
--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-text: url("~../../_icons/textColorSmall.png");
|
||||
}
|
||||
|
||||
.black {
|
||||
--primary-text-color: #EEEEEE;
|
||||
--secondary-text-color: #ddd;
|
||||
--tertiary-text-color: #EEEEEE;
|
||||
--primary-input-color: rgba(0, 0, 0, 0.50);
|
||||
--primary-shadow-color: rgba(0, 0, 0, 0.75);
|
||||
--title-color: #EEEEEEE;
|
||||
--bg-color: #000000;
|
||||
--link-color: var(--accent-color);
|
||||
--link-visited-color: var(--accent-color-visited);
|
||||
--card-bg-color: #000000;
|
||||
--secondary-card-bg-color: rgba(0, 0, 0, 0.75);
|
||||
--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-text: url("~../../_icons/textColorSmall.png");
|
||||
}
|
||||
|
||||
.gray {
|
||||
--primary-text-color: #EEEEEE;
|
||||
|
@ -203,6 +229,7 @@
|
|||
--accent-color-hover: #e53935;
|
||||
--accent-color-active: #c62828;
|
||||
--accent-color-light: #ef9a9a;
|
||||
--accent-color-visited: #b71c1c;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(244,67,54,0.04);
|
||||
--accent-color-opacity2: rgba(244,67,54,0.12);
|
||||
|
@ -215,6 +242,7 @@
|
|||
--accent-color-hover: #D81B60;
|
||||
--accent-color-active: #AD1457;
|
||||
--accent-color-light: #F48FB1;
|
||||
--accent-color-visited: #880E4F;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(233,30,99,0.04);
|
||||
--accent-color-opacity2: rgba(233,30,99,0.12);
|
||||
|
@ -227,6 +255,7 @@
|
|||
--accent-color-hover: #8E24AA;
|
||||
--accent-color-active: #6A1B9A;
|
||||
--accent-color-light: #CE93D8;
|
||||
--accent-color-visited: #4A148C;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(156,39,176,0.04);
|
||||
--accent-color-opacity2: rgba(156,39,176,0.12);
|
||||
|
@ -239,6 +268,7 @@
|
|||
--accent-color-hover: #5E35B1;
|
||||
--accent-color-active: #4527A0;
|
||||
--accent-color-light: #B39DDB;
|
||||
--accent-color-visited: #311B92;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(103,58,183,0.04);
|
||||
--accent-color-opacity2: rgba(103,58,183,0.12);
|
||||
|
@ -251,6 +281,7 @@
|
|||
--accent-color-hover: #3949AB;
|
||||
--accent-color-active: #283593;
|
||||
--accent-color-light: #9FA8DA;
|
||||
--accent-color-visited: #1A237E;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(63,81,181,0.04);
|
||||
--accent-color-opacity2: rgba(63,81,181,0.12);
|
||||
|
@ -263,6 +294,7 @@
|
|||
--accent-color-hover: #1E88E5;
|
||||
--accent-color-active: #1565C0;
|
||||
--accent-color-light: #90CAF9;
|
||||
--accent-color-visited: #0D47A1;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(33,150,243,0.04);
|
||||
--accent-color-opacity2: rgba(33,150,243,0.12);
|
||||
|
@ -275,6 +307,7 @@
|
|||
--accent-color-hover: #039BE5;
|
||||
--accent-color-active: #0277BD;
|
||||
--accent-color-light: #81D4FA;
|
||||
--accent-color-visited: #01579B;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(3,169,244,0.04);
|
||||
--accent-color-opacity2: rgba(3,169,244,0.12);
|
||||
|
@ -287,6 +320,7 @@
|
|||
--accent-color-hover: #00ACC1;
|
||||
--accent-color-active: #00838F;
|
||||
--accent-color-light: #80DEEA;
|
||||
--accent-color-visited: #006064;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(0,188,212,0.04);
|
||||
--accent-color-opacity2: rgba(0,188,212,0.12);
|
||||
|
@ -299,6 +333,7 @@
|
|||
--accent-color-hover: #00897B;
|
||||
--accent-color-active: #00695C;
|
||||
--accent-color-light: #80CBC4;
|
||||
--accent-color-visited: #004D40;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(0,150,136,0.04);
|
||||
--accent-color-opacity2: rgba(0,150,136,0.12);
|
||||
|
@ -311,6 +346,7 @@
|
|||
--accent-color-hover: #43A047;
|
||||
--accent-color-active: #2E7D32;
|
||||
--accent-color-light: #A5D6A7;
|
||||
--accent-color-visited: #1B5E20;
|
||||
--text-with-accent-color: #FFFFFF;
|
||||
--accent-color-opacity1: rgba(76,175,80,0.04);
|
||||
--accent-color-opacity2: rgba(76,175,80,0.12);
|
||||
|
@ -323,6 +359,7 @@
|
|||
--accent-color-hover: #7CB342;
|
||||
--accent-color-active: #558B2F;
|
||||
--accent-color-light: #C5E1A5;
|
||||
--accent-color-visited: #33691E;
|
||||
--text-with-accent-color: #000000;
|
||||
--accent-color-opacity1: rgba(139,195,74,0.04);
|
||||
--accent-color-opacity2: rgba(139,195,74,0.12);
|
||||
|
@ -335,6 +372,7 @@
|
|||
--accent-color-hover: #C0CA33;
|
||||
--accent-color-active: #9E9D24;
|
||||
--accent-color-light: #E6EE9C;
|
||||
--accent-color-visited: #827717;
|
||||
--text-with-accent-color: #000000;
|
||||
--accent-color-opacity1: rgba(205,220,57,0.04);
|
||||
--accent-color-opacity2: rgba(205,220,57,0.12);
|
||||
|
@ -347,6 +385,7 @@
|
|||
--accent-color-hover: #FDD835;
|
||||
--accent-color-active: #F9A825;
|
||||
--accent-color-light: #FFF59D;
|
||||
--accent-color-visited: #F57F17;
|
||||
--text-with-accent-color: #000000;
|
||||
--accent-color-opacity1: rgba(255,235,59,0.04);
|
||||
--accent-color-opacity2: rgba(255,235,59,0.12);
|
||||
|
@ -359,6 +398,7 @@
|
|||
--accent-color-hover: #FFB300;
|
||||
--accent-color-active: #FF8F00;
|
||||
--accent-color-light: #FFE082;
|
||||
--accent-color-visited: #FF6F00;
|
||||
--text-with-accent-color: #000000;
|
||||
--accent-color-opacity1: rgba(255,193,7,0.04);
|
||||
--accent-color-opacity2: rgba(255,193,7,0.12);
|
||||
|
@ -371,6 +411,7 @@
|
|||
--accent-color-hover: #FB8C00;
|
||||
--accent-color-active: #EF6C00;
|
||||
--accent-color-light: #FFCC80;
|
||||
--accent-color-visited: #E65100;
|
||||
--text-with-accent-color: #000000;
|
||||
--accent-color-opacity1: rgba(255,152,0,0.04);
|
||||
--accent-color-opacity2: rgba(255,152,0,0.12);
|
||||
|
@ -383,6 +424,7 @@
|
|||
--accent-color-hover: #F4511E;
|
||||
--accent-color-active: #D84315;
|
||||
--accent-color-light: #FFAB91;
|
||||
--accent-color-visited: #BF360C;
|
||||
--text-with-accent-color: #000000;
|
||||
--accent-color-opacity1: rgba(255,87,34,0.04);
|
||||
--accent-color-opacity2: rgba(255,87,34,0.12);
|
||||
|
@ -400,3 +442,9 @@ body {
|
|||
color: var(--primary-text-color);
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
a:link {
|
||||
color: var(--link-color);
|
||||
}
|
||||
a:visited {
|
||||
color: var(--link-visited-color);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import FtChannelBubble from '../../components/ft-channel-bubble/ft-channel-bubbl
|
|||
import FtLoader from '../../components/ft-loader/ft-loader.vue'
|
||||
import FtElementList from '../../components/ft-element-list/ft-element-list.vue'
|
||||
|
||||
import ytch from 'yt-channel-info'
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'Search',
|
||||
components: {
|
||||
|
@ -32,7 +34,9 @@ export default Vue.extend({
|
|||
subCount: 0,
|
||||
latestVideosPage: 2,
|
||||
searchPage: 2,
|
||||
videoContinuationString: '',
|
||||
playlistContinuationString: '',
|
||||
searchContinuationString: '',
|
||||
channelDescription: '',
|
||||
videoSortBy: 'newest',
|
||||
playlistSortBy: 'last',
|
||||
|
@ -42,6 +46,7 @@ export default Vue.extend({
|
|||
latestPlaylists: [],
|
||||
searchResults: [],
|
||||
shownElementList: [],
|
||||
apiUsed: '',
|
||||
videoSelectNames: [
|
||||
'Newest',
|
||||
'Oldest',
|
||||
|
@ -65,6 +70,18 @@ export default Vue.extend({
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
usingElectron: function () {
|
||||
return this.$store.getters.getUsingElectron
|
||||
},
|
||||
|
||||
backendPreference: function () {
|
||||
return this.$store.getters.getBackendPreference
|
||||
},
|
||||
|
||||
backendFallback: function () {
|
||||
return this.$store.getters.getBackendFallback
|
||||
},
|
||||
|
||||
sessionSearchHistory: function () {
|
||||
return this.$store.getters.getSessionSearchHistory
|
||||
},
|
||||
|
@ -77,25 +94,92 @@ export default Vue.extend({
|
|||
videoSortBy () {
|
||||
this.isElementListLoading = true
|
||||
this.latestVideos = []
|
||||
this.latestVideosPage = 1
|
||||
this.channelNextPage()
|
||||
switch (this.apiUsed) {
|
||||
case 'local':
|
||||
this.getChannelVideosLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
this.latestVideosPage = 1
|
||||
this.channelInvidiousNextPage()
|
||||
break
|
||||
default:
|
||||
this.getChannelVideosLocal()
|
||||
}
|
||||
},
|
||||
|
||||
playlistSortBy () {
|
||||
this.isElementListLoading = true
|
||||
this.latestPlaylists = []
|
||||
this.playlistContinuationString = ''
|
||||
this.getPlaylists()
|
||||
switch (this.apiUsed) {
|
||||
case 'local':
|
||||
this.getPlaylistsLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
this.channelInvidiousNextPage()
|
||||
break
|
||||
default:
|
||||
this.getPlaylistsLocal()
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.id = this.$route.params.id
|
||||
this.isLoading = true
|
||||
|
||||
this.getChannelInfo()
|
||||
this.getPlaylists()
|
||||
if (!this.usingElectron) {
|
||||
this.getVideoInformationInvidious()
|
||||
} 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
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getChannelInfo: function () {
|
||||
getChannelInfoLocal: function () {
|
||||
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
|
||||
this.isLoading = false
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
|
||||
getChannelVideosLocal: function () {
|
||||
this.isElementListLoading = true
|
||||
ytch.getChannelVideos(this.id, this.videoSortBy).then((response) => {
|
||||
this.latestVideos = response.items
|
||||
this.videoContinuationString = response.continuation
|
||||
this.isElementListLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
channelLocalNextPage: function () {
|
||||
console.log(this.videoContinuationString)
|
||||
ytch.getChannelVideosMore(this.id, this.videoContinuationString).then((response) => {
|
||||
this.latestVideos = this.latestVideos.concat(response.items)
|
||||
this.videoContinuationString = response.continuation
|
||||
console.log(this.videoContinuationString)
|
||||
})
|
||||
},
|
||||
|
||||
getChannelInfoInvidious: function () {
|
||||
this.isLoading = true
|
||||
|
||||
this.$store.dispatch('invidiousGetChannelInfo', this.id).then((response) => {
|
||||
|
@ -115,7 +199,7 @@ export default Vue.extend({
|
|||
})
|
||||
},
|
||||
|
||||
channelNextPage: function () {
|
||||
channelInvidiousNextPage: function () {
|
||||
const payload = {
|
||||
resource: 'channels/videos',
|
||||
id: this.id,
|
||||
|
@ -132,7 +216,24 @@ export default Vue.extend({
|
|||
})
|
||||
},
|
||||
|
||||
getPlaylists: function () {
|
||||
getPlaylistsLocal: function () {
|
||||
ytch.getChannelPlaylistInfo(this.id, this.playlistSortBy).then((response) => {
|
||||
console.log(response)
|
||||
this.latestPlaylists = response.items
|
||||
this.playlistContinuationString = response.continuation
|
||||
this.isElementListLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
getPlaylistsLocalMore: function () {
|
||||
ytch.getChannelPlaylistsMore(this.id, this.playlistContinuationString).then((response) => {
|
||||
console.log(response)
|
||||
this.latestPlaylists = this.latestPlaylists.concat(response.items)
|
||||
this.playlistContinuationString = response.continuation
|
||||
})
|
||||
},
|
||||
|
||||
getPlaylistsInvidious: function () {
|
||||
if (this.playlistContinuationString === null) {
|
||||
console.log('There are no more playlists available for this channel')
|
||||
return
|
||||
|
@ -163,13 +264,34 @@ export default Vue.extend({
|
|||
handleFetchMore: function () {
|
||||
switch (this.currentTab) {
|
||||
case 'videos':
|
||||
this.channelNextPage()
|
||||
switch (this.apiUsed) {
|
||||
case 'local':
|
||||
this.channelLocalNextPage()
|
||||
break
|
||||
case 'invidious':
|
||||
this.channelInvidiousNextPage()
|
||||
break
|
||||
}
|
||||
break
|
||||
case 'playlists':
|
||||
this.getPlaylists()
|
||||
switch (this.apiUsed) {
|
||||
case 'local':
|
||||
this.getPlaylistsLocalMore()
|
||||
break
|
||||
case 'invidious':
|
||||
this.getPlaylistsInvidious()
|
||||
break
|
||||
}
|
||||
break
|
||||
case 'search':
|
||||
this.searchChannel()
|
||||
switch (this.apiUsed) {
|
||||
case 'local':
|
||||
this.searchChannelLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
this.searchChannelInvidious()
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
},
|
||||
|
@ -180,14 +302,40 @@ export default Vue.extend({
|
|||
|
||||
newSearch: function (query) {
|
||||
this.lastSearchQuery = query
|
||||
this.searchContinuationString = ''
|
||||
this.isElementListLoading = true
|
||||
this.searchPage = 1
|
||||
this.searchResults = []
|
||||
this.changeTab('search')
|
||||
this.searchChannel()
|
||||
switch (this.apiUsed) {
|
||||
case 'local':
|
||||
this.searchChannelLocal()
|
||||
break
|
||||
case 'invidious':
|
||||
this.searchChannelInvidious()
|
||||
break
|
||||
}
|
||||
},
|
||||
|
||||
searchChannel: function () {
|
||||
searchChannelLocal: function () {
|
||||
if (this.searchContinuationString === '') {
|
||||
ytch.searchChannel(this.id, this.lastSearchQuery).then((response) => {
|
||||
console.log(response)
|
||||
this.searchResults = response.items
|
||||
this.isElementListLoading = false
|
||||
this.searchContinuationString = response.continuation
|
||||
})
|
||||
} else {
|
||||
ytch.searchChannelMore(this.id, this.searchContinuationString).then((response) => {
|
||||
console.log(response)
|
||||
this.searchResults = this.searchResults.concat(response.items)
|
||||
this.isElementListLoading = false
|
||||
this.searchContinuationString = response.continuation
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
searchChannelInvidious: function () {
|
||||
const payload = {
|
||||
resource: 'channels/search',
|
||||
id: this.id,
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
:key="index"
|
||||
:channel-name="channel.author"
|
||||
:channel-id="channel.authorId"
|
||||
:channel-thumbnail="channel.authorThumbnails[3].url"
|
||||
:channel-thumbnail="channel.authorThumbnails[channel.authorThumbnails.length - 1].url"
|
||||
/>
|
||||
</ft-flex-box>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -1,80 +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"
|
||||
: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"
|
||||
ref="videoPlayer"
|
||||
:dash-src="dashSrc"
|
||||
:source-list="activeSourceList"
|
||||
:caption-list="captionSourceList"
|
||||
:storyboard-src="videoStoryboardSrc"
|
||||
:format="activeFormat"
|
||||
:thumbnail="thumbnail"
|
||||
class="videoPlayer"
|
||||
:class="{ theatrePlayer: useTheatreMode }"
|
||||
@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"
|
||||
class="watchVideo"
|
||||
:class="{ theatreWatchVideo: useTheatreMode }"
|
||||
@theatreMode="toggleTheatreMode"
|
||||
/>
|
||||
<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"
|
||||
ref="watchVideoPlaylist"
|
||||
:playlist-id="playlistId"
|
||||
:video-id="videoId"
|
||||
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" />
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"File": "File",
|
||||
"Quit": "Quit",
|
||||
"Edit": "Edit",
|
||||
"Undo": "Undo",
|
||||
"Redo": "Redo",
|
||||
"Cut": "Cut",
|
||||
"Copy": "Copy",
|
||||
"Paste": "Paste",
|
||||
"Delete": "Delete",
|
||||
"Select all": "Select all",
|
||||
"View": "View",
|
||||
"Reload": "Reload",
|
||||
"Force Reload": "Force Reload",
|
||||
"Toggle Developer Tools": "Toggle Developer Tools",
|
||||
"Actual size": "Actual size",
|
||||
"Zoom in": "Zoom in",
|
||||
"Zoom out": "Zoom out",
|
||||
"Toggle fullscreen": "Toggle fullscreen",
|
||||
"Window": "Window",
|
||||
"Minimize": "Minimize",
|
||||
"Close": "Close",
|
||||
"FreeTube": "FreeTube",
|
||||
"Subscriptions": "Subscriptions",
|
||||
"Featured": "Featured",
|
||||
"Most Popular": "Most Popular",
|
||||
"Saved": "Saved",
|
||||
"Playlists": "Playlists",
|
||||
"History": "History",
|
||||
"Settings": "Settings",
|
||||
"About": "About",
|
||||
"Search / Go to URL": "Search / Go to URL",
|
||||
"Search Results": "Search Results",
|
||||
"Subscriber": "Subscriber",
|
||||
"Subscribers": "Subscribers",
|
||||
"Video": "Video",
|
||||
"Videos": "Videos",
|
||||
"View Full Playlist": "View Full Playlist",
|
||||
"Live Now": "Live Now",
|
||||
"Fetch more results": "Fetch more results",
|
||||
"Fetching results. Please wait": "Fetching results. Please wait",
|
||||
"Latest Subscriptions": "Latest Subscriptions",
|
||||
"Save Video": "Save Video",
|
||||
"Remove Saved Video": "Remove Saved Video",
|
||||
"Open in YouTube": "Open in YouTube",
|
||||
"Copy YouTube Link": "Copy YouTube Link",
|
||||
"Open in Invidious": "Open in Invidious",
|
||||
"Copy Invidious Link": "Copy Invidious Link",
|
||||
"URL has been copied to the clipboard": "URL copied to clipboard",
|
||||
"Found valid URL for 480p, but returned a 404. Video type might be available in the future.": "Found valid URL for 480p, but returned a 404. Video type might be available in the future.",
|
||||
"Save": "Save",
|
||||
"Mini Player": "Mini Player",
|
||||
"View": "View",
|
||||
"Views": "Views",
|
||||
"Subscribe": "Subscribe",
|
||||
"Unsubscribe": "Unsubscribe",
|
||||
"Published on": "Published on",
|
||||
"Jan": "Jan",
|
||||
"Feb": "Feb",
|
||||
"Mar": "Mar",
|
||||
"Apr": "Apr",
|
||||
"May": "May",
|
||||
"Jun": "Jun",
|
||||
"Jul": "Jul",
|
||||
"Aug": "Aug",
|
||||
"Sep": "Sep",
|
||||
"Oct": "Oct",
|
||||
"Nov": "Nov",
|
||||
"Dec": "Dec",
|
||||
"Show Comments": "Show Comments",
|
||||
"Max of 100": "Max of 100",
|
||||
"Recommendations": "Recommendations",
|
||||
"Latest Subscriptions": "Latest Subscriptions",
|
||||
"Getting Subscriptions. Please wait...": "Getting Subscriptions. Please wait…",
|
||||
"Your Subscription list is currently empty. Start adding subscriptions to see them here.": "Add subscriptions to see them here.",
|
||||
"Saved Videos": "Saved Videos",
|
||||
"Watch History": "Watch History",
|
||||
"Use Dark Theme": "Use Dark Theme",
|
||||
"Import Subscriptions": "Import Subscriptions",
|
||||
"Export Subscriptions": "Export Subscriptions",
|
||||
"Clear History": "Clear History",
|
||||
"Are you sure you want to delete your history?": "Are you sure you want to delete your history?",
|
||||
"Clear Saved Videos": "Clear Favorited Videos",
|
||||
"Are you sure you want to remove all saved videos?": "Are you sure you want to remove all saved videos?",
|
||||
"Clear Subscriptions": "Clear Subscriptions",
|
||||
"Are you sure you want to remove all subscriptions?": "Are you sure you want to remove all subscriptions?",
|
||||
"Save Settings": "Save Settings",
|
||||
"Yes": "Yes",
|
||||
"No": "No",
|
||||
"Beta": "Beta",
|
||||
"This software is FOSS and released under the GNU Public License v3+.": "This copylefted software is freely licensed GPLv3+.",
|
||||
"Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests are welcome.": "Found a bug? Want to suggest a feature? Want to help out? Check out our GitHub page. Pull requests welcome."
|
||||
}
|
|
@ -143,7 +143,7 @@ function fromCache(request) {
|
|||
return caches.open(CACHE).then(function (cache) {
|
||||
return cache.match(request).then(function (matching) {
|
||||
if (!matching || matching.status === 404) {
|
||||
return Promise.reject('no-match')
|
||||
return Promise.reject(new Error('no-match'))
|
||||
}
|
||||
|
||||
return matching
|
||||
|
|
Loading…
Reference in New Issue