Browse Source

Push Latest Code to Repository

pull/542/head
Preston 2 years ago
parent
commit
ded6534d16
  1. 18
      .babelrc
  2. 12
      .editorconfig
  3. 3
      .eslintignore
  4. 42
      .eslintrc.js
  5. 114
      .gitignore
  6. 7
      .prettierrc
  7. 51
      .travis.yml
  8. 8
      .whitesource
  9. BIN
      _icons/256x256.png
  10. BIN
      _icons/icon.icns
  11. BIN
      _icons/icon.ico
  12. BIN
      _icons/iconBlack.png
  13. BIN
      _icons/iconBlackSmall.png
  14. BIN
      _icons/iconColor.icns
  15. BIN
      _icons/iconColor.ico
  16. BIN
      _icons/iconColor.png
  17. BIN
      _icons/iconColorSmall.png
  18. BIN
      _icons/iconWhite.png
  19. BIN
      _icons/logoBlack.png
  20. BIN
      _icons/logoColor.png
  21. BIN
      _icons/logoWhite.png
  22. 1
      _icons/mejs-controls.svg
  23. BIN
      _icons/textBlack.png
  24. BIN
      _icons/textBlackSmall.png
  25. BIN
      _icons/textColor.png
  26. BIN
      _icons/textColorSmall.png
  27. BIN
      _icons/textWhite.png
  28. 77
      _scripts/build.js
  29. 120
      _scripts/dev-runner.js
  30. 75
      _scripts/webpack.main.config.js
  31. 165
      _scripts/webpack.renderer.config.js
  32. 67
      _scripts/webpack.workers.config.js
  33. 20
      appveyor.yml
  34. 15692
      package-lock.json
  35. 124
      package.json
  36. 0
      src/data/.gitkeep
  37. 30
      src/index.ejs
  38. 247
      src/main/index.js
  39. 24
      src/renderer/App.css
  40. 29
      src/renderer/App.js
  41. 24
      src/renderer/App.vue
  42. BIN
      src/renderer/assets/font/Roboto-Regular.ttf
  43. BIN
      src/renderer/assets/font/VideoJS.woff
  44. 50
      src/renderer/components/ft-button/ft-button.css
  45. 29
      src/renderer/components/ft-button/ft-button.js
  46. 16
      src/renderer/components/ft-button/ft-button.vue
  47. 8
      src/renderer/components/ft-card/ft-card.css
  48. 5
      src/renderer/components/ft-card/ft-card.js
  49. 8
      src/renderer/components/ft-card/ft-card.vue
  50. 33
      src/renderer/components/ft-channel-bubble/ft-channel-bubble.css
  51. 24
      src/renderer/components/ft-channel-bubble/ft-channel-bubble.js
  52. 17
      src/renderer/components/ft-channel-bubble/ft-channel-bubble.vue
  53. 3
      src/renderer/components/ft-element-list/ft-element-list.css
  54. 33
      src/renderer/components/ft-element-list/ft-element-list.js
  55. 50
      src/renderer/components/ft-element-list/ft-element-list.vue
  56. 5
      src/renderer/components/ft-flex-box/ft-flex-box.css
  57. 5
      src/renderer/components/ft-flex-box/ft-flex-box.js
  58. 8
      src/renderer/components/ft-flex-box/ft-flex-box.vue
  59. 6
      src/renderer/components/ft-grid/ft-grid.css
  60. 5
      src/renderer/components/ft-grid/ft-grid.js
  61. 8
      src/renderer/components/ft-grid/ft-grid.vue
  62. 46
      src/renderer/components/ft-input/ft-input.css
  63. 44
      src/renderer/components/ft-input/ft-input.js
  64. 20
      src/renderer/components/ft-input/ft-input.vue
  65. 93
      src/renderer/components/ft-list-channel/ft-list-channel.css
  66. 57
      src/renderer/components/ft-list-channel/ft-list-channel.js
  67. 40
      src/renderer/components/ft-list-channel/ft-list-channel.vue
  68. 68
      src/renderer/components/ft-list-dropdown/ft-list-dropdown.css
  69. 42
      src/renderer/components/ft-list-dropdown/ft-list-dropdown.js
  70. 24
      src/renderer/components/ft-list-dropdown/ft-list-dropdown.vue
  71. 106
      src/renderer/components/ft-list-playlist/ft-list-playlist.css
  72. 71
      src/renderer/components/ft-list-playlist/ft-list-playlist.js
  73. 38
      src/renderer/components/ft-list-playlist/ft-list-playlist.vue
  74. 172
      src/renderer/components/ft-list-video/ft-list-video.css
  75. 199
      src/renderer/components/ft-list-video/ft-list-video.js
  76. 83
      src/renderer/components/ft-list-video/ft-list-video.vue
  77. 85
      src/renderer/components/ft-loader/ft-loader.css
  78. 11
      src/renderer/components/ft-loader/ft-loader.js
  79. 14
      src/renderer/components/ft-loader/ft-loader.vue
  80. 106
      src/renderer/components/ft-radio-button/ft-radio-button.css
  81. 35
      src/renderer/components/ft-radio-button/ft-radio-button.js
  82. 33
      src/renderer/components/ft-radio-button/ft-radio-button.vue
  83. 20
      src/renderer/components/ft-search-filters/ft-search-filters.css
  84. 91
      src/renderer/components/ft-search-filters/ft-search-filters.js
  85. 40
      src/renderer/components/ft-search-filters/ft-search-filters.vue
  86. 127
      src/renderer/components/ft-select/ft-select.css
  87. 23
      src/renderer/components/ft-select/ft-select.js
  88. 29
      src/renderer/components/ft-select/ft-select.vue
  89. 8
      src/renderer/components/ft-video-player/ft-video-player.css
  90. 67
      src/renderer/components/ft-video-player/ft-video-player.js
  91. 21
      src/renderer/components/ft-video-player/ft-video-player.vue
  92. 29
      src/renderer/components/playlist-info/playlist-info.css
  93. 89
      src/renderer/components/playlist-info/playlist-info.js
  94. 39
      src/renderer/components/playlist-info/playlist-info.vue
  95. 92
      src/renderer/components/side-nav/side-nav.css
  96. 16
      src/renderer/components/side-nav/side-nav.js
  97. 101
      src/renderer/components/side-nav/side-nav.vue
  98. 173
      src/renderer/components/top-nav/top-nav.css
  99. 56
      src/renderer/components/top-nav/top-nav.js
  100. 41
      src/renderer/components/top-nav/top-nav.vue

