Initial vuecli conversion. Still much broken.

This commit is contained in:
Travis Shivers 2020-05-25 17:12:22 -05:00
parent 19b47eaa7c
commit 4d456c037e
85 changed files with 8926 additions and 8477 deletions

View File

@ -1,16 +0,0 @@
{
"presets": [
[ "env", { "modules": false } ],
"stage-2",
"es2015",
"es2016"
],
"plugins": ["transform-runtime"],
"comments": false,
"env": {
"test": {
"presets": [ "env", "stage-2" ],
"plugins": [ "istanbul" ]
}
}
}

3
.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not dead

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100

View File

@ -1,50 +1,15 @@
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
node: true
},
extends: ["plugin:vue/essential", "eslint:recommended"],
parserOptions: {
parser: "babel-eslint"
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
'airbnb-base',
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'vue/html-closing-bracket-newline': ['error', {
singleline: 'never',
multiline: 'always',
}],
'vue/html-closing-bracket-spacing': ['error', {
startTag: 'never',
endTag: 'never',
selfClosingTag: 'always',
}],
'import/extensions': ['error', 'always', {
js: 'never',
json: 'never',
vue: 'never',
}],
'no-param-reassign': ['error', {
props: true,
ignorePropertyModificationsFor: [
'state',
],
}],
'no-plusplus': 'off',
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
//"import/no-extraneous-dependencies": ["error", { devDependencies: true }]
}
}
};

4
.gitignore vendored
View File

@ -10,3 +10,7 @@ ptinvites.json
db.sqlite
.tmp
.vscode
# local env files
.env.local
.env.*.local

View File

@ -1,5 +0,0 @@
{
"include": [
"./src/**/*"
]
}

View File

@ -1,8 +0,0 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
// to edit target browsers: use "browserlist" field in package.json
"autoprefixer": {}
}
}

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
};

View File

@ -1,35 +0,0 @@
require('./check-versions')()
process.env.NODE_ENV = 'production'
var ora = require('ora')
var rm = require('rimraf')
var path = require('path')
var chalk = require('chalk')
var webpack = require('webpack')
var config = require('../config')
var webpackConfig = require('./webpack.prod.conf')
var spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, function (err, stats) {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n')
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

View File

@ -1,48 +0,0 @@
var chalk = require('chalk')
var semver = require('semver')
var packageConfig = require('../package.json')
var shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
var versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
},
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
var warnings = []
for (var i = 0; i < versionRequirements.length; i++) {
var mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (var i = 0; i < warnings.length; i++) {
var warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

View File

@ -1,9 +0,0 @@
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})

View File

@ -1,89 +0,0 @@
require('./check-versions')()
var config = require('../config')
if (!process.env.NODE_ENV) {
process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
}
var opn = require('opn')
var path = require('path')
var express = require('express')
var webpack = require('webpack')
var proxyMiddleware = require('http-proxy-middleware')
var webpackConfig = require('./webpack.dev.conf')
// default port where dev server listens for incoming traffic
var port = process.env.PORT || config.dev.port
// automatically open browser, if not set will be false
var autoOpenBrowser = !!config.dev.autoOpenBrowser
// Define HTTP proxies to your custom API backend
// https://github.com/chimurai/http-proxy-middleware
var proxyTable = config.dev.proxyTable
var app = express()
var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
quiet: true
})
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: () => {}
})
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' })
cb()
})
})
// proxy api requests
Object.keys(proxyTable).forEach(function (context) {
var options = proxyTable[context]
if (typeof options === 'string') {
options = { target: options }
}
app.use(proxyMiddleware(options.filter || context, options))
})
// handle fallback for HTML5 history API
app.use(require('connect-history-api-fallback')())
// serve webpack bundle output
app.use(devMiddleware)
// enable hot-reload and state-preserving
// compilation error display
app.use(hotMiddleware)
// serve pure static assets
var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
app.use(staticPath, express.static('./static'))
var uri = 'http://localhost:' + port
var _resolve
var readyPromise = new Promise(resolve => {
_resolve = resolve
})
console.log('> Starting dev server...')
devMiddleware.waitUntilValid(() => {
console.log('> Listening at ' + uri + '\n')
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri)
}
_resolve()
})
var server = app.listen(port)
module.exports = {
ready: readyPromise,
close: () => {
server.close()
}
}

View File

@ -1,71 +0,0 @@
var path = require('path')
var config = require('../config')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
exports.assetsPath = function (_path) {
var assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
var output = []
var loaders = exports.cssLoaders(options)
for (var extension in loaders) {
var loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}

View File

@ -1,12 +0,0 @@
var utils = require('./utils')
var config = require('../config')
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
loaders: utils.cssLoaders({
sourceMap: isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap,
extract: isProduction
})
}

View File

@ -1,68 +0,0 @@
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.styl$/,
loader: ['style-loader', 'css-loader', 'stylus-loader']
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
console: true,
fs: 'empty',
net: 'empty',
tls: 'empty'
}
}

View File

@ -1,35 +0,0 @@
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
// add hot-reload related code to entry chunks
Object.keys(baseWebpackConfig.entry).forEach(function (name) {
baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
})
module.exports = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
},
// cheap-module-eval-source-map is faster for development
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': config.dev.env
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
new FriendlyErrorsPlugin()
]
})

View File

@ -1,125 +0,0 @@
var path = require('path')
var utils = require('./utils')
var webpack = require('webpack')
var config = require('../config')
var merge = require('webpack-merge')
var baseWebpackConfig = require('./webpack.base.conf')
var CopyWebpackPlugin = require('copy-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
var env = config.build.env
var webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true
})
},
devtool: config.build.productionSourceMap ? '#source-map' : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new webpack.DefinePlugin({
'typeof global': JSON.stringify('undefined')
}),
new UglifyJsPlugin({
}),
// new webpack.optimize.UglifyJsPlugin({
// compress: true,
// sourceMap: false
// }),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css')
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: {
safe: true
}
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function (module, count) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
var CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

@ -1,9 +0,0 @@
var merge = require('webpack-merge')
var prodEnv = require('./prod.env')
var git = require('git-rev-sync')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
gitHash: '"' + git.short() + '"',
gitDate: '"' + git.date() + '"'
})

View File

@ -1,41 +0,0 @@
// see http://vuejs-templates.github.io/webpack for documentation.
var path = require('path')
let SettingsHelper = require('../SettingsHelper.js')
let settings = new SettingsHelper()
module.exports = {
build: {
env: require('./prod.env'),
index: path.resolve(__dirname, '../dist/index.html'),
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: '',
assetsPublicPath: settings.webroot || '/',
productionSourceMap: true,
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
},
dev: {
env: require('./dev.env'),
port: 8080,
autoOpenBrowser: true,
assetsSubDirectory: '',
assetsPublicPath: settings.webroot || '/',
proxyTable: {},
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
}
}

View File

@ -1,17 +0,0 @@
const git = require('git-rev-sync');
const settings = new (require('../SettingsHelper'))();
console.log('Production settings', settings);
if (process.env.API_OVERRIDE) {
console.log('Building with API_OVERRIDE', process.env.API_OVERRIDE);
}
module.exports = {
NODE_ENV: '"production"',
gitHash: `"${git.short()}"`,
gitDate: `"${git.date()}"`,
webroot: `"${settings.webroot}"`,
API_OVERRIDE: `"${process.env.API_OVERRIDE}"` || undefined,
};

View File

@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="shortcut icon" href="favicon.png" type="image/x-icon">
<link rel="icon" href="favicon.png" type="image/x-icon">
<title>SyncLounge</title>
<link href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' rel="stylesheet"
type="text/css">
<link href='https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' rel="stylesheet"
type="text/css">
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