18
.babelrc

@ -0,0 +1,18 @@
{
"presets": [
[
"@babel/env",
{
"targets": {
"chrome": "73",
"node": 12
}
}
],
"@babel/typescript"
],
"plugins": [
"@babel/proposal-class-properties",
"@babel/proposal-object-rest-spread"
]
}

12
.editorconfig

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

3
.eslintignore

@ -0,0 +1,3 @@
node_modules
_scripts
dist

42
.eslintrc.js

@ -0,0 +1,42 @@
module.exports = {
// https://eslint.org/docs/user-guide/configuring#using-configuration-files-1
root: true,
// https://eslint.org/docs/user-guide/configuring#specifying-environments
env: {
browser: true,
node: true,
},
// https://eslint.org/docs/user-guide/configuring#specifying-parser
parser: 'vue-eslint-parser',
// https://vuejs.github.io/eslint-plugin-vue/user-guide/#faq
parserOptions: {
parser: 'babel-eslint',
ecmaVersion: 2018,
sourceType: 'module',
},
// https://eslint.org/docs/user-guide/configuring#extending-configuration-files
// order matters: from least important to most important in terms of overriding
// Prettier + Vue: https://medium.com/@gogl.alex/how-to-properly-set-up-eslint-with-prettier-for-vue-or-nuxt-in-vscode-e42532099a9c
extends: [
'prettier/vue',
'prettier',
'eslint:recommended',
'plugin:vue/recommended',
'standard',
],
// https://eslint.org/docs/user-guide/configuring#configuring-plugins
plugins: ['vue'],
rules: {
'vue/no-v-html': "off",
'no-console': 0,
'no-unused-vars': 1,
'no-undef': 1,
'vue/no-template-key': 1
},
}

114
.gitignore

@ -1,104 +1,18 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
.DS_Store
dist/electron/*
dist/web/*
build/*
!build/icons
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
npm-debug.log
npm-debug.log.*
thumbs.db
!.gitkeep
data/tmp/
.tmp/
tmp/
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
coverage
__coverage__

7
.prettierrc

@ -0,0 +1,7 @@
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false
}

51
.travis.yml

@ -0,0 +1,51 @@
matrix:
include:
- os: osx
osx_image: xcode9.3
language: node_js
node_js: '12'
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
- os: linux
services: docker
language: node_js
node_js: '12'
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
cache:
directories:
- node_modules
- $HOME/.cache/electron
- $HOME/.cache/electron-builder
before_install:
- |
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v2.3.1/git-lfs-$([ "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-2.3.1.tar.gz | tar -xz -C /tmp/git-lfs --strip-components 1
export PATH="/tmp/git-lfs:$PATH"
fi
before_script:
- git lfs pull
script:
- |
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
docker run --rm \
--env-file <(env | grep -v '\r' | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') \
-v ${PWD}:/project \
-v ~/.cache/electron:/root/.cache/electron \
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
electronuserland/builder:wine
fi
- npm run build
before_cache:
- rm -rf $HOME/.cache/electron-builder/wine
branches:
except:
- "/^v\\d+\\.\\d+\\.\\d+$/"

8
.whitesource

@ -0,0 +1,8 @@
##########################################################
#### WhiteSource "Bolt for Github" configuration file ####
##########################################################
# Configuration #
#---------------#
ws.repo.scan=true
vulnerable.check.run.conclusion.level=failure

BIN
_icons/256x256.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
_icons/icon.icns

Binary file not shown.

BIN
_icons/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
_icons/iconBlack.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
_icons/iconBlackSmall.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
_icons/iconColor.icns

Binary file not shown.

BIN
_icons/iconColor.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

BIN
_icons/iconColor.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
_icons/iconColorSmall.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

BIN
_icons/iconWhite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
_icons/logoBlack.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
_icons/logoColor.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
_icons/logoWhite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

1
_icons/mejs-controls.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="120" viewBox="0 0 400 120"><style>.st0{fill:#FFFFFF;width:16px;height:16px} .st1{fill:none;stroke:#FFFFFF;stroke-width:1.5;stroke-linecap:round;} .st2{fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linecap:round;} .st3{fill:none;stroke:#FFFFFF;} .st4{fill:#231F20;} .st5{opacity:0.75;fill:none;stroke:#FFFFFF;stroke-width:5;enable-background:new;} .st6{fill:none;stroke:#FFFFFF;stroke-width:5;} .st7{opacity:0.4;fill:#FFFFFF;enable-background:new;} .st8{opacity:0.6;fill:#FFFFFF;enable-background:new;} .st9{opacity:0.8;fill:#FFFFFF;enable-background:new;} .st10{opacity:0.9;fill:#FFFFFF;enable-background:new;} .st11{opacity:0.3;fill:#FFFFFF;enable-background:new;} .st12{opacity:0.5;fill:#FFFFFF;enable-background:new;} .st13{opacity:0.7;fill:#FFFFFF;enable-background:new;}</style><path class="st0" d="M16.5 8.5c.3.1.4.5.2.8-.1.1-.1.2-.2.2l-11.4 7c-.5.3-.8.1-.8-.5V2c0-.5.4-.8.8-.5l11.4 7z"/><path class="st0" d="M24 1h2.2c.6 0 1 .4 1 1v14c0 .6-.4 1-1 1H24c-.6 0-1-.4-1-1V2c0-.5.4-1 1-1zm9.8 0H36c.6 0 1 .4 1 1v14c0 .6-.4 1-1 1h-2.2c-.6 0-1-.4-1-1V2c0-.5.4-1 1-1z"/><path class="st0" d="M81 1.4c0-.6.4-1 1-1h5.4c.6 0 .7.3.3.7l-6 6c-.4.4-.7.3-.7-.3V1.4zm0 15.8c0 .6.4 1 1 1h5.4c.6 0 .7-.3.3-.7l-6-6c-.4-.4-.7-.3-.7.3v5.4zM98.8 1.4c0-.6-.4-1-1-1h-5.4c-.6 0-.7.3-.3.7l6 6c.4.4.7.3.7-.3V1.4zm0 15.8c0 .6-.4 1-1 1h-5.4c-.6 0-.7-.3-.3-.7l6-6c.4-.4.7-.3.7.3v5.4z"/><path class="st0" d="M112.7 5c0 .6.4 1 1 1h4.1c.6 0 .7-.3.3-.7L113.4.6c-.4-.4-.7-.3-.7.3V5zm-7.1 1c.6 0 1-.4 1-1V.9c0-.6-.3-.7-.7-.3l-4.7 4.7c-.4.4-.3.7.3.7h4.1zm1 7.1c0-.6-.4-1-1-1h-4.1c-.6 0-.7.3-.3.7l4.7 4.7c.4.4.7.3.7-.3v-4.1zm7.1-1c-.6 0-1 .4-1 1v4.1c0 .5.3.7.7.3l4.7-4.7c.4-.4.3-.7-.3-.7h-4.1z"/><path class="st0" d="M67 5.8c-.5.4-1.2.6-1.8.6H62c-.6 0-1 .4-1 1v5.7c0 .6.4 1 1 1h4.2c.3.2.5.4.8.6l3.5 2.6c.4.3.8.1.8-.4V3.5c0-.5-.4-.7-.8-.4L67 5.8z"/><path class="st1" d="M73.9 2.5s3.9-.8 3.9 7.7-3.9 7.8-3.9 7.8"/><path class="st1" d="M72.6 6.4s2.6-.4 2.6 3.8-2.6 3.9-2.6 3.9"/><path class="st0" d="M47 5.8c-.5.4-1.2.6-1.8.6H42c-.6 0-1 .4-1 1v5.7c0 .6.4 1 1 1h4.2c.3.2.5.4.8.6l3.5 2.6c.4.3.8.1.8-.4V3.5c0-.5-.4-.7-.8-.4L47 5.8z"/><path class="st2" d="M52.8 7l5.4 5.4m-5.4 0L58.2 7"/><path class="st3" d="M128.7 8.6c-6.2-4.2-6.5 7.8 0 3.9m6.5-3.9c-6.2-4.2-6.5 7.8 0 3.9"/><path class="st0" d="M122.2 3.4h15.7v13.1h-15.7V3.4zM120.8 2v15.7h18.3V2h-18.3z"/><path class="st0" d="M143.2 3h14c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2h-14c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2z"/><path class="st4" d="M146.4 13.8c-.8 0-1.6-.4-2.1-1-1.1-1.4-1-3.4.1-4.8.5-.6 2-1.7 4.6.2l-.6.8c-1.4-1-2.6-1.1-3.3-.3-.8 1-.8 2.4-.1 3.5.7.9 1.9.8 3.4-.1l.5.9c-.7.5-1.6.7-2.5.8zm7.5 0c-.8 0-1.6-.4-2.1-1-1.1-1.4-1-3.4.1-4.8.5-.6 2-1.7 4.6.2l-.5.8c-1.4-1-2.6-1.1-3.3-.3-.8 1-.8 2.4-.1 3.5.7.9 1.9.8 3.4-.1l.5.9c-.8.5-1.7.7-2.6.8z"/><path class="st0" d="M60.3 77c.6.2.8.8.6 1.4-.1.3-.3.5-.6.6L30 96.5c-1 .6-1.7.1-1.7-1v-35c0-1.1.8-1.5 1.7-1L60.3 77z"/><path class="st5" d="M2.5 79c0-20.7 16.8-37.5 37.5-37.5S77.5 58.3 77.5 79 60.7 116.5 40 116.5 2.5 99.7 2.5 79z"/><path class="st0" d="M140.3 77c.6.2.8.8.6 1.4-.1.3-.3.5-.6.6L110 96.5c-1 .6-1.7.1-1.7-1v-35c0-1.1.8-1.5 1.7-1L140.3 77z"/><path class="st6" d="M82.5 79c0-20.7 16.8-37.5 37.5-37.5s37.5 16.8 37.5 37.5-16.8 37.5-37.5 37.5S82.5 99.7 82.5 79z"/><circle class="st0" cx="201.9" cy="47.1" r="8.1"/><circle class="st7" cx="233.9" cy="79" r="5"/><circle class="st8" cx="201.9" cy="110.9" r="6"/><circle class="st9" cx="170.1" cy="79" r="7"/><circle class="st10" cx="178.2" cy="56.3" r="7.5"/><circle class="st11" cx="226.3" cy="56.1" r="4.5"/><circle class="st12" cx="225.8" cy="102.8" r="5.5"/><circle class="st13" cx="178.2" cy="102.8" r="6.5"/><path class="st0" d="M178 9.4c0 .4-.4.7-.9.7-.1 0-.2 0-.2-.1L172 8.2c-.5-.2-.6-.6-.1-.8l6.2-3.6c.5-.3.8-.1.7.5l-.8 5.1z"/><path class="st0" d="M169.4 15.9c-1 0-2-.2-2.9-.7-2-1-3.2-3-3.2-5.2.1-3.4 2.9-6 6.3-6 2.5.1 4.8 1.7 5.6 4.1l.1-.1 2.1 1.1c-.6-4.4-4.7-7.5-9.1-6.9-3.9.6-6.9 3.9-7 7.9 0 2.9 1.7 5.6 4.3 7 1.2.6 2.5.9 3.8 1 2.6 0 5-1.2 6.6-3.3l-1.8-.9c-1.2 1.2-3 2-4.8 2z"/><path class="st0" d="M183.4 3.2c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5-1.5-.7-1.5-1.5c0-.9.7-1.5 1.5-1.5zm5.1 0h8.5c.9 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5h-8.5c-.9 0-1.5-.7-1.5-1.5-.1-.9.6-1.5 1.5-1.5zm-5.1 5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5-1.5-.7-1.5-1.5c0-.9.7-1.5 1.5-1.5zm5.1 0h8.5c.9 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5h-8.5c-.9 0-1.5-.7-1.5-1.5-.1-.9.6-1.5 1.5-1.5zm-5.1 5c.8 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5-1.5-.7-1.5-1.5c0-.9.7-1.5 1.5-1.5zm5.1 0h8.5c.9 0 1.5.7 1.5 1.5s-.7 1.5-1.5 1.5h-8.5c-.9 0-1.5-.7-1.5-1.5-.1-.9.6-1.5 1.5-1.5z"/></svg>

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
_icons/textBlack.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
_icons/textBlackSmall.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
_icons/textColor.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
_icons/textColorSmall.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
_icons/textWhite.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

77
_scripts/build.js

@ -0,0 +1,77 @@
const os = require('os')
const builder = require('electron-builder')
const Platform = builder.Platform
const { name, productName } = require('../package.json')
let targets
var platform = os.platform()
if (platform == 'darwin') {
targets = Platform.MAC.createTarget()
} else if (platform == 'win32') {
targets = Platform.WINDOWS.createTarget()
} else if (platform == 'linux') {
targets = Platform.LINUX.createTarget()
}
const config = {
appId: `io.freetubeapp.${name}`,
copyright: 'Copyleft ©2020 freetubeapp@protonmail.com',
// asar: false,
// compression: 'store',
// productName,
directories: {
output: './build/',
},
files: ['_icons/icon.*', './dist/**/*'],
dmg: {
contents: [
{
path: '/Applications',
type: 'link',
x: 410,
y: 230,
},
{
type: 'file',
x: 130,
y: 230,
},
],
window: {
height: 380,
width: 540,
},
},
linux: {
icon: '_icons/icon.png',
target: ['deb', 'snap', 'AppImage'],
},
mac: {
category: 'public.app-category.utilities',
icon: '_icons/icon.icns',
target: ['dmg', 'zip'],
type: 'distribution',
},
win: {
icon: '_icons/icon.ico',
target: ['nsis', 'zip', 'portable'],
},
nsis: {
allowToChangeInstallationDirectory: true,
oneClick: false,
},
}
builder
.build({
targets,
config,
})
.then(m => {
console.log(m)
})
.catch(e => {
console.error(e)
})