15536
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,106 +1,58 @@
{
"name": "SyncLounge",
"version": "2.0.0",
"description": "Synced playback of Plex content",
"author": "samcm",
"private": false,
"author": "samcm",
"repository": {
"type": "git",
"url": "https://github.com/samcm/synclounge.git"
},
"bugs": {
"url": "https://github.com/samcm/synclounge/issues"
},
"license": "MIT",
"homepage": "https://synclounge.tv/",
"scripts": {
"dev": "node build/dev-server.js",
"start": "npm run build; node webapp.js",
"server": "node server.js",
"build": "node build/build.js"
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"args-parser": "^1.1.0",
"axios": "^0.18.1",
"babel-eslint": "^8.2.2",
"body-parser": "^1.19.0",
"cors": "^2.8.3",
"express": "^4.16.3",
"fscreen": "^1.0.2",
"git-rev-sync": "^1.12.0",
"jsonfile": "^4.0.0",
"moment": "^2.18.1",
"sails-disk": "^1.1.0",
"socket.io": "^2.0.3",
"string-similarity": "^1.2.0",
"videojs-contrib-hls": "^5.15.0",
"vue": "^2.6.10",
"vue-cookies": "^1.7.0",
"vue-router": "^2.3.1",
"vuetify": "^1.5.14",
"vuex": "^2.3.1",
"waterline": "^0.13.6",
"waterline-mysql": "^0.6.0"
"core-js": "^3.6.5",
"vue": "^2.6.11",
"vue-router": "^3.2.0",
"vuetify": "^2.2.11",
"vuex": "^3.4.0"
},
"devDependencies": {
"autoprefixer": "^6.7.2",
"babel-core": "^6.26.3",
"babel-loader": "^6.2.10",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2016": "^6.24.1",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^1.1.3",
"clipboard": "^1.7.1",
"connect-history-api-fallback": "^1.3.0",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.19.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-config-standard": "^11.0.0",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-node": "^6.0.1",
"eslint-plugin-promise": "^3.7.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue": "^4.3.0",
"eventsource-polyfill": "^0.9.6",
"extract-text-webpack-plugin": "^2.0.0",
"file-loader": "^0.11.1",
"friendly-errors-webpack-plugin": "^1.1.3",
"html-webpack-plugin": "^2.28.0",
"http-proxy-middleware": "^0.19.1",
"humanize-duration": "^3.10.0",
"opn": "^4.0.2",
"optimize-css-assets-webpack-plugin": "^1.3.0",
"ora": "^1.2.0",
"prettier-eslint": "^8.7.0",
"prettier-eslint-cli": "^4.7.0",
"request": "^2.88.0",
"rimraf": "^2.6.0",
"@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-eslint": "~4.4.0",
"@vue/cli-plugin-router": "~4.4.0",
"@vue/cli-plugin-vuex": "~4.4.0",
"@vue/cli-service": "~4.4.0",
"@vue/eslint-config-airbnb": "^5.0.2",
"args-parser": "^1.1.0",
"axios": "^0.19.2",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-vue": "^6.2.2",
"fscreen": "^1.0.2",
"humanize-duration": "^3.23.0",
"moment": "^2.26.0",
"safe-json-parse": "^4.0.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"socket.io-client": "^2.0.2",
"stylus": "^0.54.5",
"stylus-loader": "^3.0.1",
"uglifyjs-webpack-plugin": "^1.2.7",
"url-loader": "^0.5.8",
"vanilla-tilt": "^1.4.0",
"vue-clipboards": "^1.0.2",
"vue-loader": "^11.3.4",
"vue-observe-visibility": "^0.1.3",
"vue-resource": "^1.3.4",
"vue-scrollto": "^2.6.10",
"vue-style-loader": "^2.0.5",
"vue-template-compiler": "^2.6.10",
"vue-video-player": "^3.1.1",
"webpack": "^2.3.3",
"webpack-bundle-analyzer": "^2.2.1",
"webpack-dev-middleware": "^1.10.0",
"webpack-hot-middleware": "^2.18.0",
"webpack-merge": "^4.1.0",
"xml2js": "^0.4.17"
},
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
"sass": "^1.26.5",
"sass-loader": "^8.0.2",
"socket.io-client": "^2.3.0",
"string-similarity": "^4.0.1",
"vue-cli-plugin-vuetify": "~2.0.5",
"vue-clipboard2": "^0.3.1",
"vue-cookies": "^1.7.0",
"vue-observe-visibility": "^0.4.6",
"vue-scrollto": "^2.18.1",
"vue-template-compiler": "^2.6.11",
"vue-video-player": "^5.0.2",
"vuetify-loader": "^1.3.0",
"xml2js": "^0.4.23"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

23
public/index.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 671 B

After

Width:  |  Height:  |  Size: 671 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 857 B

After

Width:  |  Height:  |  Size: 857 B

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,8 +1,6 @@
<template>
<v-app dark style="height:100%">
<v-navigation-drawer app temporary style="padding: 0" v-model="drawer" disable-route-watcher>
<leftsidebar></leftsidebar>
</v-navigation-drawer>
<leftsidebar :drawer="drawer"></leftsidebar>
<v-navigation-drawer
v-if="showRightDrawerButton"
style="padding: 0; z-index: 6"
@ -15,15 +13,8 @@
<drawerright></drawerright>
</v-navigation-drawer>
<v-toolbar
app
fixed
scroll-off-screen
:scroll-threshold="1"
:manual-scroll="appIsFullscreen"
style="z-index: 5"
>
<v-toolbar-side-icon @click="drawer = !drawer"></v-toolbar-side-icon>
<v-app-bar fixed scroll-off-screen :scroll-threshold="1" style="z-index: 5">
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
<a href="https://synclounge.tv" target="_blank">
<img
class="ma-1 hidden-xs-only"
@ -54,20 +45,20 @@
small
tag="a"
class="hidden-sm-and-down"
flat
text
v-for="item in links"
:key="item.title"
:href="item.href"
:target="item.target"
>{{ item.title }}</v-btn>
<v-btn small tag="a" class="hidden-sm-and-down" flat @click="donateDialog = true">Donate </v-btn>
<v-btn small tag="a" class="hidden-sm-and-down" text @click="donateDialog = true">Donate </v-btn>
<v-icon
v-if="showRightDrawerButton"
@click="toggleDrawerRight"
class="clickable"
>{{ drawerRight ? 'last_page' : 'first_page' }}</v-icon>
</v-toolbar-items>
</v-toolbar>
</v-app-bar>
<v-content v-bind:style="mainStyle" app>
<v-container
class="ma-0 pa-0"
@ -77,16 +68,11 @@
fluid
>
<v-flex xs12 v-if="configError">
<v-alert
:dismissible="true"
:value="configError"
type="error"
class="mt-0"
>{{ configError }}</v-alert>
<v-alert :dismissible="true" type="error" class="mt-0">{{ configError }}</v-alert>
</v-flex>
<v-flex xs12 v-if="(loading || (plex && !plex.gotDevices)) && route.protected">
<v-container fill-height>
<v-layout justify-center align-center wrap row class="pt-4 text-xs-center">
<v-layout justify-center align-center wrap row class="pt-4 text-center">
<v-flex xs8 md4>
<v-progress-circular indeterminate v-bind:size="60" class="amber--text"></v-progress-circular>
</v-flex>
@ -123,19 +109,18 @@
<script>
// Custom css
import './assets/css/style.css';
import "./assets/css/style.css";
import fscreen from 'fscreen';
import fscreen from "fscreen";
import drawerright from './sidebar';
import leftsidebar from './leftsidebar';
import upnext from './upnext';
import nowplayingchip from './nowplayingchip';
import donate from './donate';
import { mapActions, mapState } from "vuex";
import drawerright from "./sidebar.vue";
import leftsidebar from "./leftsidebar.vue";
import upnext from "./upnext.vue";
import nowplayingchip from "./nowplayingchip.vue";
import donate from "./donate.vue";
import { mapActions, mapState } from 'vuex';
const SettingsHelper = require('../SettingsHelper');
const SettingsHelper = require("../SettingsHelper");
const settings = new SettingsHelper();
@ -145,7 +130,7 @@ export default {
upnext,
nowplayingchip,
leftsidebar,
donate,
donate
},
data() {
return {
@ -165,38 +150,38 @@ export default {
items: [
{
title: 'Preferences',
title: "Preferences"
},
{
title: 'Signout',
},
title: "Signout"
}
],
links: [
{
title: 'Github',
href: 'https://github.com/samcm/SyncLounge',
target: '_blank',
title: "Github",
href: "https://github.com/samcm/SyncLounge",
target: "_blank"
},
{
title: 'Discord',
target: '_blank',
href: 'https://discord.gg/fKQB3yt',
},
title: "Discord",
target: "_blank",
href: "https://discord.gg/fKQB3yt"
}
],
appIsFullscreen: false,
appIsFullscreen: false
};
},
methods: {
...mapActions('config', ['fetchConfig']),
...mapActions("config", ["fetchConfig"]),
sendNotification() {
window.EventBus.$emit('notification', 'Copied to clipboard');
window.EventBus.$emit("notification", "Copied to clipboard");
},
toggleDrawerRight() {
this.drawerRight = !this.drawerRight;
},
goFullscreen() {
fscreen.requestFullscreen(document.body);
},
}
},
async mounted() {
try {
@ -210,102 +195,106 @@ export default {
//
// Set AutoJoin information in order of importance: query -> config -> settings
if (this.$route.query.autojoin) {
this.$store.commit('SET_AUTOJOIN', true);
this.$store.commit('SET_AUTOJOINROOM', this.$route.query.room);
this.$store.commit('SET_AUTOJOINURL', this.$route.query.server);
this.$store.commit('SET_VALUE', [
'autoJoinOwner',
this.$route.query.owner,
this.$store.commit("SET_AUTOJOIN", true);
this.$store.commit("SET_AUTOJOINROOM", this.$route.query.room);
this.$store.commit("SET_AUTOJOINURL", this.$route.query.server);
this.$store.commit("SET_VALUE", [
"autoJoinOwner",
this.$route.query.owner
]);
if (this.$route.query.password) {
this.$store.commit('SET_AUTOJOINPASSWORD', this.$route.query.password);
this.$store.commit("SET_AUTOJOINPASSWORD", this.$route.query.password);
}
} else if (this.config) {
if (
this.config.autoJoin &&
(this.config.autoJoin === true || this.config.autoJoin === 'true')
(this.config.autoJoin === true || this.config.autoJoin === "true")
) {
this.$store.commit('SET_AUTOJOIN', true);
this.$store.commit('SET_AUTOJOINROOM', this.config.autoJoinRoom);
this.$store.commit('SET_AUTOJOINURL', this.config.autoJoinServer);
this.$store.commit("SET_AUTOJOIN", true);
this.$store.commit("SET_AUTOJOINROOM", this.config.autoJoinRoom);
this.$store.commit("SET_AUTOJOINURL", this.config.autoJoinServer);
this.$store.commit(
'SET_AUTOJOINPASSWORD',
this.config.autoJoinPassword,
"SET_AUTOJOINPASSWORD",
this.config.autoJoinPassword
);
}
} else if (settings) {
if (
settings.autoJoin &&
(settings.autoJoin === true || settings.autoJoin === 'true')
(settings.autoJoin === true || settings.autoJoin === "true")
) {
this.$store.commit('SET_AUTOJOIN', true);
this.$store.commit('SET_AUTOJOINROOM', settings.autoJoinRoom);
this.$store.commit('SET_AUTOJOINURL', settings.autoJoinServer);
this.$store.commit('SET_AUTOJOINPASSWORD', settings.autoJoinPassword);
this.$store.commit("SET_AUTOJOIN", true);
this.$store.commit("SET_AUTOJOINROOM", settings.autoJoinRoom);
this.$store.commit("SET_AUTOJOINURL", settings.autoJoinServer);
this.$store.commit("SET_AUTOJOINPASSWORD", settings.autoJoinPassword);
}
}
// Get other settings in order of importance: config -> settings
// Authentication Mechanism setting
if (this.config && this.config.authentication) {
this.$store.commit('SET_AUTHENTICATION', this.config.authentication);
this.$store.commit("SET_AUTHENTICATION", this.config.authentication);
} else if (settings && settings.authentication) {
this.$store.commit('SET_AUTHENTICATION', settings.authentication);
this.$store.commit("SET_AUTHENTICATION", settings.authentication);
} else {
this.$store.commit('SET_AUTHENTICATION', {
type: 'none',
this.$store.commit("SET_AUTHENTICATION", {
type: "none"
});
}
// Custom Servers list settings
let servers = [
{
name: 'SyncLounge AU1',
location: 'Sydney, Australia',
url: 'https://v3au1.synclounge.tv/slserver',
image: 'flags/au.png',
name: "SyncLounge AU1",
location: "Sydney, Australia",
url: "https://v3au1.synclounge.tv/slserver",
image: "flags/au.png"
},
{
name: 'SyncLounge EU1',
location: 'Amsterdam, Netherlands',
url: 'https://v2eu1.synclounge.tv/server',
image: 'flags/eu.png',
name: "SyncLounge EU1",
location: "Amsterdam, Netherlands",
url: "https://v2eu1.synclounge.tv/server",
image: "flags/eu.png"
},
{
name: 'SyncLounge US1',
location: 'Miami, United States',
url: 'https://v2us1.synclounge.tv/server',
image: 'flags/us.png',
name: "SyncLounge US1",
location: "Miami, United States",
url: "https://v2us1.synclounge.tv/server",
image: "flags/us.png"
},
{
name: 'SyncLounge US2',
location: 'Miami, United States',
url: 'https://v3us1.synclounge.tv/slserver',
image: 'flags/us.png',
name: "SyncLounge US2",
location: "Miami, United States",
url: "https://v3us1.synclounge.tv/slserver",
image: "flags/us.png"
},
{
name: 'SyncLounge US3',
location: 'Miami, United States',
url: 'https://v3us2.synclounge.tv/slserver',
image: 'flags/us.png',
},
name: "SyncLounge US3",
location: "Miami, United States",
url: "https://v3us2.synclounge.tv/slserver",
image: "flags/us.png"
}
];
const customServer = {
name: 'Custom Server',
location: 'Anywhere!',
url: 'custom',
image: 'synclounge-white.png',
name: "Custom Server",
location: "Anywhere!",
url: "custom",
image: "synclounge-white.png"
};
if (this.config && this.config.servers) {
servers = this.config.servers;
if (this.config.customServer) {
console.error("'customServer' setting provided with 'servers' setting. Ignoring 'customServer' setting.");
console.error(
"'customServer' setting provided with 'servers' setting. Ignoring 'customServer' setting."
);
}
} else if (settings && settings.servers) {
servers = settings.servers;
if (settings.customServer) {
console.error("'customServer' setting provided with 'servers' setting. Ignoring 'customServer' setting.");
console.error(
"'customServer' setting provided with 'servers' setting. Ignoring 'customServer' setting."
);
}
} else if (this.config && this.config.customServer) {
servers.push(this.config.customServer);
@ -315,72 +304,74 @@ export default {
servers.push(customServer);
}
this.$store.commit('setSetting', ['SERVERS', servers]);
this.$store.commit("setSetting", ["SERVERS", servers]);
// Auto-join if a single server is provided and autoJoinServer is not
if (servers.length == 1 && !this.$store.autoJoinServer) {
if (servers.length === 1 && !this.$store.autoJoinServer) {
const server = servers[0];
this.$store.commit('SET_AUTOJOIN', true);
this.$store.commit('SET_AUTOJOINURL', server.url);
this.$store.commit("SET_AUTOJOIN", true);
this.$store.commit("SET_AUTOJOINURL", server.url);
if (!this.$store.autoJoinRoom && server.defaultRoom) {
this.$store.commit('SET_AUTOJOINROOM', server.defaultRoom);
this.$store.commit("SET_AUTOJOINROOM", server.defaultRoom);
}
if (!this.$store.autoJoinPassword && server.defaultPassword) {
this.$store.commit('SET_AUTOJOINPASSWORD', server.defaultPassword);
this.$store.commit("SET_AUTOJOINPASSWORD", server.defaultPassword);
}
}
//
// End Settings
//
window.EventBus.$on('notification', (msg) => {
window.EventBus.$on("notification", msg => {
this.snackbarMsg = msg;
this.snackbar = true;
});
window.EventBus.$on('NEW_TIMELINE', (timeline) => {
this.$store.dispatch('NEW_TIMELINE', timeline);
window.EventBus.$on("NEW_TIMELINE", timeline => {
this.$store.dispatch("NEW_TIMELINE", timeline);
});
window.EventBus.$on('PLAYBACK_CHANGE', (data) => {
if (this.chosenClient.clientIdentifier !== 'PTPLAYER9PLUS10' && data[1]) {
this.$router.push(`/nowplaying/${data[2].machineIdentifier}/${data[1]}`);
window.EventBus.$on("PLAYBACK_CHANGE", data => {
if (this.chosenClient.clientIdentifier !== "PTPLAYER9PLUS10" && data[1]) {
this.$router.push(
`/nowplaying/${data[2].machineIdentifier}/${data[1]}`
);
}
if (
this.chosenClient.clientIdentifier !== 'PTPLAYER9PLUS10' &&
this.chosenClient.clientIdentifier !== "PTPLAYER9PLUS10" &&
!data[1] &&
this.$route.fullPath.indexOf('/nowplaying') > -1
this.$route.fullPath.indexOf("/nowplaying") > -1
) {
this.$router.push('/browse/');
this.$router.push("/browse/");
}
this.$store.dispatch('PLAYBACK_CHANGE', data);
this.$store.dispatch("PLAYBACK_CHANGE", data);
});
if (!window.localStorage.getItem('plexuser')) {
this.$router.push('/signin');
if (!window.localStorage.getItem("plexuser")) {
this.$router.push("/signin");
this.loading = false;
return;
}
if (this.$route.path === '/') {
this.$router.push('/clientselect');
if (this.$route.path === "/") {
this.$router.push("/clientselect");
}
const plexstorage = JSON.parse(window.localStorage.getItem('plexuser'));
const plexstorage = JSON.parse(window.localStorage.getItem("plexuser"));
try {
await this.$store.dispatch('PLEX_LOGIN_TOKEN', plexstorage.authToken);
await this.$store.dispatch("PLEX_LOGIN_TOKEN", plexstorage.authToken);
} catch (e) {
this.$router.push('/signin');
//this.$router.push('/signin');
return;
}
if (this.$store.state.autoJoin) {
this.$store.dispatch('autoJoin', {
this.$store.dispatch("autoJoin", {
server: this.$store.state.autoJoinUrl,
password: this.$store.state.autoJoinPassword,
room: this.$store.state.autoJoinRoom,
room: this.$store.state.autoJoinRoom
});
}
fscreen.addEventListener('fullscreenchange', () => {
fscreen.addEventListener("fullscreenchange", () => {
const isFullscreen = fscreen.fullscreenElement !== null;
this.appIsFullscreen = isFullscreen;
document.body.classList.toggle('is-fullscreen', isFullscreen);
document.body.classList.toggle("is-fullscreen", isFullscreen);
});
this.loading = false;
@ -390,11 +381,11 @@ export default {
if (this.showRightDrawerButton) {
this.drawerRight = true;
}
},
}
},
computed: {
...mapState('config', {
config: state => state.configuration,
...mapState("config", {
config: state => state.configuration
}),
plex() {
return this.$store.getters.getPlex;
@ -410,39 +401,39 @@ export default {
},
crumbs() {
if (
this.$route.path.indexOf('browse') === -1 &&
this.$route.path.indexOf('nowplaying') === -1
this.$route.path.indexOf("browse") === -1 &&
this.$route.path.indexOf("nowplaying") === -1
) {
return [];
}
const getTitle = (id) => {
const getTitle = id => {
try {
return this.itemCache[this.$route.params.machineIdentifier][id].title;
} catch (e) {
return 'Loading..';
return "Loading..";
}
};
const getLibrary = (id) => {
const getLibrary = id => {
try {
return this.libraryCache[this.$route.params.machineIdentifier][id];
} catch (e) {
return 'Library';
return "Library";
}
};
const data = [
{
text: 'Home',
to: '/browse',
},
text: "Home",
to: "/browse"
}
];
const map = {
machineIdentifier: () => ({
text: this.plex.servers[this.$route.params.machineIdentifier].name,
to: `/browse/${this.$route.params.machineIdentifier}`,
to: `/browse/${this.$route.params.machineIdentifier}`
}),
sectionId: () => ({
text: getLibrary(this.$route.params.sectionId),
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}`,
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}`
}),
parentKey: () => {
let to;
@ -453,19 +444,19 @@ export default {
}
return {
text: getTitle(this.$route.params.parentKey),
to,
to
};
},
grandparentKey: () => ({
text: getTitle(this.$route.params.grandparentKey),
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/tv/${this.$route.params.grandparentKey}/`,
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/tv/${this.$route.params.grandparentKey}/`
}),
ratingKey: () => ({
text: getTitle(this.$route.params.ratingKey),
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/${this.$route.params.ratingKey}`,
}),
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/${this.$route.params.ratingKey}`
})
};
Object.keys(this.$route.params).forEach((param) => {
Object.keys(this.$route.params).forEach(param => {
const link = map[param]();
if (link) {
data.push(link);
@ -477,7 +468,7 @@ export default {
return (
this.chosenClient &&
this.chosenClient.clientPlayingMetadata &&
this.$route.name === 'browse'
this.$route.name === "browse"
);
},
showRightDrawerButton() {
@ -496,7 +487,7 @@ export default {
return this.logos.light.small;
},
isPlayer() {
if (this.$route.path === '/') {
if (this.$route.path === "/") {
return true;
}
return false;
@ -532,10 +523,10 @@ export default {
mainStyle() {
if (this.$store.getters.getBackground !== null) {
return {
'background-image': `url(${this.$store.getters.getBackground})`,
'background-repeat': 'no-repeat',
'background-size': 'cover',
'background-position': 'center',
"background-image": `url(${this.$store.getters.getBackground})`,
"background-repeat": "no-repeat",
"background-size": "cover",
"background-position": "center"
};
}
return {};
@ -544,21 +535,21 @@ export default {
const arr = [];
if (this.$store.getters.getBackground !== null) {
arr.push({
background: 'rgba(0,0,0,0.7)',
background: "rgba(0,0,0,0.7)"
});
}
return arr;
},
paddingStyle() {
const arr = [];
if (this.$route.path.indexOf('/player') === -1) {
if (this.$route.path.indexOf("/player") === -1) {
arr.push({
padding: '16px',
padding: "16px"
});
}
return arr;
},
},
}
}
};
</script>
@ -568,7 +559,3 @@ export default {
text-decoration: none !important;
}
</style>
<style lang="stylus">
@import './stylus/main';
</style>

View File

@ -88,6 +88,10 @@ html {
background: #353e58
}
.v-navigation-drawer .theme--dark.v-list--nav.v-list {
background: inherit;
}
main {
/*background: #272c38;*/
background: #2b5876;

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

1
src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -36,18 +36,18 @@
<v-subheader>Recent Rooms</v-subheader>
<v-list class="pa-0">
<template v-for="(item, index) in recentsSorted">
<v-list-tile :key="index" v-if="index < 5" avatar @click="recentConnect(item)">
<v-list-tile-avatar>
<v-list-item :key="index" v-if="index < 5" avatar @click="recentConnect(item)">
<v-list-item-avatar>
<img :src="logos.light.small" style="width: 32px; height: auto" />
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title>{{ item.name || item.server || 'Custom' }}</v-list-tile-title>
<v-list-tile-sub-title>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>{{ item.name || item.server || 'Custom' }}</v-list-item-title>
<v-list-item-subtitle>
<b>{{ item.room }}</b>
<span style="opacity: 0.5; float: right">{{ sinceNow(item.time) }}</span>
</v-list-tile-sub-title>
</v-list-tile-content>
<v-list-tile-action>
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-tooltip top color="light-blue darken-4">
<v-icon
color="white"
@ -56,8 +56,8 @@
@click.stop="removeHistoryItem(item)"
>close</v-icon>Remove
</v-tooltip>
</v-list-tile-action>
</v-list-tile>
</v-list-item-action>
</v-list-item>
</template>
</v-list>
</v-flex>
@ -79,17 +79,17 @@
>
<v-card height="300px" style="border-radius: 20px">
<v-layout row wrap justify-start align-center style="height: 100%">
<v-flex xs12 class="text-xs-center pa-2" style="height: 80px">
<v-flex xs12 class="text-center pa-2" style="height: 80px">
<img
:src="server.image"
style="max-height: 100%; vertical-align: middle; max-width: 80%; border-radius: 7px"
/>
</v-flex>
<v-flex xs12 class="text-xs-center">
<v-flex xs12 class="text-center">
<h2>{{ server.name }}</h2>
<h4>{{ server.location }}</h4>
</v-flex>
<v-flex xs12 class="text-xs-center" v-if="server.url !== 'custom'">
<v-flex xs12 class="text-center" v-if="server.url !== 'custom'">
<div v-if="results[server.url]">
<div v-if="results[server.url].alive">
Ping:
@ -98,10 +98,10 @@
:class="connectionQualityClass(results[server.url].latency)"
>{{ results[server.url].latency }}ms</span>
</div>
<div v-else class="text-xs-center red--text">error</div>
<div v-else class="text-center red--text">error</div>
</div>
</v-flex>
<v-flex xs12 class="text-xs-center">
<v-flex xs12 class="text-center">
<div v-if="server.url !== 'custom'">
<div v-if="results[server.url]">
<div v-if="results[server.url].alive">
@ -113,12 +113,12 @@
>{{ results[server.url].result || 'Unknown' }}</span>
</div>
</div>
<div v-else class="text-xs-center red--text">error</div>
<div v-else class="text-center red--text">error</div>
</div>
</div>
</v-flex>
<v-flex xs12 class="text-xs-center pt-1 mt-4">
<v-flex xs12 class="text-center pt-1 mt-4">
<v-btn
color="primary"
:disabled="connectionPending"
@ -160,14 +160,14 @@
</div>
</v-flex>
</v-layout>
<v-layout class="pt-3 text-xs-center" row wrap v-if="serverError">
<v-layout class="pt-3 text-center" row wrap v-if="serverError">
<v-flex xs12 class="red--text">
<v-icon class="red--text">info</v-icon>
{{ serverError }}
</v-flex>
</v-layout>
</v-flex>
<v-flex xs12 v-if="context.getters.getConnected" class="text-xs-center">
<v-flex xs12 v-if="context.getters.getConnected" class="text-center">
<v-layout row wrap>
<v-flex xs12 md6 offset-md3>
<v-text-field
@ -193,7 +193,7 @@
<v-flex xs12 md6 offset-md3>
<v-btn block color="primary" v-on:click.native="joinRoom()">Join</v-btn>
</v-flex>
<v-layout class="pt-3 text-xs-center" row wrap v-if="roomError">
<v-layout class="pt-3 text-center" row wrap v-if="roomError">
<v-flex xs12 class="red--text">
<v-icon class="red--text">info</v-icon>
{{ roomError }}

View File

@ -19,7 +19,7 @@
contain
></v-img>
</v-flex>
<v-flex xs8 md12 class="text-xs-center hidden-sm-and-down ">
<v-flex xs8 md12 class="text-center hidden-sm-and-down ">
<div v-if="playable">
<v-btn block v-if="playable && contents.Media.length == 1 && (contents.viewOffset == 0 || !contents.viewOffset)" v-on:click.native="playMedia(contents)" class="primary white--text">
<v-icon> play_arrow </v-icon>
@ -47,12 +47,12 @@
<v-icon>more_vert</v-icon>
</v-btn>
<v-list>
<v-list-tile @click="markWatched(contents)">
<v-list-tile-title>Mark as played</v-list-tile-title>
</v-list-tile>
<v-list-tile :href="'https://app.plex.tv/desktop#!/server/' + contents.machineIdentifier + '/details?key=' + contents.key" target="_blank">
<v-list-tile-title>Open in Plex Web</v-list-tile-title>
</v-list-tile>
<v-list-item @click="markWatched(contents)">
<v-list-item-title>Mark as played</v-list-item-title>
</v-list-item>
<v-list-item :href="'https://app.plex.tv/desktop#!/server/' + contents.machineIdentifier + '/details?key=' + contents.key" target="_blank">
<v-list-item-title>Open in Plex Web</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</span>
@ -71,7 +71,7 @@
<v-chip v-if="contents.studio" color="grey darken-2" small label> {{ contents.studio }}</v-chip>
</div>
</v-flex>
<v-flex xs12 class="text-xs-center hidden-md-and-up ">
<v-flex xs12 class="text-center hidden-md-and-up ">
<div v-if="playable">
<v-btn block v-if="playable && contents.Media.length == 1 && (contents.viewOffset == 0 || !contents.viewOffset)" v-on:click.native="playMedia(contents)" class="primary white--text">
<v-icon> play_arrow </v-icon>

View File

@ -12,11 +12,11 @@
<h4> Libraries </h4>
<v-layout row wrap v-if="libraries && !browsingLibrary">
<v-flex xs12 md3 xl2 lg2 v-for="library in filteredLibraries" class="pa-1" :key="library.name">
<v-card v-on:click.native="setLibrary(library)" :img="getArtLibrary(library)" flat class="clickable text-xs-center" style="max-width:100%; cursor: pointer; border-radius: 0px !important">
<v-card v-on:click.native="setLibrary(library)" :img="getArtLibrary(library)" flat class="clickable text-center" style="max-width:100%; cursor: pointer; border-radius: 0px !important">
<div style="position:relative; width:100%; background: rgba(0,0,0,0.4); height:8em" class="hidden-xs-only">
<img style="height: 70%; display: block; margin-left: auto; margin-right: auto " :src="getThumb(library)" />
</div>
<div style="background: rgba(0,0,0,0.7); position:relative; width:100%;" class="text-xs-center pa-1">
<div style="background: rgba(0,0,0,0.7); position:relative; width:100%;" class="text-center pa-1">
<h2 class="truncate text-xs-left text-sm-center">{{ library.title }}</h2>
</div>
</v-card>

View File

@ -1,13 +1,13 @@
<template>
<v-list-tile avatar :style="styleObj" class="pa-1">
<v-list-tile-avatar>
<v-list-item avatar :style="styleObj" class="pa-1">
<v-list-item-avatar>
<img class="clientLogo" :class="platformClass" :src="url">
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title>{{ object.name }}<v-chip v-for="label in object.labels" :key="label[0]" :color="label[1]" small label>{{ label[0] }}</v-chip></v-list-tile-title>
<v-list-tile-sub-title>{{ object.product }} - last seen {{ lastSeenAgo }}</v-list-tile-sub-title>
</v-list-tile-content>
</v-list-tile>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title>{{ object.name }}<v-chip v-for="label in object.labels" :key="label[0]" :color="label[1]" small label>{{ label[0] }}</v-chip></v-list-item-title>
<v-list-item-subtitle>{{ object.product }} - last seen {{ lastSeenAgo }}</v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
</template>
<script>

View File

@ -24,7 +24,7 @@
Choose a client from the list below. Once you've found the client you would like to use, click the connect button. SyncLounge will test to see if it can connect with the client and will let you know if it cannot.
</v-flex>
</v-layout>
<div v-if="plex && !plex.gotDevices" class="text-xs-center pa-4">
<div v-if="plex && !plex.gotDevices" class="text-center pa-4">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</div>
<v-layout v-else row wrap justify-center class="ml-4 mr-4">
@ -42,7 +42,7 @@
Selected Player
</v-subheader>
<v-layout row wrap>
<v-flex md3 class="text-xs-center" style="position: relative">
<v-flex md3 class="text-center" style="position: relative">
<img :src="url" style="height: 100px; width: auto; vertical-align: middle" />
</v-flex>
<v-flex xs12 md9>

View File

@ -1,10 +1,10 @@
<template>
<v-layout wrap row class="text-xs-center" justify-center align-center>
<v-layout wrap row class="text-center" justify-center align-center>
<v-flex v-if="!loading" xs12 md5>
<v-card style="background: rgba(0,0,0,0.3)" class="pa-1">
<v-container fill-height>
<v-layout row wrap>
<v-flex xs12 md3 class="text-xs-center">
<v-flex xs12 md3 class="text-center">
<img :src="logos.light.small" style="width: 90%" />
</v-flex>
<v-flex md9>
@ -16,7 +16,7 @@
</div>
</div>
<v-layout wrap row class="pa-4 pt-2" justify-center align-center>
<v-flex xs12 md8 class="text-xs-center">
<v-flex xs12 md8 class="text-center">
<v-btn class="center" style="background-color: #E5A00D" @click.native="letsGo()">Accept Invite</v-btn>
</v-flex>
</v-layout>
@ -24,7 +24,7 @@
</v-layout>
</v-container>
<v-divider></v-divider>
<p style="opacity:0.7" class="text-xs-center pt-3">
<p style="opacity:0.7" class="text-center pt-3">
SyncLounge is a tool to sync Plex content with your family and friends. For more info click <a target="_blank" href="https://github.com/samcm/synclounge"> here</a>.
</p>
</v-card>

View File

@ -5,7 +5,7 @@
<!-- <v-flex><span style="opacity:0.6;font-size:60%; float:right"> {{ message.time}}</span></v-flex> -->
<div class="pt-1 pb-1 pl-2 pr-2 messageBubble me" style="">{{ message.msg }}</div>
</v-flex>
<v-flex xs1 class="text-xs-center pt-1">
<v-flex xs1 class="text-center pt-1">
<v-tooltip bottom color="rgb(44, 44, 49)" multi-line>
<span slot="activator">
<img :src="message.user.thumb || message.user.avatarUrl" class="mt-2 messageAvatar" />
@ -15,7 +15,7 @@
</v-flex>
</v-layout>
<v-layout row wrap justify-start align-center v-else>
<v-flex xs1 class="text-xs-center pt-1 mr-2 ml-2">
<v-flex xs1 class="text-center pt-1 mr-2 ml-2">
<v-tooltip bottom color="rgb(44, 44, 49)" multi-line>
<span slot="activator">
<img :src="message.user.thumb || message.user.avatarUrl" class="mt-2 messageAvatar" />

View File

@ -4,7 +4,7 @@
<v-card style="background: rgba(0,0,0,0.3)" class="pa-4">
<v-layout row wrap justify-center align-center v-if="ready">
<v-flex xs12 sm8 lg4>
<h1 class="text-xs-center pa-2">
<h1 class="text-center pa-2">
Hello
<span style="font-weight: 700">{{ plex.user.username }}</span>!
</h1>
@ -39,13 +39,13 @@
</v-flex>
</v-layout>
</div>
<div v-if="preAuth && !checkingAuth && !authError" class="text-xs-center">
<div v-if="preAuth && !checkingAuth && !authError" class="text-center">
<v-btn
class="primary"
@click="openPopup()"
>Sign in with Plex</v-btn>
</div>
<div v-if="authError" class="text-xs-center error">
<div v-if="authError" class="text-center error">
<p>You are not authorized to access this server</p>
</div>
<v-layout wrap row class="pt-4 pa-2">

View File

@ -10,13 +10,13 @@
<p class="pa-2"> All donations to SyncLounge go directly towards running the SyncLounge public servers and the continued development of the application. </p>
<v-subheader> How to donate </v-subheader>
<v-layout row justify-center align-center class="pa-0 ma-1">
<v-flex xs4 class="text-xs-center">
<v-flex xs4 class="text-center">
<v-btn block color="primary" class="white--text" target="_blank" href="https://paypal.me/PlexTogether">
Paypal
</v-btn>
</v-flex>
</v-layout>
<div class="text-xs-center pa-2">
<div class="text-center pa-2">
<v-layout row justify-center align-center v-for="(address, coin) in addresses" :key="coin" class="pa-0 ma-1">
<v-flex xs2 style="font-weight: 600">
{{ coin }}
@ -24,7 +24,7 @@
<v-flex xs8>
{{ address }}
</v-flex>
<v-flex xs2 class="text-xs-center">
<v-flex xs2 class="text-center">
<v-icon v-clipboard="address" v-on:click.native="sendNotification()" class="mr-2 primary--text click-cursor">content_copy</v-icon>
</v-flex>
</v-layout>

View File

@ -1,101 +1,99 @@
<template>
<v-container fill-height class="pa-0" style="height: 100%">
<v-layout row wrap justify-space-between>
<v-flex xs12>
<v-list class="pa-1 left-sidebar-list" dense style="background: none;">
<template>
<v-list-tile v-if="plex && plex.user">
<v-list-tile-avatar>
<img class="pa-1" :src="plex.user.thumb" />
</v-list-tile-avatar>
<v-list-tile-content>
<v-list-tile-title style="font-weight: bold">{{ plex.user.username }}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-divider></v-divider>
<v-subheader>Preferences</v-subheader>
<v-list-tile @click.stop="ptsettingstoggle = !ptsettingstoggle" class="text-xs-center">
<v-list-tile-action>
<v-icon color="white">settings</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>SyncLounge Settings</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile
v-if="plex && plex.gotDevices"
@click.stop="plexsettingstoggle = !plexsettingstoggle"
>
<v-list-tile-action>
<v-icon color="white">settings</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>Plex Settings</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-subheader v-if="plex && plex.gotDevices">Account</v-subheader>
<v-list-tile :router="true" to="/signout">
<v-list-tile-action>
<v-icon color="white">cancel</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>Sign out</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-subheader>About</v-subheader>
<v-list-tile href="https://synclounge.tv/" target="_blank">
<v-list-tile-action>
<v-icon color="white">info</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>SyncLounge v{{appVersion}}</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile href="https://discord.gg/fKQB3yt" target="_blank">
<v-list-tile-action>
<v-icon color="white">chat</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>Discord</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile href="https://github.com/samcm/synclounge" target="_blank">
<v-list-tile-action>
<v-icon color="white">code</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>GitHub</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-list-tile @click.stop="donateDialog = true">
<v-list-tile-action>
<v-icon color="white">favorite</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title>Donate</v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
</template>
</v-list>
</v-flex>
<v-navigation-drawer app temporary v-model="drawer" disable-route-watcher>
<v-list-item v-if="plex && plex.user">
<v-list-item-avatar>
<img class="pa-1" :src="plex.user.thumb" />
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-title style="font-weight: bold">{{ plex.user.username }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-divider></v-divider>
<v-list dense nav>
<v-subheader>Preferences</v-subheader>
<v-list-item @click.stop="ptsettingstoggle = !ptsettingstoggle">
<v-list-item-icon>
<v-icon color="white">settings</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>SyncLounge Settings</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item
v-if="plex && plex.gotDevices"
@click.stop="plexsettingstoggle = !plexsettingstoggle"
>
<v-list-item-icon>
<v-icon color="white">settings</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Plex Settings</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-subheader v-if="plex && plex.gotDevices">Account</v-subheader>
<v-list-item :router="true" to="/signout">
<v-list-item-icon>
<v-icon color="white">cancel</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Sign out</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-subheader>About</v-subheader>
<v-list-item href="https://synclounge.tv/" target="_blank">
<v-list-item-icon>
<v-icon color="white">info</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>SyncLounge v{{appVersion}}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item href="https://discord.gg/fKQB3yt" target="_blank">
<v-list-item-icon>
<v-icon color="white">chat</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Discord</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item href="https://github.com/samcm/synclounge" target="_blank">
<v-list-item-icon>
<v-icon color="white">code</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>GitHub</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item @click.stop="donateDialog = true">
<v-list-item-icon>
<v-icon color="white">favorite</v-icon>
</v-list-item-icon>
<v-list-item-content>
<v-list-item-title>Donate</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-spacer></v-spacer>
<v-flex xs12>
<v-layout row wrap justify-end align-end style="height: 100%">
<v-flex xs12>
<v-divider></v-divider>
<div class="text-xs-center pa-2" style="opacity: 0.7; font-size: 12px">
<div>Build #{{ hash }}</div>
<div>Last updated {{ updatedAt }}</div>
</div>
</v-flex>
</v-layout>
</v-flex>
</v-layout>
</v-list>
<template v-slot:append>
<v-divider></v-divider>
<div class="text-center pa-2" style="opacity: 0.7; font-size: 12px">
<div>Build #{{ hash }}</div>
<div>Last updated {{ updatedAt }}</div>
</div>
</template>
<v-dialog v-model="ptsettingstoggle" width="350">
<v-card style="background-color: #151924" class="pa-3">
<div class="text-xs-center">
<div class="text-center">
<h2>SyncLounge Settings</h2>
</div>
<v-divider class="mt-2 mb-2"></v-divider>
@ -104,7 +102,7 @@
</v-dialog>
<v-dialog v-model="plexsettingstoggle" width="350">
<v-card style="background-color: #151924" class="pa-3">
<div class="text-xs-center">
<div class="text-center">
<h2>Plex Settings</h2>
</div>
<v-divider class="mt-2 mb-2"></v-divider>
@ -114,7 +112,7 @@
<v-dialog v-model="donateDialog" max-width="650px">
<donate :donateDialog="donateDialog" :onClose="() => this.donateDialog = false"></donate>
</v-dialog>
</v-container>
</v-navigation-drawer>
</template>
<script>
@ -130,6 +128,9 @@ export default {
plexsettings,
donate
},
props: {
drawer: Boolean
},
data() {
return {
ptsettingstoggle: false,

View File

@ -1,49 +1,32 @@
import Vue from 'vue';
import VueScrollTo from 'vue-scrollto';
import Vuetify from 'vuetify';
import { ObserveVisibility } from 'vue-observe-visibility/dist/vue-observe-visibility';
import VueObserveVisibility from 'vue-observe-visibility';
import VueVideoPlayer from 'vue-video-player';
import VueResource from 'vue-resource';
import VueClipboards from 'vue-clipboards';
import VueCookies from 'vue-cookies'
import VueClipboard from 'vue-clipboard2';
import VueCookies from 'vue-cookies';
import moment from 'moment';
import App from './App';
import vuetify from './plugins/vuetify';
import App from './App.vue';
import router from './router';
import store from './store';
require('videojs-contrib-hls/dist/videojs-contrib-hls.js');
require('vanilla-tilt');
// require('videojs-contrib-hls/dist/videojs-contrib-hls.js');
// require('vanilla-tilt');
const moment = require('moment');
Vue.use(VueScrollTo);
Vue.use(VueClipboards);
Vue.use(VueResource);
Vue.directive('observe-visibility', ObserveVisibility);
Vue.use(VueClipboard);
Vue.use(VueObserveVisibility);
Vue.use(VueVideoPlayer);
Vue.use(Vuetify, {
theme: {
primary: '#E5A00D',
secondary: '#b0bec5',
accent: '#E5A00D',
error: '#b71c1c',
},
});
Vue.config.productionTip = false;
Vue.use(VueCookies);
// set default config
Vue.$cookies.config('7d');
function nolog() {}
if (process.env.NODE_ENV !== 'development') {
// console.log = nolog
// console.warn = nolog
// console.error = nolog
}
// Our Event bus
window.EventBus = new Vue();
window.EventBus.$on('command', (data) => {
@ -80,7 +63,7 @@ Vue.mixin({
sinceNow(x) {
const time = moment(x);
return time.fromNow();
},
}
},
computed: {
appVersion() {
@ -117,39 +100,41 @@ Vue.mixin({
return this.$route;
},
fontSizes() {
const w = Math.round(Math.max(document.documentElement.clientWidth, window.innerWidth || 0));
const w = Math.round(
Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
);
const maxPx = 94;
const maxRes = 3000;
return {
largest: {
'font-size': `${(w / maxRes) * maxPx}px`,
'font-size': `${(w / maxRes) * maxPx}px`
},
medium: {
'font-size': `${(w / maxRes) * maxPx * 0.6}px`,
},
'font-size': `${(w / maxRes) * maxPx * 0.6}px`
}
};
},
},
}
}
});
router.beforeEach((to, from, next) => {
// console.log('Route change', to, this, store)
if (to.matched.some(record => record.meta.protected)) {
if (to.matched.some((record) => record.meta.protected)) {
// this route requires us to be in a room with a client selected
// if not, redirect to the needed stage
if (!store.getters.getChosenClient) {
return next({
path: '/clientselect',
path: '/clientselect'
});
}
if (!store.getters.getRoom) {
return next({
path: '/joinroom',
path: '/joinroom'
});
}
if (!store.getters.getServer) {
return next({
path: '/joinroom',
path: '/joinroom'
});
}
next();
@ -161,20 +146,15 @@ router.beforeEach((to, from, next) => {
}
});
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: {
App,
},
vuetify,
render: (h) => h(App),
}).$mount('#app');
global.waitFor = async (ms) => new Promise((resolve) => {
setTimeout(() => resolve, ms);
});
global.waitFor = async ms =>
new Promise((resolve) => {
setTimeout(() => resolve, ms);
});
global.to = promise => promise.then(data => [null, data]).catch(err => [err]);
global.to = (promise) => promise.then((data) => [null, data]).catch((err) => [err]);

View File

@ -2,7 +2,7 @@
<router-link :to="href">
<v-card color="blue darken-4" hover style="height: 100%; width: 230px">
<v-layout row wrap justify-center align-center>
<v-flex md2 class="hidden-xs-only text-xs-center">
<v-flex md2 class="hidden-xs-only text-center">
<img :src="thumb" style="height: 52px; vertical-align: middle" />
</v-flex>
<v-flex md10 xs12 class="pl-3 pa-1 text-xs-left" style="overflow: hidden; white-space: nowrap; line-height: 24px">

22
src/plugins/vuetify.js Normal file
View File

@ -0,0 +1,22 @@
import Vue from 'vue';
import Vuetify from 'vuetify/lib';
Vue.use(Vuetify);
export default new Vuetify({
icons: {
iconfont: 'md',
},
theme: {
dark: true,
themes: {
dark: {
primary: '#E5A00D',
secondary: '#b0bec5',
accent: '#E5A00D',
error: '#b71c1c',
},
},
},
});

View File

@ -1,9 +1,9 @@
import Vue from 'vue';
import Router from 'vue-router';
// ===================== Pages Components ======================
import signin from '../components/signin';
import signout from '../components/signout';
import join from '../components/join';
import signin from '../components/signin.vue';
import signout from '../components/signout.vue';
import join from '../components/join.vue';
const SettingsHelper = require('../../SettingsHelper.js');
@ -15,128 +15,131 @@ Vue.use(Router);
export default new Router({
mode: 'hash',
base: settings.webroot || '/',
routes: [{
path: '/',
meta: {
protected: true,
routes: [
{
path: '/',
meta: {
protected: true,
},
},
},
{
path: '/signin',
meta: {
noload: true,
{
path: '/signin',
meta: {
noload: true,
},
component: signin,
},
component: signin,
},
{
path: '/signout',
meta: {
noload: true,
{
path: '/signout',
meta: {
noload: true,
},
component: signout,
},
component: signout,
},
{
path: '/join',
meta: {
noload: true,
protected: false,
{
path: '/join',
meta: {
noload: true,
protected: false,
},
component: join,
},
component: join,
},
{
path: '/clientselect',
meta: {
noload: false,
{
path: '/clientselect',
meta: {
noload: false,
},
component: () => import('../components/application/walkthrough.vue'),
},
component: require('../components/application/walkthrough.vue'),
},
{
path: '/joinroom',
meta: {
noload: false,
{
path: '/joinroom',
meta: {
noload: false,
},
component: () => import('../components/application/joinroom.vue'),
},
component: require('../components/application/joinroom.vue'),
},
{
path: '/player',
meta: {
protected: true,
{
path: '/player',
meta: {
protected: true,
},
component: () => import('../components/application/ptplayer.vue'),
},
component: require('../components/application/ptplayer.vue'),
},
{
path: '/nowplaying/:machineIdentifier/:ratingKey',
meta: {
protected: true,
{
path: '/nowplaying/:machineIdentifier/:ratingKey',
meta: {
protected: true,
},
name: 'nowplaying',
component: () => import('../components/application/plexbrowser/plexcontent.vue'),
},
name: 'nowplaying',
component: require('../components/application/plexbrowser/plexcontent.vue'),
},
{
path: '/browse',
meta: {
protected: true,
{
path: '/browse',
meta: {
protected: true,
},
name: 'browse',
component: () => import('../components/application/plexbrowser.vue'),
},
name: 'browse',
component: require('../components/application/plexbrowser.vue'),
},
{
path: '/browse/:machineIdentifier',
meta: {
protected: true,
{
path: '/browse/:machineIdentifier',
meta: {
protected: true,
},
name: 'server',
component: () => import('../components/application/plexbrowser/plexserver.vue'),
},
name: 'server',
component: require('../components/application/plexbrowser/plexserver.vue'),
},
{
path: '/browse/:machineIdentifier/:sectionId',
meta: {
protected: true,
{
path: '/browse/:machineIdentifier/:sectionId',
meta: {
protected: true,
},
name: 'library',
component: () => import('../components/application/plexbrowser/plexlibrary.vue'),
},
name: 'library',
component: require('../components/application/plexbrowser/plexlibrary.vue'),
},
{
path: '/browse/:machineIdentifier/:sectionId/:ratingKey',
meta: {
protected: true,
{
path: '/browse/:machineIdentifier/:sectionId/:ratingKey',
meta: {
protected: true,
},
name: 'content',
component: () => import('../components/application/plexbrowser/plexcontent.vue'),
},
name: 'content',
component: require('../components/application/plexbrowser/plexcontent.vue'),
},
{
path: '/browse/:machineIdentifier/:sectionId/tv/:ratingKey',
meta: {
protected: true,
{
path: '/browse/:machineIdentifier/:sectionId/tv/:ratingKey',
meta: {
protected: true,
},
name: 'series',
component: () => import('../components/application/plexbrowser/plexseries.vue'),
},
name: 'series',
component: require('../components/application/plexbrowser/plexseries.vue'),
},
{
path: '/browse/:machineIdentifier/:sectionId/tv/:parentKey/:ratingKey',
meta: {
protected: true,
{
path: '/browse/:machineIdentifier/:sectionId/tv/:parentKey/:ratingKey',
meta: {
protected: true,
},
name: 'season',
component: () => import('../components/application/plexbrowser/plexseason.vue'),
},
name: 'season',
component: require('../components/application/plexbrowser/plexseason.vue'),
},
{
path: '/browse/:machineIdentifier/:sectionId/tv/:grandparentKey/:parentKey/:ratingKey',
meta: {
protected: true,
{
path:
'/browse/:machineIdentifier/:sectionId/tv/:grandparentKey/:parentKey/:ratingKey',
meta: {
protected: true,
},
name: 'content',
component: () => import('../components/application/plexbrowser/plexcontent.vue'),
},
name: 'content',
component: require('../components/application/plexbrowser/plexcontent.vue'),
},
{
path: '/browse/:machineIdentifier/tv/:grandparentKey/:parentKey/:ratingKey',
meta: {
protected: true,
{
path:
'/browse/:machineIdentifier/tv/:grandparentKey/:parentKey/:ratingKey',
meta: {
protected: true,
},
name: 'content',
component: () => import('../components/application/plexbrowser/plexcontent.vue'),
},
name: 'content',
component: require('../components/application/plexbrowser/plexcontent.vue'),
},
],
});

View File

@ -14,9 +14,9 @@
<v-icon>more_vert</v-icon>
</v-btn>
<v-list>
<v-list-tile @click="handleDisconnect()">
<v-list-tile-title class="user-menu-list">Leave Room</v-list-tile-title>
</v-list-tile>
<v-list-item @click="handleDisconnect()">
<v-list-item-title class="user-menu-list">Leave Room</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-flex>
@ -62,7 +62,7 @@
</v-card>
<v-card style="background: #E5A00D; border-radius: 7px" class="pa-2 ma-3" v-if="me.role !== 'host' && this.$route.path.indexOf('/player') === -1">
<v-layout row wrap justify-space-between="" align-center>
<v-flex xs12 class="text-xs-center">
<v-flex xs12 class="text-center">
<span class="mb-0 pb-0 pa-0" style="color: rgb(44, 44, 49); "> Waiting for {{ hostUser().username }} to start</span>
</v-flex>
</v-layout>
@ -70,33 +70,33 @@
</v-flex>
<v-list dense two-line style="overflow: auto; max-height: calc(50vh - 154px); background: none">
<v-card style="background: linear-gradient(180deg,#1f1c2c,#182848)!important; border-radius: 7px" class="pa-1 ml-3 mr-3">
<v-list-tile avatar style="height:4em" class="pl-1 pr-1 mb-0" tag="div">
<v-list-tile-avatar>
<v-list-item avatar style="height:4em" class="pl-1 pr-1 mb-0" tag="div">
<v-list-item-avatar>
<img v-bind:src="hostUser().avatarUrl" :style="getImgStyle(hostUser())">
<v-icon v-if="hostUser().playerState !== 'playing'" style="font-size: 26px; opacity: 0.8; position: absolute;background-color: rgba(0,0,0,0.5)">
{{ playerState(hostUser()) }}
</v-icon>
</img>
</v-list-tile-avatar>
<v-list-tile-content>
</v-list-item-avatar>
<v-list-item-content>
<v-tooltip bottom color="rgb(44, 44, 49)" multi-line class="userlist">
<span slot="activator">
<v-list-tile-title> {{ hostUser().username }} <span style="opacity: 0.6" v-if="hostUser().uuid === me.uuid"> (you) </span></v-list-tile-title>
<v-list-tile-sub-title style="opacity:0.6;color:white;font-size:70%">{{ getTitle(hostUser()) }}</v-list-tile-sub-title>
<v-list-item-title> {{ hostUser().username }} <span style="opacity: 0.6" v-if="hostUser().uuid === me.uuid"> (you) </span></v-list-item-title>
<v-list-item-subtitle style="opacity:0.6;color:white;font-size:70%">{{ getTitle(hostUser()) }}</v-list-item-subtitle>
</span>
Watching on {{ hostUser().playerProduct || 'Unknown Plex Client' }}
<span v-if="plex.servers[hostUser().machineIdentifier]">
<br />via {{ plex.servers[hostUser().machineIdentifier].name }}
</span>
</v-tooltip>
</v-list-tile-content>
<v-list-tile-action>
</v-list-item-content>
<v-list-item-action>
<v-tooltip bottom color="rgb(44, 44, 49)" multi-line class="userlist">
<v-icon style="color: #E5A00D" slot="activator">star</v-icon>
Host
</v-tooltip>
</v-list-tile-action>
</v-list-tile>
</v-list-item-action>
</v-list-item>
<div class="pl-1 pr-1 pt-1 mt-0 pb-0 mb-0">
<span style="float: left; font-size:70%" class="ptuser-time pl-1">{{ getCurrent(hostUser()) }}</span>
<span style="float: right; font-size:70%" class="ptuser-maxTime pr-1">{{ getMax(hostUser()) }}</span>
@ -105,27 +105,27 @@
</v-card>
<div v-for="user in ptUsers" v-bind:key="user.username">
<div class="pa-1 ml-3 mr-3" v-if="!isHost(user)">
<v-list-tile avatar style="height:4em" class="pb-0 mb-0" tag="div">
<v-list-tile-avatar v-on:dblclick="transferHost(user.username)">
<v-list-item avatar style="height:4em" class="pb-0 mb-0" tag="div">
<v-list-item-avatar v-on:dblclick="transferHost(user.username)">
<img v-bind:src="user.avatarUrl" :style="getImgStyle(user)">
<v-icon v-if="user.playerState !== 'playing'" style="font-size: 26px; opacity: 0.8; position: absolute;background-color: rgba(0,0,0,0.7)">
{{ playerState(user) }}
</v-icon>
</img>
</v-list-tile-avatar>
<v-list-tile-content>
</v-list-item-avatar>
<v-list-item-content>
<v-tooltip bottom color="rgb(44, 44, 49)" multi-line class="userlist">
<span slot="activator">
<v-list-tile-title> {{ user.username }} <span style="opacity: 0.6" v-if="user.uuid === me.uuid"> (you) </span></v-list-tile-title>
<v-list-tile-sub-title style="opacity:0.6;color:white;font-size:70%">{{ getTitle(user) }}</v-list-tile-sub-title>
<v-list-item-title> {{ user.username }} <span style="opacity: 0.6" v-if="user.uuid === me.uuid"> (you) </span></v-list-item-title>
<v-list-item-subtitle style="opacity:0.6;color:white;font-size:70%">{{ getTitle(user) }}</v-list-item-subtitle>
</span>
Watching on {{ user.playerProduct || 'Unknown Plex Client' }}
<span v-if="plex.servers[user.machineIdentifier]">
<br />via {{ plex.servers[user.machineIdentifier].name }}
</span>
</v-tooltip>
</v-list-tile-content>
<v-list-tile-action>
</v-list-item-content>
<v-list-item-action>
<v-tooltip bottom color="rgb(44, 44, 49)" multi-line class="userlist">
<v-icon v-if="isHost(user)" style="color: #E5A00D" slot="activator">star</v-icon>
Host
@ -135,16 +135,16 @@
<v-icon>more_vert</v-icon>
</v-btn>
<v-list>
<v-list-tile @click="transferHost(user.username)">
<v-list-tile-title class="user-menu-list">Make Host</v-list-tile-title>
</v-list-tile>
<!-- <v-list-tile @click.stop="openInviteDialog(user)">
<v-list-tile-title class="user-menu-list">Invite to a Plex Server</v-list-tile-title>
</v-list-tile> -->
<v-list-item @click="transferHost(user.username)">
<v-list-item-title class="user-menu-list">Make Host</v-list-item-title>
</v-list-item>
<!-- <v-list-item @click.stop="openInviteDialog(user)">
<v-list-item-title class="user-menu-list">Invite to a Plex Server</v-list-item-title>
</v-list-item> -->
</v-list>
</v-menu>
</v-list-tile-action>
</v-list-tile>
</v-list-item-action>
</v-list-item>
<div class="pl-0 pr-0 pt-1 mt-0 pb-0 mb-0">
<span style="float: left; font-size:70%" class="ptuser-time pl-1">{{ getCurrent(user) }}</span>
<span style="float: right; font-size:70%" class="ptuser-maxTime pr-1">{{ getMax(user) }}</span>

15
src/store/index.js Normal file
View File

@ -0,0 +1,15 @@
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
},
});

View File

@ -1,15 +0,0 @@
@import '../../node_modules/vuetify/src/stylus/settings/_colors'
$theme := {
primary: #E5A00D
accent: #353e58
lighter: #353e58
secondary: #353e58
info: $blue.lighten-1
warning: $amber.darken-2
error: $red.accent-4
success: $green.lighten-2
}
/** Stylus Styles */
@import '../../node_modules/vuetify/src/stylus/main'

5
vue.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
transpileDependencies: [
'vuetify',
],
};