120
_scripts/dev-runner.js

@ -0,0 +1,120 @@
process.env.NODE_ENV = 'development'
const electron = require('electron')
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const kill = require('tree-kill')
const path = require('path')
const { spawn } = require('child_process')
const mainConfig = require('./webpack.main.config')
const rendererConfig = require('./webpack.renderer.config')
const workersConfig = require('./webpack.workers.config')
let electronProcess = null
let manualRestart = null
const remoteDebugging = !!(
process.argv[2] && process.argv[2] === '--remote-debug'
)
if (remoteDebugging) {
// disable dvtools open in electron
process.env.RENDERER_REMOTE_DEBUGGING = true
}
async function killElectron(pid) {
return new Promise((resolve, reject) => {
if (pid) {
kill(pid, err => {
if (err) reject(err)
resolve()
})
} else {
resolve()
}
})
}
async function restartElectron() {
console.log('\nStarting electron...')
const { pid } = electronProcess || {}
await killElectron(pid)
electronProcess = spawn(electron, [
path.join(__dirname, '../dist/main.js'),
// '--enable-logging', Enable to show logs from all electron processes
remoteDebugging ? '--inspect=9222' : '',
remoteDebugging ? '--remote-debugging-port=9223' : '',
])
electronProcess.on('exit', (code, signal) => {
if (!manualRestart) process.exit(0)
})
}
function startMain() {
const webpackSetup = webpack([mainConfig, workersConfig])
webpackSetup.compilers.forEach(compiler => {
const { name } = compiler
switch (name) {
case 'workers':
compiler.hooks.afterEmit.tap('afterEmit', async () => {
console.log(`\nCompiled ${name} script!`)
console.log(`\nWatching file changes for ${name} script...`)
})
break
case 'main':
default:
compiler.hooks.afterEmit.tap('afterEmit', async () => {
console.log(`\nCompiled ${name} script!`)
manualRestart = true
await restartElectron()
setTimeout(() => {
manualRestart = false
}, 2500)
console.log(`\nWatching file changes for ${name} script...`)
})
break
}
})
webpackSetup.watch({
aggregateTimeout: 500,
},
err => {
if (err) console.error(err)
})
}
function startRenderer(callback) {
const compiler = webpack(rendererConfig)
const { name } = compiler
compiler.hooks.afterEmit.tap('afterEmit', () => {
console.log(`\nCompiled ${name} script!`)
console.log(`\nWatching file changes for ${name} script...`)
})
const server = new WebpackDevServer(compiler, {
contentBase: path.join(__dirname, '../'),
hot: true,
overlay: true,
clientLogLevel: 'warning'
})
server.listen(9080, '', err => {
if (err) console.error(err)
callback()
})
}
startRenderer(startMain)

75
_scripts/webpack.main.config.js

@ -0,0 +1,75 @@
const path = require('path')
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const {
dependencies,
devDependencies,
productName,
} = require('../package.json')
const externals = Object.keys(dependencies).concat(Object.keys(devDependencies))
const isDevMode = process.env.NODE_ENV === 'development'
const whiteListedModules = []
const config = {
name: 'main',
mode: process.env.NODE_ENV,
devtool: isDevMode ? 'eval' : false,
entry: {
main: path.join(__dirname, '../src/main/index.js'),
},
externals: externals.filter(d => !whiteListedModules.includes(d)),
module: {
rules: [
{
test: /\.(j|t)s$/,
loader: ['babel-loader'],
exclude: /node_modules/,
},
{
test: /\.node$/,
use: 'node-loader',
},
],
},
node: {
__dirname: isDevMode,
__filename: isDevMode,
},
plugins: [
new webpack.DefinePlugin({
'process.env.PRODUCT_NAME': JSON.stringify(productName),
}),
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist'),
},
resolve: {
extensions: ['.ts', '.js', '.json'],
alias: {
'@': path.join(__dirname, '../src/'),
src: path.join(__dirname, '../src/'),
},
},
target: 'electron-main',
}
if (!isDevMode) {
config.plugins.push(
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../src/data'),
to: path.join(__dirname, '../dist/data'),
},
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/static'),
},
])
)
}
module.exports = config

165
_scripts/webpack.renderer.config.js

@ -0,0 +1,165 @@
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const {
dependencies,
devDependencies,
productName,
} = require('../package.json')
const externals = Object.keys(dependencies).concat(Object.keys(devDependencies))
const isDevMode = process.env.NODE_ENV === 'development'
const whiteListedModules = ['vue']
const config = {
name: 'renderer',
mode: process.env.NODE_ENV,
devtool: isDevMode ? 'eval' : false,
entry: {
renderer: path.join(__dirname, '../src/renderer/main.js'),
},
output: {
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist'),
filename: '[name].js',
},
externals: externals.filter(d => !whiteListedModules.includes(d)),
module: {
rules: [
{
test: /\.(j|t)s$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.node$/,
use: 'node-loader',
},
{
test: /\.vue$/,
loader: 'vue-loader',
// use: {
// loader: 'vue-loader',
// options: {
// loaders: {
// sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax',
// },
// },
// },
},
{
test: /\.s(c|a)ss$/,
use: [
// {
// loader: 'vue-style-loader',
// },
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: isDevMode,
},
},
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
options: {
// eslint-disable-next-line
implementation: require('sass'),
},
},
],
},
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
hmr: isDevMode,
},
},
// 'style-loader',
'css-loader',
],
},
{
test: /\.(png|jpe?g|gif|tif?f|bmp|webp|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'imgs/[name]--[folder].[ext]',
},
},
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
query: {
limit: 10000,
name: 'fonts/[name]--[folder].[ext]',
},
},
},
],
},
node: {
__dirname: isDevMode,
__filename: isDevMode,
},
plugins: [
// new WriteFilePlugin(),
new HtmlWebpackPlugin({
excludeChunks: ['processTaskWorker'],
filename: 'index.html',
template: path.resolve(__dirname, '../src/index.ejs'),
nodeModules: isDevMode
? path.resolve(__dirname, '../node_modules')
: false,
}),
new VueLoaderPlugin(),
new webpack.DefinePlugin({
'process.env.PRODUCT_NAME': JSON.stringify(productName),
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
],
resolve: {
alias: {
vue$: 'vue/dist/vue.common.js',
'@': path.join(__dirname, '../src/'),
src: path.join(__dirname, '../src/'),
icons: path.join(__dirname, '../_icons/'),
},
extensions: ['.ts', '.js', '.vue', '.json'],
},
target: 'electron-renderer',
}
/**
* Adjust rendererConfig for production settings
*/
if (isDevMode) {
// any dev only config
config.plugins.push(new webpack.HotModuleReplacementPlugin())
} else {
config.plugins.push(
new CopyWebpackPlugin([
{
from: path.join(__dirname, '../static'),
to: path.join(__dirname, '../dist/static'),
},
])
)
}
module.exports = config

67
_scripts/webpack.workers.config.js

@ -0,0 +1,67 @@
const path = require('path')
const webpack = require('webpack')
const {
dependencies,
devDependencies,
productName,
} = require('../package.json')
const externals = Object.keys(dependencies).concat(Object.keys(devDependencies))
const isDevMode = process.env.NODE_ENV === 'development'
const config = {
name: 'workers',
mode: process.env.NODE_ENV,
devtool: isDevMode ? 'eval' : false,
entry: {
workerSample: path.join(__dirname, '../src/utilities/workerSample.ts'),
},
output: {
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../dist'),
filename: '[name].js',
},
externals: externals,
module: {
rules: [
{
test: /\.(j|t)s$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.node$/,
use: 'node-loader',
},
],
},
node: {
__dirname: isDevMode,
__filename: isDevMode,
},
plugins: [
// new WriteFilePlugin(),
new webpack.DefinePlugin({
'process.env.PRODUCT_NAME': JSON.stringify(productName),
}),
],
resolve: {
alias: {
'@': path.join(__dirname, '../src/'),
src: path.join(__dirname, '../src/'),
},
extensions: ['.ts', '.js', '.json'],
},
target: 'node',
}
/**
* Adjust rendererConfig for production settings
*/
if (isDevMode) {
// any dev only config
config.plugins.push(new webpack.HotModuleReplacementPlugin())
}
module.exports = config

20
appveyor.yml

@ -0,0 +1,20 @@
image: Visual Studio 2017
platform:
- x64
cache:
- node_modules
- '%USERPROFILE%\.electron'
init:
- git config --global core.autocrlf input
install:
- ps: Install-Product node 12 x64
- npm install
build_script:
- npm run build
test: off

15692
package-lock.json

File diff suppressed because it is too large

124
package.json

@ -0,0 +1,124 @@
{
"author": {
"name": "PrestonN",
"email": "FreeTubeApp@protonmail.com",
"url": "https://github.com/FreeTubeApp/FreeTube"
},
"bugs": {
"url": "https://github.com/FreeTubeApp/FreeTube/issues"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.27",
"@fortawesome/free-solid-svg-icons": "^5.12.1",
"@fortawesome/vue-fontawesome": "^0.1.9",
"autolinker": "^3.11.1",
"bulma-pro": "^0.1.8",
"dateformat": "^3.0.3",
"electron-context-menu": "^0.16.0",
"jquery": "^3.4.1",
"lodash.isequal": "^4.5.0",
"material-design-icons": "^3.0.1",
"mediaelement": "^4.2.14",
"nedb": "^1.8.0",
"opml-to-json": "0.0.3",
"video.js": "^7.6.6",
"videojs-replay": "^1.1.0",
"vue": "^2.6.11",
"vue-electron": "^1.0.6",
"vue-router": "^3.1.5",
"vuex": "^3.1.2",
"xml2json": "^0.12.0",
"youtube-chat": "^1.0.2",
"youtube-comments-fetch": "^1.0.1",
"youtube-comments-task": "^1.3.14",
"youtube-suggest": "^1.1.0",
"ytdl-core": "^1.0.7",
"ytpl": "^0.1.20",
"ytsr": "^0.1.10"
},
"description": "A private YouTube client",
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-proposal-object-rest-spread": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-typescript": "^7.8.3",
"@typescript-eslint/eslint-plugin": "^2.19.0",
"@typescript-eslint/parser": "^2.19.0",
"acorn": "^7.1.0",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2",
"devtron": "^1.4.0",
"electron": "^8.0.0",
"electron-builder": "^22.3.2",
"electron-debug": "^3.0.1",
"electron-rebuild": "^1.10.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"eslint-plugin-vue": "^6.1.2",
"fast-glob": "^3.1.1",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-loader": "^0.6.0",
"npm-run-all": "^4.1.5",
"prettier": "^1.19.1",
"sass": "^1.25.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.1.3",
"tree-kill": "1.2.2",
"typescript": "^3.7.5",
"url-loader": "^3.0.0",
"vue-devtools": "^5.1.3",
"vue-eslint-parser": "^7.0.0",
"vue-loader": "^15.8.3",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.3"
},
"license": "GPL-3.0-or-later",
"main": "./dist/main.js",
"name": "freetube",
"private": true,
"productName": "FreeTube",
"repository": {
"type": "git",
"url": "git+https://github.com/mubaidr/vue-electron-template.git"
},
"scripts": {
"build": "run-s rebuild:electron pack build-release",
"build-release": "node _scripts/build.js",
"debug": "run-s rebuild:electron debug-runner",
"debug-runner": "node _scripts/dev-runner.js --remote-debug",
"dev": "run-s rebuild:electron dev-runner",
"dev-runner": "node _scripts/dev-runner.js",
"electron-builder-install": "electron-builder install-app-deps",
"electron-rebuild": "electron-rebuild",
"jest": "jest",
"jest:coverage": "jest --collect-coverage",
"jest:watch": "jest --watch",
"lint": "eslint --fix --ext .js,.ts,.vue ./",
"pack": "run-p pack:main pack:renderer 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",
"pack:workers": "webpack --mode=production --env.NODE_ENV=production --hide-modules --config _scripts/webpack.workers.config.js",
"postinstall": "electron-rebuild",
"prettier": "prettier --write \"{src,_scripts}/**/*.{js,ts,vue}\"",
"rebuild:electron": "run-s electron-builder-install electron-rebuild",
"rebuild:node": "npm rebuild",
"release": "run-s test build",
"test": "run-s rebuild:node pack:workers jest",
"test:watch": "run-s rebuild:node pack:workers jest:watch"
},
"version": "0.8.0"
}

0
src/data/.gitkeep

30
src/index.ejs

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<title></title>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
<script>
require('module').globalPaths.push(
`<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>`
)
</script>
<% } %>
</head>
<body class="redLight">
<div id="app"></div>
<!-- Set `__static` path to static files in production -->
<script>
if (process.env.NODE_ENV !== 'development')
window.__static = require('path')
.join(__dirname, '/static')
.replace(/\\/g, '\\\\')
</script>
<!-- webpack builds are automatically injected -->
</body>
</html>

247
src/main/index.js

@ -0,0 +1,247 @@
import { app, BrowserWindow, Menu } from 'electron'
import { productName } from '../../package.json'
// set app name
app.setName(productName)
// disable electron warning
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true'
const gotTheLock = app.requestSingleInstanceLock()
const isDev = process.env.NODE_ENV === 'development'
const isDebug = process.argv.includes('--debug')
let mainWindow
// 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')
})
}
async function installDevTools () {
try {
/* eslint-disable */
require('devtron').install()
require('vue-devtools').install()
/* eslint-enable */
} catch (err) {
console.log(err)
}
}
function createWindow () {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
backgroundColor: '#fff',
width: 960,
height: 540,
minWidth: 960,
minHeight: 540,
// useContentSize: true,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: false,
webSecurity: false
},
show: false
})
// eslint-disable-next-line
setMenu()
// load root file/url
if (isDev) {
mainWindow.loadURL('http://localhost:9080')
} else {
mainWindow.loadFile(`${__dirname}/index.html`)
global.__static = require('path')
.join(__dirname, '/static')
.replace(/\\/g, '\\\\')
}
// Show when loaded
mainWindow.on('ready-to-show', () => {
mainWindow.show()
mainWindow.focus()
})
mainWindow.on('closed', () => {
console.log('closed')
})
}
app.on('ready', () => {
createWindow()
if (isDev) {
installDevTools()
}
if (isDebug) {
mainWindow.webContents.openDevTools()
}
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (mainWindow === null) {
createWindow()
}
})
/**
* Auto Updater
*
* Uncomment the following code below and install `electron-updater` to
* support auto updating. Code Signing with a valid certificate is required.
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
*/
/*
import { autoUpdater } from 'electron-updater'
autoUpdater.on('update-downloaded', () => {
autoUpdater.quitAndInstall()
})
app.on('ready', () => {
if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates()
})
*/
/* eslint-disable-next-line */
const sendMenuEvent = async data => {
mainWindow.webContents.send('change-view', data)
}
const template = [{
label: 'File',
submenu: [
{
role: 'quit'
}
]
},
{
label: 'Edit',
submenu: [{
role: 'cut'
},
{
role: 'copy',
accelerator: 'CmdOrCtrl+C',
selector: 'copy:'
},
{
role: 'paste',
accelerator: 'CmdOrCtrl+V',
selector: 'paste:'
},
{
role: 'pasteandmatchstyle'
},
{
role: 'delete'
},
{
role: 'selectall'
}
]
},
{
label: 'View',
submenu: [{
role: 'reload'
},
{
role: 'forcereload',
accelerator: 'CmdOrCtrl+Shift+R'
},
{
role: 'toggledevtools'
},
{
type: 'separator'
},
{
role: 'resetzoom'
},
{
role: 'zoomin'
},
{
role: 'zoomout'
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
}
]
},
{
role: 'window',
submenu: [{
role: 'minimize'
},
{
role: 'close'
}
]
}
]
function setMenu () {
if (process.platform === 'darwin') {
template.unshift({
label: app.getName(),
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideothers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
})
template.push({
role: 'window'
})
template.push({
role: 'help'
})
template.push({ role: 'services' })
}
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
}

24
src/renderer/App.css

@ -0,0 +1,24 @@
@font-face {
font-family: Roboto;
src: url(assets/font/Roboto-Regular.ttf);
}
body {
min-height: 100vh;
}
#app {
font-family: 'Roboto', sans-serif;
}
.routerView {
margin-left: 200px;
margin-top: 80px;
transition-property: margin;
transition-duration: 150ms;
transition-timing-function: ease-in-out;
}
.expand {
margin-left: 80px;
}

29
src/renderer/App.js

@ -0,0 +1,29 @@
import Vue from 'vue'
import TopNav from './components/top-nav/top-nav.vue'
import SideNav from './components/side-nav/side-nav.vue'
import $ from 'jquery'
import { shell } from 'electron'
export default Vue.extend({
name: 'App',
components: {
TopNav,
SideNav
},
computed: {
isOpen: function () {
return this.$store.getters.getIsSideNavOpen
}
},
mounted: function () {
// Open links externally by default
$(document).on('click', 'a[href^="http"]', (event) => {
const el = event.currentTarget
console.log(el)
if (typeof (shell) !== 'undefined') {
event.preventDefault()
shell.openExternal(el.href)
}
})
}
})

24
src/renderer/App.vue

@ -0,0 +1,24 @@
<template>
<div id="app">
<top-nav ref="topNav" />
<side-nav ref="sideNav" />
<Transition
mode="out-in"
name="slide-up"
>
<!-- <keep-alive> -->
<RouterView
ref="router"
class="routerView"
:class="{ expand: !isOpen }"
/>
<!-- </keep-alive> -->
</Transition>
</div>
</template>
<script src="./App.js" />
<style src="./themes.css" />
<style src="./videoJS.css" />
<style scoped src="./App.css" />

BIN
src/renderer/assets/font/Roboto-Regular.ttf

Binary file not shown.

BIN
src/renderer/assets/font/VideoJS.woff

Binary file not shown.

50
src/renderer/components/ft-button/ft-button.css

@ -0,0 +1,50 @@
.btn {
font-family: 'Roboto', sans-serif;
min-width: 100px;
font-size: 0.9rem;
padding: 10px 20px;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
align-items: center;
justify-content: center;
text-align: center;
text-decoration: none;
transition: 0.3s;
border-radius: 4px;
outline: none;
white-space: nowrap;
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);
}
.ripple {
position: relative;
overflow: hidden;
transform: translate3d(0, 0, 0);
}
.ripple:after {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
background-image: radial-gradient(circle, #fff 10%, transparent 10.01%);
background-repeat: no-repeat;
background-position: 50%;
transform: scale(10, 10);
opacity: 0;
transition: transform .5s, opacity 1s;
}
.ripple:active:after {
transform: scale(0, 0);
opacity: .3;
transition: 0s;
}

29
src/renderer/components/ft-button/ft-button.js

@ -0,0 +1,29 @@
import Vue from 'vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtListVideo from '../ft-list-video/ft-list-video.vue'
import FtListChannel from '../ft-list-channel/ft-list-channel.vue'
import FtListPlaylist from '../ft-list-playlist/ft-list-playlist.vue'
export default Vue.extend({
name: 'FtElementList',
components: {
'ft-flex-box': FtFlexBox,
'ft-list-video': FtListVideo,
'ft-list-channel': FtListChannel,
'ft-list-playlist': FtListPlaylist
},
props: {
label: {
type: String,
required: true
},
textColor: {
type: String,
default: '#FFFFFF'
},
backgroundColor: {
type: String,
default: '#2196F3'
}
}
})

16
src/renderer/components/ft-button/ft-button.vue

@ -0,0 +1,16 @@
<template>
<button
class="btn ripple"
:style="{
color: textColor,
backgroundColor: backgroundColor,
border: `2px solid ${backgroundColor}`
}"
@click="$emit('click')"
>
{{ label }}
</button>
</template>
<script src="./ft-button.js" />
<style scoped src="./ft-button.css" />

8
src/renderer/components/ft-card/ft-card.css

@ -0,0 +1,8 @@
.ft-card {
background-color: var(--card-bg-color);
margin: 8px;
padding: 16px;
padding-top: 3px;
padding-bottom: 16px;
box-shadow: 0 1px 2px rgba(0,0,0,.1);
}

5
src/renderer/components/ft-card/ft-card.js

@ -0,0 +1,5 @@
import Vue from 'vue'
export default Vue.extend({
name: 'FtCard'
})

8
src/renderer/components/ft-card/ft-card.vue

@ -0,0 +1,8 @@
<template>
<div class="ft-card">
<slot />
</div>
</template>
<script src="./ft-card.js" />
<style scoped src="./ft-card.css" />

33
src/renderer/components/ft-channel-bubble/ft-channel-bubble.css

@ -0,0 +1,33 @@
.bubblePadding {
width: 100px;
height: 115px;
padding: 10px;
cursor: pointer;
-webkit-transition: background 0.2s ease-out;
-moz-transition: background 0.2s ease-out;
-o-transition: background 0.2s ease-out;
transition: background 0.2s ease-out;
}
.bubblePadding: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;
}
.bubble {
width: 50px;
height: 50px;
margin-bottom: 5px;
margin-left: 25px;
border-radius: 200px 200px 200px 200px;
-webkit-border-radius: 200px 200px 200px 200px;
}
.channelName {
font-size: 13px;
height: 60px;
overflow: hidden;
text-align: center;
}

24
src/renderer/components/ft-channel-bubble/ft-channel-bubble.js

@ -0,0 +1,24 @@
import Vue from 'vue'
export default Vue.extend({
name: 'FtChannelBubble',
props: {
channelName: {
type: String,
required: true
},
channelId: {
type: String,
required: true
},
channelThumbnail: {
type: String,
required: true
}
},
methods: {
goToChannel: function () {
console.log('Go to channel')
}
}
})