mirror of https://github.com/ErsatzTV/ErsatzTV.git
60 changed files with 2 additions and 25553 deletions
@ -1,22 +0,0 @@ |
|||||||
name: Lint VueJS Files on PR Request |
|
||||||
on: |
|
||||||
pull_request: |
|
||||||
jobs: |
|
||||||
vue-lint: |
|
||||||
runs-on: ubuntu-latest |
|
||||||
steps: |
|
||||||
# Checkout the current repo |
|
||||||
- name: Checkout current repository |
|
||||||
uses: actions/checkout@v4 |
|
||||||
# Setup NodeJS version 16 |
|
||||||
- name: Setup NodeJS V16.x.x |
|
||||||
uses: actions/setup-node@v3 |
|
||||||
with: |
|
||||||
node-version: '16' |
|
||||||
# CD into the current client directory and lint and build the client |
|
||||||
- name: Lint and Build the client |
|
||||||
run: | |
|
||||||
cd ./ErsatzTV/client-app/ |
|
||||||
npm ci --no-optional |
|
||||||
npm run lint |
|
||||||
npm run build --if-present |
|
@ -1,2 +0,0 @@ |
|||||||
VUE_APP_I18N_LOCALE=en |
|
||||||
VUE_APP_I18N_FALLBACK_LOCALE=en |
|
@ -1,28 +0,0 @@ |
|||||||
.DS_Store |
|
||||||
node_modules |
|
||||||
/dist |
|
||||||
|
|
||||||
|
|
||||||
# local env files |
|
||||||
.env.local |
|
||||||
.env.*.local |
|
||||||
|
|
||||||
# Log files |
|
||||||
npm-debug.log* |
|
||||||
yarn-debug.log* |
|
||||||
yarn-error.log* |
|
||||||
pnpm-debug.log* |
|
||||||
|
|
||||||
# Editor directories and files |
|
||||||
.idea |
|
||||||
.vscode |
|
||||||
*.suo |
|
||||||
*.ntvs* |
|
||||||
*.njsproj |
|
||||||
*.sln |
|
||||||
*.sw? |
|
||||||
|
|
||||||
# generated files |
|
||||||
src/assets/css/*.css |
|
||||||
src/models/*.js |
|
||||||
src/models/*.js.map |
|
@ -1,5 +0,0 @@ |
|||||||
{ |
|
||||||
"semi": true, |
|
||||||
"singleQuote": true, |
|
||||||
"trailingComma": "none" |
|
||||||
} |
|
@ -1,27 +0,0 @@ |
|||||||
# ErsatzTV Vuetify UI - WIP |
|
||||||
|
|
||||||
## Project setup |
|
||||||
``` |
|
||||||
npm install |
|
||||||
``` |
|
||||||
|
|
||||||
### Compiles and hot-reloads for development |
|
||||||
``` |
|
||||||
npm run serve |
|
||||||
``` |
|
||||||
|
|
||||||
To set a custom ErsatzTV api URL, use the following environment variable `VUE_APP_ETV_BASE_URL`. |
|
||||||
|
|
||||||
### Compiles and minifies for production |
|
||||||
``` |
|
||||||
npm run build |
|
||||||
``` |
|
||||||
|
|
||||||
### Lints and fixes files |
|
||||||
``` |
|
||||||
npm run lint |
|
||||||
``` |
|
||||||
|
|
||||||
### Customize configuration |
|
||||||
See [Configuration Reference](https://cli.vuejs.org/config/). |
|
||||||
|
|
@ -1,3 +0,0 @@ |
|||||||
module.exports = { |
|
||||||
presets: ['@vue/cli-plugin-babel/preset'] |
|
||||||
}; |
|
@ -1,19 +0,0 @@ |
|||||||
{ |
|
||||||
"compilerOptions": { |
|
||||||
"target": "es5", |
|
||||||
"module": "esnext", |
|
||||||
"baseUrl": "./", |
|
||||||
"moduleResolution": "node", |
|
||||||
"paths": { |
|
||||||
"@/*": [ |
|
||||||
"src/*" |
|
||||||
] |
|
||||||
}, |
|
||||||
"lib": [ |
|
||||||
"esnext", |
|
||||||
"dom", |
|
||||||
"dom.iterable", |
|
||||||
"scripthost" |
|
||||||
] |
|
||||||
} |
|
||||||
} |
|
File diff suppressed because it is too large
Load Diff
@ -1,80 +0,0 @@ |
|||||||
{ |
|
||||||
"name": "client-app", |
|
||||||
"version": "0.1.0", |
|
||||||
"private": true, |
|
||||||
"scripts": { |
|
||||||
"serve": "vue-cli-service serve", |
|
||||||
"build": "vue-cli-service build", |
|
||||||
"lint": "vue-cli-service lint", |
|
||||||
"i18n:report": "vue-cli-service i18n:report --src \"./src/**/*.?(js|vue)\" --locales \"./src/locales/**/*.json\"" |
|
||||||
}, |
|
||||||
"dependencies": { |
|
||||||
"@mdi/font": "5.9.55", |
|
||||||
"@sweetalert2/theme-dark": "^5.0.11", |
|
||||||
"axios": "^0.26.1", |
|
||||||
"core-js": "^3.8.3", |
|
||||||
"pinia": "^2.0.11", |
|
||||||
"pinia-plugin-persistedstate": "^1.5.1", |
|
||||||
"roboto-fontface": "*", |
|
||||||
"sweetalert2": "^11.4.17", |
|
||||||
"vue": "^2.6.14", |
|
||||||
"vue-class-component": "^7.2.6", |
|
||||||
"vue-i18n": "^8.26.3", |
|
||||||
"vue-property-decorator": "^9.1.2", |
|
||||||
"vue-router": "^3.2.0", |
|
||||||
"vue-sweetalert2": "^5.0.5", |
|
||||||
"vuetify": "^2.6.0" |
|
||||||
}, |
|
||||||
"devDependencies": { |
|
||||||
"@babel/core": "^7.12.16", |
|
||||||
"@babel/eslint-parser": "^7.12.16", |
|
||||||
"@typescript-eslint/eslint-plugin": "^5.4.0", |
|
||||||
"@typescript-eslint/parser": "^5.4.0", |
|
||||||
"@vue/cli-plugin-babel": "~5.0.0", |
|
||||||
"@vue/cli-plugin-eslint": "~5.0.0", |
|
||||||
"@vue/cli-plugin-typescript": "~5.0.0", |
|
||||||
"@vue/cli-service": "~5.0.0", |
|
||||||
"@vue/composition-api": "^1.4.9", |
|
||||||
"@vue/eslint-config-typescript": "^9.1.0", |
|
||||||
"eslint": "^7.32.0", |
|
||||||
"eslint-config-prettier": "^8.5.0", |
|
||||||
"eslint-plugin-prettier": "^4.0.0", |
|
||||||
"eslint-plugin-vue": "^8.0.3", |
|
||||||
"sass": "~1.32.0", |
|
||||||
"sass-loader": "^10.0.0", |
|
||||||
"typescript": "~4.5.5", |
|
||||||
"vue-cli-plugin-i18n": "~2.3.1", |
|
||||||
"vue-cli-plugin-vuetify": "~2.4.7", |
|
||||||
"vue-template-compiler": "^2.6.14", |
|
||||||
"vuetify-loader": "^1.7.0" |
|
||||||
}, |
|
||||||
"eslintConfig": { |
|
||||||
"root": true, |
|
||||||
"env": { |
|
||||||
"node": true |
|
||||||
}, |
|
||||||
"extends": [ |
|
||||||
"plugin:vue/essential", |
|
||||||
"plugin:prettier/recommended", |
|
||||||
"eslint:recommended", |
|
||||||
"@vue/typescript" |
|
||||||
], |
|
||||||
"parserOptions": { |
|
||||||
"parser": "@typescript-eslint/parser" |
|
||||||
}, |
|
||||||
"rules": { |
|
||||||
"prettier/prettier": [ |
|
||||||
"error", |
|
||||||
{ |
|
||||||
"endOfLine": "auto", |
|
||||||
"tabWidth": 4 |
|
||||||
} |
|
||||||
] |
|
||||||
} |
|
||||||
}, |
|
||||||
"browserslist": [ |
|
||||||
"> 1%", |
|
||||||
"last 2 versions", |
|
||||||
"not dead" |
|
||||||
] |
|
||||||
} |
|
Before Width: | Height: | Size: 15 KiB |
@ -1,17 +0,0 @@ |
|||||||
<!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> |
|
||||||
</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> |
|
@ -1,36 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-app> |
|
||||||
<Toolbar /> |
|
||||||
<!-- Sizes your content based upon application components --> |
|
||||||
<v-main> |
|
||||||
<!-- Provides the application the proper gutter --> |
|
||||||
<v-container fluid> |
|
||||||
<!-- If using vue-router --> |
|
||||||
<router-view></router-view> |
|
||||||
</v-container> |
|
||||||
<SnackBar /> |
|
||||||
</v-main> |
|
||||||
</v-app> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import Vue from 'vue'; |
|
||||||
import Toolbar from '@/components/Navigation/ToolBar.vue'; |
|
||||||
import SnackBar from '@/components/PopUps/SnackBar'; |
|
||||||
export default Vue.extend({ |
|
||||||
name: 'App', |
|
||||||
components: { Toolbar, SnackBar }, |
|
||||||
data: () => ({}) |
|
||||||
}); |
|
||||||
</script> |
|
||||||
<style> |
|
||||||
main { |
|
||||||
background-attachment: fixed; |
|
||||||
background-repeat: no-repeat; |
|
||||||
-webkit-background-size: cover; |
|
||||||
-moz-background-size: cover; |
|
||||||
-o-background-size: cover; |
|
||||||
background-size: cover; |
|
||||||
background-position: center; |
|
||||||
} |
|
||||||
</style> |
|
@ -1,12 +0,0 @@ |
|||||||
.swal2-container.swal2-center > .swal2-popup { |
|
||||||
background-color: #1E1E1E !important; |
|
||||||
border-color: green; |
|
||||||
border-width: thin; |
|
||||||
border-style: solid; |
|
||||||
width: fit-content; |
|
||||||
} |
|
||||||
|
|
||||||
.swal2-html-container, .swal2-title { |
|
||||||
color: white !important; |
|
||||||
font-family:Cambria; |
|
||||||
} |
|
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 2.7 KiB |
@ -1,44 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-select |
|
||||||
v-on:change="setUserLanguage($event)" |
|
||||||
:value="language" |
|
||||||
dense |
|
||||||
outlined |
|
||||||
:items="langs" |
|
||||||
:label="$t('sidebar.languages')" |
|
||||||
class="ma-2" |
|
||||||
></v-select> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { languageState } from '@/stores/languageState'; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'language-changer', |
|
||||||
data() { |
|
||||||
return { |
|
||||||
langs: [ |
|
||||||
{ text: this.$t('languages-code.en'), value: 'en' }, |
|
||||||
{ text: this.$t('languages-code.pt-br'), value: 'pt-br' } |
|
||||||
] |
|
||||||
}; |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
...mapState(languageState, ['language', 'setLanguage']), |
|
||||||
languageCode: { |
|
||||||
get() { |
|
||||||
return languageState.language; |
|
||||||
}, |
|
||||||
set(value) { |
|
||||||
this.setLanguage(value); |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
methods: { |
|
||||||
setUserLanguage: ($event) => { |
|
||||||
languageState().setLanguage($event); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,27 +0,0 @@ |
|||||||
<template> |
|
||||||
<span> |
|
||||||
<v-img |
|
||||||
v-if="!isNavigationMini" |
|
||||||
src="@/assets/images/ersatztv.svg" |
|
||||||
class="ma-3" |
|
||||||
></v-img> |
|
||||||
<v-img |
|
||||||
v-if="isNavigationMini" |
|
||||||
src="@/assets/images/ersatztv-logo.svg" |
|
||||||
class="ma-1" |
|
||||||
></v-img> |
|
||||||
<v-divider inset></v-divider> |
|
||||||
</span> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { applicationState } from '@/stores/applicationState'; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'SideBarLogo', |
|
||||||
computed: { |
|
||||||
...mapState(applicationState, ['isNavigationMini']) |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,43 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-list nav dense> |
|
||||||
<span v-for="(nav, i) in navigation" :key="i"> |
|
||||||
<SideBarMenuItem |
|
||||||
v-if="(!nav.children || !nav.showchildren) && !nav.meta.hidden" |
|
||||||
:name="nav.name" |
|
||||||
:path="nav.path" |
|
||||||
:icon="nav.meta.icon" |
|
||||||
:disabled="nav.meta.disabled" |
|
||||||
/> |
|
||||||
<SideBarMenuItemExpandable |
|
||||||
v-else-if="nav.children" |
|
||||||
@click.native="disableMiniNavigation()" |
|
||||||
:name="nav.name" |
|
||||||
:icon="nav.meta.icon" |
|
||||||
:disabled="nav.meta.disabled" |
|
||||||
:children="nav.children" |
|
||||||
/> |
|
||||||
</span> |
|
||||||
</v-list> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import SideBarMenuItem from './SideBarMenuItem'; |
|
||||||
import SideBarMenuItemExpandable from './SideBarMenuItemExpandable'; |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { applicationState } from '@/stores/applicationState'; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'NavSidebar', |
|
||||||
components: { SideBarMenuItem, SideBarMenuItemExpandable }, |
|
||||||
data: () => ({ |
|
||||||
navigation: null |
|
||||||
}), |
|
||||||
computed: { |
|
||||||
...mapState(applicationState, ['disableMiniNavigation']) |
|
||||||
}, |
|
||||||
beforeMount: function () { |
|
||||||
//Pull in navigation from routes and load into DOM |
|
||||||
this.navigation = this.$router.options.routes; |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,37 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-list-item-group color="primary"> |
|
||||||
<v-list-item :to="path" :disabled="disabled"> |
|
||||||
<v-list-item-icon> |
|
||||||
<v-icon v-text="icon" :disabled="disabled" /> |
|
||||||
</v-list-item-icon> |
|
||||||
|
|
||||||
<v-list-item-content> |
|
||||||
<v-list-item-title v-text="$t(name)" /> |
|
||||||
</v-list-item-content> |
|
||||||
</v-list-item> |
|
||||||
</v-list-item-group> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: 'SideBarMenuItem', |
|
||||||
props: { |
|
||||||
name: { |
|
||||||
type: String, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
path: { |
|
||||||
type: String, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
icon: { |
|
||||||
type: String, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
disabled: { |
|
||||||
type: Boolean, |
|
||||||
required: true |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,73 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-list-group |
|
||||||
color="primary" |
|
||||||
v-model="menuItemOpened" |
|
||||||
:disabled="disabled" |
|
||||||
:prepend-icon="icon" |
|
||||||
no-action |
|
||||||
> |
|
||||||
<template v-slot:activator> |
|
||||||
<v-list-item-content> |
|
||||||
<v-list-item-title v-text="$t(name)"></v-list-item-title> |
|
||||||
</v-list-item-content> |
|
||||||
</template> |
|
||||||
|
|
||||||
<v-list-item |
|
||||||
v-for="child in children" |
|
||||||
:key="child.name" |
|
||||||
:to="child.path" |
|
||||||
:disabled="child.meta.disabled" |
|
||||||
> |
|
||||||
<v-list-item-icon> |
|
||||||
<v-icon |
|
||||||
v-text="child.meta.icon" |
|
||||||
:disabled="child.meta.disabled" |
|
||||||
/> |
|
||||||
</v-list-item-icon> |
|
||||||
<v-list-item-content> |
|
||||||
<v-list-item-title v-text="$t(child.name)"></v-list-item-title> |
|
||||||
</v-list-item-content> |
|
||||||
</v-list-item> |
|
||||||
</v-list-group> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: 'SideBarMenuItemExpandable', |
|
||||||
props: { |
|
||||||
name: { |
|
||||||
type: String, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
icon: { |
|
||||||
type: String, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
disabled: { |
|
||||||
type: Boolean, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
children: { |
|
||||||
type: Array, |
|
||||||
required: true |
|
||||||
}, |
|
||||||
nonmenuchildren: { |
|
||||||
type: Array, |
|
||||||
required: false |
|
||||||
} |
|
||||||
}, |
|
||||||
data: () => ({ |
|
||||||
opened: false |
|
||||||
}), |
|
||||||
computed: { |
|
||||||
menuItemOpened: { |
|
||||||
get: function () { |
|
||||||
return this.opened; |
|
||||||
}, |
|
||||||
set: function () { |
|
||||||
return !this.opened; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,28 +0,0 @@ |
|||||||
<template> |
|
||||||
<span v-if="!isNavigationMini" class="text-center"> |
|
||||||
<v-divider inset></v-divider> |
|
||||||
<h4 class="pt-2">ErsatzTV {{ $t('sidebar.version') }}</h4> |
|
||||||
|
|
||||||
<p>{{ currentServerVersion }}</p> |
|
||||||
</span> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { applicationState } from '@/stores/applicationState'; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'SideBarVersion', |
|
||||||
setup() { |
|
||||||
applicationState().getVersion(); |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
...mapState(applicationState, [ |
|
||||||
'currentServerVersion', |
|
||||||
'isNavigationMini' |
|
||||||
]) |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped></style> |
|
@ -1,53 +0,0 @@ |
|||||||
<template> |
|
||||||
<nav> |
|
||||||
<v-app-bar flat app dense absolute> |
|
||||||
<v-app-bar-nav-icon @click.stop="toggleMiniNavigation()" /> |
|
||||||
|
|
||||||
<ToolBarSearch /> |
|
||||||
<v-spacer /> |
|
||||||
|
|
||||||
<ToolBarLinks /> |
|
||||||
</v-app-bar> |
|
||||||
<v-navigation-drawer app :mini-variant="isNavigationMini" permanent> |
|
||||||
<template v-slot:prepend> |
|
||||||
<SideBarLogo /> |
|
||||||
</template> |
|
||||||
|
|
||||||
<SideBarMenu /> |
|
||||||
|
|
||||||
<template v-slot:append> |
|
||||||
<SideBarLanguageSelect /> |
|
||||||
<SideBarVersion /> |
|
||||||
</template> |
|
||||||
</v-navigation-drawer> |
|
||||||
</nav> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import SideBarLogo from './SideBarLogo.vue'; |
|
||||||
import SideBarMenu from './SideBarMenu.vue'; |
|
||||||
import SideBarVersion from './SideBarVersion'; |
|
||||||
import ToolBarLinks from './ToolBarLinks'; |
|
||||||
import ToolBarSearch from './ToolBarSearch'; |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { applicationState } from '@/stores/applicationState'; |
|
||||||
import SideBarLanguageSelect from './SideBarLanguageSelect.vue'; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'NavToolbar', |
|
||||||
components: { |
|
||||||
SideBarMenu, |
|
||||||
SideBarLogo, |
|
||||||
SideBarVersion, |
|
||||||
ToolBarLinks, |
|
||||||
ToolBarSearch, |
|
||||||
SideBarLanguageSelect |
|
||||||
}, |
|
||||||
computed: { |
|
||||||
...mapState(applicationState, [ |
|
||||||
'isNavigationMini', |
|
||||||
'toggleMiniNavigation' |
|
||||||
]) |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,96 +0,0 @@ |
|||||||
<template> |
|
||||||
<span> |
|
||||||
<v-tooltip bottom> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-btn |
|
||||||
class="ma-2" |
|
||||||
outlined |
|
||||||
color="primary" |
|
||||||
@click="copyTextToClipboard(navBarURLs.m3uURL)" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
small |
|
||||||
> |
|
||||||
<v-icon>mdi-playlist-play</v-icon> M3U |
|
||||||
</v-btn> |
|
||||||
</template> |
|
||||||
<span>{{ $t('tool-bar.click-to-copy') }}</span> |
|
||||||
</v-tooltip> |
|
||||||
|
|
||||||
<v-tooltip bottom> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-btn |
|
||||||
class="ma-2" |
|
||||||
outlined |
|
||||||
color="primary" |
|
||||||
@click="copyTextToClipboard(navBarURLs.xmlURL)" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
small |
|
||||||
> |
|
||||||
<v-icon>mdi-xml</v-icon> XML |
|
||||||
</v-btn> |
|
||||||
</template> |
|
||||||
<span>{{ $t('tool-bar.click-to-copy') }}</span> |
|
||||||
</v-tooltip> |
|
||||||
|
|
||||||
<v-btn |
|
||||||
icon |
|
||||||
color="secondary" |
|
||||||
:href="navBarURLs.documentationURL" |
|
||||||
target="_blank" |
|
||||||
> |
|
||||||
<v-icon>mdi-file-document</v-icon> |
|
||||||
</v-btn> |
|
||||||
|
|
||||||
<v-btn |
|
||||||
icon |
|
||||||
color="secondary" |
|
||||||
:href="navBarURLs.discordURL" |
|
||||||
target="_blank" |
|
||||||
> |
|
||||||
<v-icon>mdi-discord</v-icon> |
|
||||||
</v-btn> |
|
||||||
|
|
||||||
<v-btn |
|
||||||
icon |
|
||||||
color="secondary" |
|
||||||
:href="navBarURLs.githubURL" |
|
||||||
target="_blank" |
|
||||||
> |
|
||||||
<v-icon>mdi-github</v-icon> |
|
||||||
</v-btn> |
|
||||||
</span> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { applicationState } from '@/stores/applicationState'; |
|
||||||
import { snackbarState } from '@/stores/snackbarState'; |
|
||||||
|
|
||||||
export default { |
|
||||||
name: 'ToolBarLinks.vue', |
|
||||||
data: () => ({ |
|
||||||
toast: false |
|
||||||
}), |
|
||||||
computed: { |
|
||||||
...mapState(applicationState, ['navBarURLs']), |
|
||||||
...mapState(snackbarState, ['showSnackbar']) |
|
||||||
}, |
|
||||||
methods: { |
|
||||||
copyTextToClipboard(text) { |
|
||||||
try { |
|
||||||
navigator.clipboard.writeText(text); |
|
||||||
this.showSnackbar(this.$t('tool-bar.copy-success')); |
|
||||||
} catch (error) { |
|
||||||
console.error(error); |
|
||||||
this.showSnackbar( |
|
||||||
this.$t('tool-bar.copy-failure', { message: error }) |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped></style> |
|
@ -1,18 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-text-field |
|
||||||
outlined |
|
||||||
class="ml-5" |
|
||||||
:label="$t('tool-bar.search')" |
|
||||||
prepend-inner-icon="mdi-magnify" |
|
||||||
hide-details |
|
||||||
dense |
|
||||||
></v-text-field> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default { |
|
||||||
name: 'ToolBarSearch' |
|
||||||
}; |
|
||||||
</script> |
|
||||||
|
|
||||||
<style scoped></style> |
|
@ -1,35 +0,0 @@ |
|||||||
<template> |
|
||||||
<v-snackbar v-model="snackbar" :timeout="timeout"> |
|
||||||
{{ currentMessage }} |
|
||||||
<v-btn text color="primary" @click.native="closeSnackbar()" |
|
||||||
>Close</v-btn |
|
||||||
> |
|
||||||
</v-snackbar> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
import { mapState } from 'pinia'; |
|
||||||
import { snackbarState } from '@/stores/snackbarState'; |
|
||||||
|
|
||||||
export default { |
|
||||||
data: () => ({ |
|
||||||
timeout: 4000 |
|
||||||
}), |
|
||||||
computed: { |
|
||||||
...mapState(snackbarState, [ |
|
||||||
'currentMessage', |
|
||||||
'isVisible', |
|
||||||
'closeSnackbar', |
|
||||||
'openSnackbar' |
|
||||||
]), |
|
||||||
snackbar: { |
|
||||||
get() { |
|
||||||
return this.isVisible; |
|
||||||
}, |
|
||||||
set() { |
|
||||||
this.closeSnackbar(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
</script> |
|
@ -1,206 +0,0 @@ |
|||||||
{ |
|
||||||
"$vuetify": { |
|
||||||
"badge": "Badge", |
|
||||||
"close": "Close", |
|
||||||
"dataIterator": { |
|
||||||
"noResultsText": "No matching records found", |
|
||||||
"loadingText": "Loading items..." |
|
||||||
}, |
|
||||||
"dataTable": { |
|
||||||
"itemsPerPageText": "Rows per page:", |
|
||||||
"ariaLabel": { |
|
||||||
"sortDescending": "Sorted descending.", |
|
||||||
"sortAscending": "Sorted ascending.", |
|
||||||
"sortNone": "Not sorted.", |
|
||||||
"activateNone": "Activate to remove sorting.", |
|
||||||
"activateDescending": "Activate to sort descending.", |
|
||||||
"activateAscending": "Activate to sort ascending." |
|
||||||
}, |
|
||||||
"sortBy": "Sort by" |
|
||||||
}, |
|
||||||
"dataFooter": { |
|
||||||
"itemsPerPageText": "Items per page:", |
|
||||||
"itemsPerPageAll": "All", |
|
||||||
"nextPage": "Next page", |
|
||||||
"prevPage": "Previous page", |
|
||||||
"firstPage": "First page", |
|
||||||
"lastPage": "Last page", |
|
||||||
"pageText": "{0}-{1} of {2}" |
|
||||||
}, |
|
||||||
"datePicker": { |
|
||||||
"itemsSelected": "{0} selected", |
|
||||||
"nextMonthAriaLabel": "Next month", |
|
||||||
"nextYearAriaLabel": "Next year", |
|
||||||
"prevMonthAriaLabel": "Previous month", |
|
||||||
"prevYearAriaLabel": "Previous year" |
|
||||||
}, |
|
||||||
"noDataText": "No data available", |
|
||||||
"carousel": { |
|
||||||
"prev": "Previous visual", |
|
||||||
"next": "Next visual", |
|
||||||
"ariaLabel": { |
|
||||||
"delimiter": "Carousel slide {0} of {1}" |
|
||||||
} |
|
||||||
}, |
|
||||||
"calendar": { |
|
||||||
"moreEvents": "{0} more" |
|
||||||
}, |
|
||||||
"fileInput": { |
|
||||||
"counter": "{0} files", |
|
||||||
"counterSize": "{0} files ({1} in total)" |
|
||||||
}, |
|
||||||
"timePicker": { |
|
||||||
"am": "AM", |
|
||||||
"pm": "PM" |
|
||||||
}, |
|
||||||
"pagination": { |
|
||||||
"ariaLabel": { |
|
||||||
"wrapper": "Pagination Navigation", |
|
||||||
"next": "Next page", |
|
||||||
"previous": "Previous page", |
|
||||||
"page": "Goto Page {0}", |
|
||||||
"currentPage": "Current Page, Page {0}" |
|
||||||
} |
|
||||||
}, |
|
||||||
"rating": { |
|
||||||
"ariaLabel": { |
|
||||||
"icon": "Rating {0} of {1}" |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
"languages-code": { |
|
||||||
"en": "English", |
|
||||||
"pt-br": "Portuguese (Brazil)" |
|
||||||
}, |
|
||||||
"tool-bar": { |
|
||||||
"search": "Search...", |
|
||||||
"click-to-copy": "Click to copy to clipboard!", |
|
||||||
"copy-success": "Successfully copied text to clipboard!", |
|
||||||
"copy-failure": "Failed to copy text to clipboard! Error: {message}" |
|
||||||
}, |
|
||||||
"sidebar": { |
|
||||||
"languages": "Languages", |
|
||||||
"version": "Version" |
|
||||||
}, |
|
||||||
"home": { |
|
||||||
"title": "Home" |
|
||||||
}, |
|
||||||
"ffmpeg-profiles": { |
|
||||||
"title": "FFmpeg Profiles", |
|
||||||
"add-profile": "Add FFmpeg Profile", |
|
||||||
"actions": "Actions", |
|
||||||
"delete-dialog-title": "Are you sure?", |
|
||||||
"delete-dialog-text": "Delete {profileName} FFmpeg Profile?", |
|
||||||
"no": "No", |
|
||||||
"yes": "Yes", |
|
||||||
"profile-deleted": "FFmpeg Profile deleted!", |
|
||||||
"table": { |
|
||||||
"name": "Name", |
|
||||||
"resolution": "Resolution", |
|
||||||
"video": "Video", |
|
||||||
"audio": "Audio" |
|
||||||
} |
|
||||||
}, |
|
||||||
"edit-ffmpeg-profile": { |
|
||||||
"General": "General", |
|
||||||
"Video": "Video", |
|
||||||
"Audio": "Audio", |
|
||||||
"Name": "Name", |
|
||||||
"thread-count": "Thread Count", |
|
||||||
"preferred-resolution": "Preferred Resolution", |
|
||||||
"format": "Format", |
|
||||||
"bitrate": "Bitrate", |
|
||||||
"buffer-size": "Buffer Size", |
|
||||||
"hardware-acceleration": "Hardware Acceleration", |
|
||||||
"vaapi-driver": "VAAPI Driver", |
|
||||||
"vaapi-device": "VAAPI Device", |
|
||||||
"normalize-framerate": "Normalize Framerate", |
|
||||||
"auto-deinterlace-video": "Auto Deinterlace Video", |
|
||||||
"channels": "Channels", |
|
||||||
"sample-rate": "Sample Rate", |
|
||||||
"normalize-loudness": "Normalize Loudness", |
|
||||||
"save-profile": "Save Profile", |
|
||||||
"cancel": "Cancel", |
|
||||||
"help": "Help", |
|
||||||
"add-profile": "Add FFmpeg Profile", |
|
||||||
"edit-profile": "Edit FFmpeg Profile", |
|
||||||
"profile-saved": "FFmpeg Profile Saved!" |
|
||||||
}, |
|
||||||
"watermarks": { |
|
||||||
"title": "Watermarks" |
|
||||||
}, |
|
||||||
"media-sources": { |
|
||||||
"title": "Media Sources", |
|
||||||
"local": { |
|
||||||
"title": "Local" |
|
||||||
}, |
|
||||||
"emby": { |
|
||||||
"title": "Emby" |
|
||||||
}, |
|
||||||
"jellyfin": { |
|
||||||
"title": "Jellyfin" |
|
||||||
}, |
|
||||||
"plex": { |
|
||||||
"title": "Plex" |
|
||||||
} |
|
||||||
}, |
|
||||||
"lists": { |
|
||||||
"title": "Lists", |
|
||||||
"collections": { |
|
||||||
"title": "Collections" |
|
||||||
}, |
|
||||||
"trakt-lists": { |
|
||||||
"title": "Trakt Lists" |
|
||||||
}, |
|
||||||
"filler-presets": { |
|
||||||
"title": "Filler Presets" |
|
||||||
} |
|
||||||
}, |
|
||||||
"schedules": { |
|
||||||
"title": "Schedules" |
|
||||||
}, |
|
||||||
"playouts": { |
|
||||||
"title": "Playouts" |
|
||||||
}, |
|
||||||
"settings": { |
|
||||||
"title": "Settings" |
|
||||||
}, |
|
||||||
"logs": { |
|
||||||
"title": "Logs" |
|
||||||
}, |
|
||||||
"media": { |
|
||||||
"title": "Media", |
|
||||||
"libraries": { |
|
||||||
"title": "Libraries" |
|
||||||
}, |
|
||||||
"trash": { |
|
||||||
"title": "Trash" |
|
||||||
}, |
|
||||||
"tv-shows": { |
|
||||||
"title": "TV Shows" |
|
||||||
}, |
|
||||||
"movies": { |
|
||||||
"title": "Movies" |
|
||||||
}, |
|
||||||
"music-videos": { |
|
||||||
"title": "Music Videos" |
|
||||||
}, |
|
||||||
"other-videos": { |
|
||||||
"title": "Other Videos" |
|
||||||
}, |
|
||||||
"songs": { |
|
||||||
"title": "Songs" |
|
||||||
} |
|
||||||
}, |
|
||||||
"channels": { |
|
||||||
"title": "Channels", |
|
||||||
"table": { |
|
||||||
"number": "Number", |
|
||||||
"logo": "Logo", |
|
||||||
"name": "Name", |
|
||||||
"language": "Language", |
|
||||||
"mode": "Mode", |
|
||||||
"ffmpeg-profile": "FFmpeg Profile" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,207 +0,0 @@ |
|||||||
{ |
|
||||||
"$vuetify": { |
|
||||||
"badge": "Distintivo", |
|
||||||
"close": "Fechar", |
|
||||||
"Actions": "Ações", |
|
||||||
"dataIterator": { |
|
||||||
"noResultsText": "Nenhum dado encontrado", |
|
||||||
"loadingText": "Carregando itens..." |
|
||||||
}, |
|
||||||
"dataTable": { |
|
||||||
"itemsPerPageText": "Linhas por página:", |
|
||||||
"ariaLabel": { |
|
||||||
"sortDescending": "Ordenado decrescente.", |
|
||||||
"sortAscending": "Ordenado crescente.", |
|
||||||
"sortNone": "Não ordenado.", |
|
||||||
"activateNone": "Ative para remover a ordenação.", |
|
||||||
"activateDescending": "Ative para ordenar decrescente.", |
|
||||||
"activateAscending": "Ative para ordenar crescente." |
|
||||||
}, |
|
||||||
"sortBy": "Ordenar por" |
|
||||||
}, |
|
||||||
"dataFooter": { |
|
||||||
"itemsPerPageText": "Itens por página:", |
|
||||||
"itemsPerPageAll": "Todos", |
|
||||||
"nextPage": "Próxima página", |
|
||||||
"prevPage": "Página anterior", |
|
||||||
"firstPage": "Primeira página", |
|
||||||
"lastPage": "Última página", |
|
||||||
"pageText": "{0}-{1} de {2}" |
|
||||||
}, |
|
||||||
"datePicker": { |
|
||||||
"itemsSelected": "{0} selecionado(s)", |
|
||||||
"nextMonthAriaLabel": "Próximo mês", |
|
||||||
"nextYearAriaLabel": "Próximo ano", |
|
||||||
"prevMonthAriaLabel": "Mês anterior", |
|
||||||
"prevYearAriaLabel": "Ano anterior" |
|
||||||
}, |
|
||||||
"noDataText": "Não há dados disponíveis", |
|
||||||
"carousel": { |
|
||||||
"prev": "Visão anterior", |
|
||||||
"next": "Próxima visão", |
|
||||||
"ariaLabel": { |
|
||||||
"delimiter": "Slide {0} de {1} do carrossel" |
|
||||||
} |
|
||||||
}, |
|
||||||
"calendar": { |
|
||||||
"moreEvents": "Mais {0}" |
|
||||||
}, |
|
||||||
"fileInput": { |
|
||||||
"counter": "{0} arquivo(s)", |
|
||||||
"counterSize": "{0} arquivo(s) ({1} no total)" |
|
||||||
}, |
|
||||||
"timePicker": { |
|
||||||
"am": "AM", |
|
||||||
"pm": "PM" |
|
||||||
}, |
|
||||||
"pagination": { |
|
||||||
"ariaLabel": { |
|
||||||
"wrapper": "Navegação de paginação", |
|
||||||
"next": "Próxima página", |
|
||||||
"previous": "Página anterior", |
|
||||||
"page": "Ir à página {0}", |
|
||||||
"currentPage": "Página atual, página {0}" |
|
||||||
} |
|
||||||
}, |
|
||||||
"rating": { |
|
||||||
"ariaLabel": { |
|
||||||
"icon": "Rating {0} de {1}" |
|
||||||
} |
|
||||||
} |
|
||||||
}, |
|
||||||
"languages-code": { |
|
||||||
"en": "Inglês", |
|
||||||
"pt-br": "Português (Brasil)" |
|
||||||
}, |
|
||||||
"tool-bar": { |
|
||||||
"search": "Busca...", |
|
||||||
"click-to-copy": "Clique para copiar para a área de transferência!", |
|
||||||
"copy-success": "Texto copiado com sucesso para a área de transferência!", |
|
||||||
"copy-failure": "Falha ao copiar o texto para a área de transferência! Erro: {message}" |
|
||||||
}, |
|
||||||
"sidebar": { |
|
||||||
"languages": "Línguas", |
|
||||||
"version": "Versão" |
|
||||||
}, |
|
||||||
"home": { |
|
||||||
"title": "Início" |
|
||||||
}, |
|
||||||
"ffmpeg-profiles": { |
|
||||||
"title": "Perfis FFmpeg", |
|
||||||
"add-profile": "Adicionar perfil FFmpeg", |
|
||||||
"actions": "Ações", |
|
||||||
"delete-dialog-title": "Tem certeza?", |
|
||||||
"delete-dialog-text": "Excluir perfil {profileName} do FFmpeg?", |
|
||||||
"no": "Não", |
|
||||||
"yes": "Sim", |
|
||||||
"profile-deleted": "Perfil do FFmpeg excluído!", |
|
||||||
"table": { |
|
||||||
"name": "Nome", |
|
||||||
"resolution": "Resolução", |
|
||||||
"video": "Vídeo", |
|
||||||
"audio": "Áudio" |
|
||||||
} |
|
||||||
}, |
|
||||||
"edit-ffmpeg-profile": { |
|
||||||
"General": "Em geral", |
|
||||||
"Video": "Vídeo", |
|
||||||
"Audio": "Áudio", |
|
||||||
"Name": "Nome", |
|
||||||
"thread-count": "Contagem de fios", |
|
||||||
"preferred-resolution": "Resolução preferencial", |
|
||||||
"format": "Formato", |
|
||||||
"bitrate": "Taxa de bits", |
|
||||||
"buffer-size": "Tamanho do buffer", |
|
||||||
"hardware-acceleration": "Aceleraçao do hardware", |
|
||||||
"vaapi-driver": "Driver VAAPI", |
|
||||||
"vaapi-device": "Dispositivo VAAPI", |
|
||||||
"normalize-framerate": "Normalizar taxa de quadros", |
|
||||||
"auto-deinterlace-video": "Vídeo de desentrelaçamento automático", |
|
||||||
"channels": "Canais", |
|
||||||
"sample-rate": "Taxa de amostragem", |
|
||||||
"normalize-loudness": "Normalizar volume", |
|
||||||
"save-profile": "Salvar perfil", |
|
||||||
"cancel": "Cancelar", |
|
||||||
"help": "Ajuda", |
|
||||||
"add-profile": "Adicionar perfil FFmpeg", |
|
||||||
"edit-profile": "Editar perfil do FFmpeg", |
|
||||||
"profile-saved": "Perfil do FFmpeg salvo!" |
|
||||||
}, |
|
||||||
"watermarks": { |
|
||||||
"title": "Marcas d'água" |
|
||||||
}, |
|
||||||
"media-sources": { |
|
||||||
"title": "Fontes de mídia", |
|
||||||
"local": { |
|
||||||
"title": "Local" |
|
||||||
}, |
|
||||||
"emby": { |
|
||||||
"title": "Emby" |
|
||||||
}, |
|
||||||
"jellyfin": { |
|
||||||
"title": "Jellyfin" |
|
||||||
}, |
|
||||||
"plex": { |
|
||||||
"title": "Plex" |
|
||||||
} |
|
||||||
}, |
|
||||||
"lists": { |
|
||||||
"title": "Listas", |
|
||||||
"collections": { |
|
||||||
"title": "Coleções" |
|
||||||
}, |
|
||||||
"trakt-lists": { |
|
||||||
"title": "Listas Trakt" |
|
||||||
}, |
|
||||||
"filler-presets": { |
|
||||||
"title": "Predefinições de preenchimento" |
|
||||||
} |
|
||||||
}, |
|
||||||
"schedules": { |
|
||||||
"title": "Horários" |
|
||||||
}, |
|
||||||
"playouts": { |
|
||||||
"title": "Playouts" |
|
||||||
}, |
|
||||||
"settings": { |
|
||||||
"title": "Definições" |
|
||||||
}, |
|
||||||
"logs": { |
|
||||||
"title": "Logs" |
|
||||||
}, |
|
||||||
"media": { |
|
||||||
"title": "Mídias", |
|
||||||
"libraries": { |
|
||||||
"title": "Bibliotecas" |
|
||||||
}, |
|
||||||
"trash": { |
|
||||||
"title": "Lixo" |
|
||||||
}, |
|
||||||
"tv-shows": { |
|
||||||
"title": "Séries" |
|
||||||
}, |
|
||||||
"movies": { |
|
||||||
"title": "Filmes" |
|
||||||
}, |
|
||||||
"music-videos": { |
|
||||||
"title": "Vídeos de música" |
|
||||||
}, |
|
||||||
"other-videos": { |
|
||||||
"title": "Other Videos" |
|
||||||
}, |
|
||||||
"songs": { |
|
||||||
"title": "Songs" |
|
||||||
} |
|
||||||
}, |
|
||||||
"channels": { |
|
||||||
"title": "Canais", |
|
||||||
"table": { |
|
||||||
"number": "Numero", |
|
||||||
"logo": "Logo", |
|
||||||
"name": "Nome", |
|
||||||
"language": "Idioma", |
|
||||||
"mode": "Modo", |
|
||||||
"ffmpeg-profile": "Perfil FFmpeg" |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
import Vue from 'vue'; |
|
||||||
import App from './App.vue'; |
|
||||||
import vuetify from './plugins/vuetify'; |
|
||||||
import router from './router'; |
|
||||||
import { createPinia, PiniaVuePlugin } from 'pinia'; |
|
||||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; |
|
||||||
import autoPageTitleMixin from './mixins/autoPageTitle'; |
|
||||||
import 'roboto-fontface/css/roboto/roboto-fontface.css'; |
|
||||||
import '@mdi/font/css/materialdesignicons.css'; |
|
||||||
import i18n from './plugins/i18n'; |
|
||||||
import VueSweetalert2 from 'vue-sweetalert2'; |
|
||||||
import 'sweetalert2/dist/sweetalert2.min.css'; |
|
||||||
import './assets/css/global.scss'; |
|
||||||
|
|
||||||
Vue.config.productionTip = false; |
|
||||||
|
|
||||||
Vue.use(PiniaVuePlugin); |
|
||||||
const pinia = createPinia(); |
|
||||||
pinia.use(piniaPluginPersistedstate); |
|
||||||
// Mixin to automate the page title when navigating... Will default to "ErsatzTV if no title value exported from page.
|
|
||||||
Vue.mixin(autoPageTitleMixin); |
|
||||||
|
|
||||||
const options = { |
|
||||||
confirmButtonColor: '#1E1E1E', |
|
||||||
cancelButtonColor: '#1E1E1E', |
|
||||||
background: '#1E1E1E', |
|
||||||
iconColor: '#4CAF50' |
|
||||||
}; |
|
||||||
Vue.use(VueSweetalert2, options); |
|
||||||
|
|
||||||
new Vue({ |
|
||||||
vuetify, |
|
||||||
router, |
|
||||||
pinia, |
|
||||||
i18n, |
|
||||||
render: (h) => h(App) |
|
||||||
}).$mount('#app'); |
|
@ -1,18 +0,0 @@ |
|||||||
function getTitle(vm) { |
|
||||||
const { title } = vm.$options; |
|
||||||
if (title) { |
|
||||||
return typeof title === 'function' ? title.call(vm) : title; |
|
||||||
} |
|
||||||
if (vm._data.title) { |
|
||||||
return vm._data.title; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default { |
|
||||||
created() { |
|
||||||
const title = getTitle(this); |
|
||||||
if (title) { |
|
||||||
document.title = `ErsatzTV | ${title}`; |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
@ -1,6 +0,0 @@ |
|||||||
export interface Channel { |
|
||||||
id: number; |
|
||||||
number: string; |
|
||||||
name: string; |
|
||||||
streamingMode: string; |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
export interface FFmpegFullProfile { |
|
||||||
Id: number; |
|
||||||
name: string; |
|
||||||
threadCount: number; |
|
||||||
hardwareAcceleration: number; |
|
||||||
vaapiDriver: number; |
|
||||||
vaapiDevice: string; |
|
||||||
resolutionId: number; |
|
||||||
videoFormat: number; |
|
||||||
videoBitrate: number; |
|
||||||
videoBufferSize: number; |
|
||||||
audioFormat: number; |
|
||||||
audioBitrate: number; |
|
||||||
audioBufferSize: number; |
|
||||||
normalizeLoudness: boolean; |
|
||||||
audioChannels: number; |
|
||||||
audioSampleRate: number; |
|
||||||
normalizeFramerate: boolean; |
|
||||||
deinterlaceVideo: boolean; |
|
||||||
} |
|
@ -1,7 +0,0 @@ |
|||||||
export interface FFmpegProfile { |
|
||||||
id: number; |
|
||||||
name: string; |
|
||||||
resolution: string; |
|
||||||
video: string; |
|
||||||
audio: string; |
|
||||||
} |
|
@ -1,27 +0,0 @@ |
|||||||
import Vue from 'vue'; |
|
||||||
import VueI18n, { LocaleMessages } from 'vue-i18n'; |
|
||||||
|
|
||||||
Vue.use(VueI18n); |
|
||||||
|
|
||||||
function loadLocaleMessages(): LocaleMessages { |
|
||||||
const locales = require.context( |
|
||||||
'../locales', |
|
||||||
true, |
|
||||||
/[A-Za-z0-9-_,\s]+\.json$/i |
|
||||||
); |
|
||||||
const messages: LocaleMessages = {}; |
|
||||||
locales.keys().forEach((key) => { |
|
||||||
const matched = key.match(/([A-Za-z0-9-_]+)\./i); |
|
||||||
if (matched && matched.length > 1) { |
|
||||||
const locale = matched[1]; |
|
||||||
messages[locale] = locales(key); |
|
||||||
} |
|
||||||
}); |
|
||||||
return messages; |
|
||||||
} |
|
||||||
|
|
||||||
export default new VueI18n({ |
|
||||||
locale: process.env.VUE_APP_I18N_LOCALE || 'en', |
|
||||||
fallbackLocale: process.env.VUE_APP_I18N_FALLBACK_LOCALE || 'en', |
|
||||||
messages: loadLocaleMessages() |
|
||||||
}); |
|
@ -1,33 +0,0 @@ |
|||||||
import Vue from 'vue'; |
|
||||||
import Vuetify from 'vuetify'; |
|
||||||
import colors from 'vuetify/lib/util/colors'; |
|
||||||
import VueI18n from './i18n'; |
|
||||||
|
|
||||||
Vue.use(Vuetify); |
|
||||||
|
|
||||||
export default new Vuetify({ |
|
||||||
icons: { |
|
||||||
iconfont: 'mdi' // default - only for display purposes
|
|
||||||
}, |
|
||||||
theme: { |
|
||||||
themes: { |
|
||||||
dark: { |
|
||||||
primary: '#c0c000', |
|
||||||
secondary: '#00c0c0', |
|
||||||
accent: colors.yellow.accent2, |
|
||||||
error: colors.red, |
|
||||||
warning: colors.orange, |
|
||||||
info: colors.lightBlue, |
|
||||||
success: colors.green, |
|
||||||
background: '#121212' |
|
||||||
} |
|
||||||
}, |
|
||||||
options: { |
|
||||||
customProperties: true |
|
||||||
}, |
|
||||||
dark: true |
|
||||||
}, |
|
||||||
lang: { |
|
||||||
t: (key, ...params) => VueI18n.t(key, params).toString() |
|
||||||
} |
|
||||||
}); |
|
@ -1,252 +0,0 @@ |
|||||||
import Vue from 'vue'; |
|
||||||
import VueRouter from 'vue-router'; |
|
||||||
import HomePage from '../views/HomePage.vue'; |
|
||||||
import ChannelsPage from '../views/ChannelsPage.vue'; |
|
||||||
import FFmpegProfilesPage from '../views/FFmpegProfilesPage.vue'; |
|
||||||
import AddEditFFmpegProfilePage from '../views/AddEditFFmpegProfilePage.vue'; |
|
||||||
|
|
||||||
Vue.use(VueRouter); |
|
||||||
|
|
||||||
const routes = [ |
|
||||||
{ |
|
||||||
path: '/', |
|
||||||
name: 'home.title', |
|
||||||
component: HomePage, |
|
||||||
meta: { |
|
||||||
icon: 'mdi-home', |
|
||||||
disabled: false |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/channels', |
|
||||||
name: 'channels.title', |
|
||||||
component: ChannelsPage, |
|
||||||
meta: { |
|
||||||
icon: 'mdi-broadcast', |
|
||||||
disabled: false |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/ffmpeg-profiles', |
|
||||||
name: 'ffmpeg-profiles.title', |
|
||||||
component: FFmpegProfilesPage, |
|
||||||
meta: { |
|
||||||
icon: 'mdi-video-input-component', |
|
||||||
disabled: false |
|
||||||
}, |
|
||||||
showchildren: false |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/watermarks', |
|
||||||
name: 'watermarks.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-watermark', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/sources', |
|
||||||
name: 'media-sources.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-server-network', |
|
||||||
disabled: false |
|
||||||
}, |
|
||||||
showchildren: true, |
|
||||||
children: [ |
|
||||||
{ |
|
||||||
path: '/sources/local', |
|
||||||
name: 'media-sources.local.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-folder', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/sources/emby', |
|
||||||
name: 'media-sources.emby.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-emby', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/sources/jellyfin', |
|
||||||
name: 'media-sources.jellyfin.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-jellyfish', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/sources/plex', |
|
||||||
name: 'media-sources.plex.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-plex', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media', |
|
||||||
name: 'media.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-cog', |
|
||||||
disabled: false |
|
||||||
}, |
|
||||||
showchildren: true, |
|
||||||
children: [ |
|
||||||
{ |
|
||||||
path: '/media/libraries', |
|
||||||
name: 'media.libraries.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-library', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media/trash', |
|
||||||
name: 'media.trash.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-trash-can', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media/tv-shows', |
|
||||||
name: 'media.tv-shows.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-television-classic', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media/movies', |
|
||||||
name: 'media.movies.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-movie', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media/music-videos', |
|
||||||
name: 'media.music-videos.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-music-circle', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media/other-videos', |
|
||||||
name: 'media.other-videos.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-video', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/media/songs', |
|
||||||
name: 'media.songs.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-album', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/lists', |
|
||||||
name: 'lists.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-format-list-bulleted', |
|
||||||
disabled: false |
|
||||||
}, |
|
||||||
showchildren: true, |
|
||||||
children: [ |
|
||||||
{ |
|
||||||
path: '/lists/collections', |
|
||||||
name: 'lists.collections.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-collage', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/lists/trakt-lists', |
|
||||||
name: 'lists.trakt-lists.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-hammer', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/lists/filler-presets', |
|
||||||
name: 'lists.filler-presets.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-tune-vertical', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
} |
|
||||||
] |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/schedules', |
|
||||||
name: 'schedules.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-calendar', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/playouts', |
|
||||||
name: 'playouts.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-clipboard-play-multiple', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/settings', |
|
||||||
name: 'settings.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-cog', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/Logs', |
|
||||||
name: 'logs.title', |
|
||||||
meta: { |
|
||||||
icon: 'mdi-card-text', |
|
||||||
disabled: true |
|
||||||
} |
|
||||||
}, |
|
||||||
//hidden routes - used for non-menu routes
|
|
||||||
{ |
|
||||||
path: '/add-ffmpeg-profile', |
|
||||||
name: 'add-ffmpeg-profile', |
|
||||||
component: AddEditFFmpegProfilePage, |
|
||||||
meta: { |
|
||||||
disabled: false, |
|
||||||
hidden: true |
|
||||||
} |
|
||||||
}, |
|
||||||
{ |
|
||||||
path: '/edit-ffmpeg-profile', |
|
||||||
name: 'edit-ffmpeg', |
|
||||||
component: AddEditFFmpegProfilePage, |
|
||||||
meta: { |
|
||||||
disabled: false, |
|
||||||
hidden: true |
|
||||||
}, |
|
||||||
props: (route) => ({ query: route.query.id }) |
|
||||||
} |
|
||||||
]; |
|
||||||
|
|
||||||
const router = new VueRouter({ |
|
||||||
mode: 'history', |
|
||||||
base: process.env.BASE_URL, |
|
||||||
routes |
|
||||||
}); |
|
||||||
|
|
||||||
export default router; |
|
@ -1,72 +0,0 @@ |
|||||||
import type { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; |
|
||||||
import axios from 'axios'; |
|
||||||
|
|
||||||
export function isAxiosError(value: any): value is AxiosError { |
|
||||||
return typeof value?.response === 'object'; |
|
||||||
} |
|
||||||
|
|
||||||
export abstract class AbstractApiService { |
|
||||||
protected readonly http: AxiosInstance; |
|
||||||
|
|
||||||
protected constructor( |
|
||||||
protected readonly path?: string, |
|
||||||
protected readonly baseURL: string = process.env.VUE_APP_ETV_BASE_URL ?? |
|
||||||
'/' |
|
||||||
) { |
|
||||||
if (path) { |
|
||||||
this.baseURL += path; |
|
||||||
} |
|
||||||
this.http = axios.create({ |
|
||||||
baseURL |
|
||||||
// ... further stuff, e.g. `withCredentials: true`
|
|
||||||
}); |
|
||||||
this.http.defaults.headers.common['Accept'] = |
|
||||||
'application/json;charset=UTF-8'; |
|
||||||
this.http.defaults.headers.common['Content-Type'] = |
|
||||||
'application/json;charset=UTF-8'; |
|
||||||
} |
|
||||||
|
|
||||||
protected createParams(record: Record<string, any>): URLSearchParams { |
|
||||||
const params: URLSearchParams = new URLSearchParams(); |
|
||||||
for (const key in record) { |
|
||||||
if (Object.prototype.hasOwnProperty.call(record, key)) { |
|
||||||
const value: any = record[key]; |
|
||||||
if (value !== null && value !== undefined) { |
|
||||||
params.append(key, value); |
|
||||||
} else { |
|
||||||
console.debug( |
|
||||||
`Param key '${key}' was null or undefined and will be ignored` |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return params; |
|
||||||
} |
|
||||||
|
|
||||||
protected handleResponse<T>(response: AxiosResponse<T>): T { |
|
||||||
return response.data; |
|
||||||
} |
|
||||||
|
|
||||||
protected handleError(error: unknown): never { |
|
||||||
if (error instanceof Error) { |
|
||||||
if (isAxiosError(error)) { |
|
||||||
if (error.response) { |
|
||||||
console.log(error.response.data); |
|
||||||
console.log(error.response.status); |
|
||||||
console.log(error.response.headers); |
|
||||||
throw error; |
|
||||||
} else if (error.request) { |
|
||||||
// The request was made but no response was received
|
|
||||||
// `error.request` is an instance of XMLHttpRequest in the browser
|
|
||||||
console.log(error.request); |
|
||||||
throw new Error(error as any); |
|
||||||
} |
|
||||||
} else { |
|
||||||
// Something happened in setting up the request that triggered an Error
|
|
||||||
console.log('Error', error.message); |
|
||||||
throw new Error(error.message); |
|
||||||
} |
|
||||||
} |
|
||||||
throw new Error(error as any); |
|
||||||
} |
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
import { AbstractApiService } from './AbstractApiService'; |
|
||||||
import { Channel } from '@/models/Channel'; |
|
||||||
|
|
||||||
class ChannelApiService extends AbstractApiService { |
|
||||||
public constructor() { |
|
||||||
super(); |
|
||||||
} |
|
||||||
|
|
||||||
public getAll(): Promise<Channel[]> { |
|
||||||
return this.http |
|
||||||
.get('/api/channels') |
|
||||||
.then(this.handleResponse.bind(this)) |
|
||||||
.catch(this.handleError.bind(this)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export const channelApiService: ChannelApiService = new ChannelApiService(); |
|
@ -1,114 +0,0 @@ |
|||||||
import { AbstractApiService } from './AbstractApiService'; |
|
||||||
import { FFmpegProfile } from '@/models/FFmpegProfile'; |
|
||||||
import { FFmpegFullProfile } from '../models/FFmpegFullProfile'; |
|
||||||
|
|
||||||
class FFmpegProfileApiService extends AbstractApiService { |
|
||||||
public constructor() { |
|
||||||
super(); |
|
||||||
} |
|
||||||
|
|
||||||
public getAll(): Promise<FFmpegProfile[]> { |
|
||||||
return this.http |
|
||||||
.get('/api/ffmpeg/profiles') |
|
||||||
.then(this.handleResponse.bind(this)) |
|
||||||
.catch(this.handleError.bind(this)); |
|
||||||
} |
|
||||||
|
|
||||||
public getOne(id: string): Promise<FFmpegFullProfile[]> { |
|
||||||
return this.http |
|
||||||
.get('/api/ffmpeg/profiles/' + id) |
|
||||||
.then(this.handleResponse.bind(this)) |
|
||||||
.catch(this.handleError.bind(this)); |
|
||||||
} |
|
||||||
|
|
||||||
public newFFmpegProfile( |
|
||||||
Name: string, |
|
||||||
ThreadCount: number, |
|
||||||
HardwareAcceleration: number, |
|
||||||
VaapiDriver: number, |
|
||||||
VaapiDevice: string, |
|
||||||
ResolutionId: number, |
|
||||||
VideoFormat: number, |
|
||||||
VideoBitrate: number, |
|
||||||
VideoBufferSize: number, |
|
||||||
AudioFormat: number, |
|
||||||
AudioBitrate: number, |
|
||||||
AudioBufferSize: number, |
|
||||||
NormalizeLoudness: boolean, |
|
||||||
AudioChannels: number, |
|
||||||
AudioSampleRate: number, |
|
||||||
NormalizeFramerate: boolean, |
|
||||||
DeinterlaceVideo: boolean |
|
||||||
) { |
|
||||||
const data = { |
|
||||||
Name: Name, |
|
||||||
ThreadCount: ThreadCount, |
|
||||||
HardwareAcceleration: HardwareAcceleration, |
|
||||||
VaapiDriver: VaapiDriver, |
|
||||||
VaapiDevice: VaapiDevice, |
|
||||||
ResolutionId: ResolutionId, |
|
||||||
VideoFormat: VideoFormat, |
|
||||||
VideoBitrate: VideoBitrate, |
|
||||||
VideoBufferSize: VideoBufferSize, |
|
||||||
AudioFormat: AudioFormat, |
|
||||||
AudioBitrate: AudioBitrate, |
|
||||||
AudioBufferSize: AudioBufferSize, |
|
||||||
NormalizeLoudness: NormalizeLoudness, |
|
||||||
AudioChannels: AudioChannels, |
|
||||||
AudioSampleRate: AudioSampleRate, |
|
||||||
NormalizeFramerate: NormalizeFramerate, |
|
||||||
DeinterlaceVideo: DeinterlaceVideo |
|
||||||
}; |
|
||||||
this.http.post('/api/ffmpeg/profiles/new', data); |
|
||||||
} |
|
||||||
|
|
||||||
public updateFFmpegProfile( |
|
||||||
Id: number, |
|
||||||
Name: string, |
|
||||||
ThreadCount: number, |
|
||||||
HardwareAcceleration: number, |
|
||||||
VaapiDriver: number, |
|
||||||
VaapiDevice: string, |
|
||||||
ResolutionId: number, |
|
||||||
VideoFormat: number, |
|
||||||
VideoBitrate: number, |
|
||||||
VideoBufferSize: number, |
|
||||||
AudioFormat: number, |
|
||||||
AudioBitrate: number, |
|
||||||
AudioBufferSize: number, |
|
||||||
NormalizeLoudness: boolean, |
|
||||||
AudioChannels: number, |
|
||||||
AudioSampleRate: number, |
|
||||||
NormalizeFramerate: boolean, |
|
||||||
DeinterlaceVideo: boolean |
|
||||||
) { |
|
||||||
const data = { |
|
||||||
Id: Id, |
|
||||||
Name: Name, |
|
||||||
ThreadCount: ThreadCount, |
|
||||||
HardwareAcceleration: HardwareAcceleration, |
|
||||||
VaapiDriver: VaapiDriver, |
|
||||||
VaapiDevice: VaapiDevice, |
|
||||||
ResolutionId: ResolutionId, |
|
||||||
VideoFormat: VideoFormat, |
|
||||||
VideoBitrate: VideoBitrate, |
|
||||||
VideoBufferSize: VideoBufferSize, |
|
||||||
AudioFormat: AudioFormat, |
|
||||||
AudioBitrate: AudioBitrate, |
|
||||||
AudioBufferSize: AudioBufferSize, |
|
||||||
NormalizeLoudness: NormalizeLoudness, |
|
||||||
AudioChannels: AudioChannels, |
|
||||||
AudioSampleRate: AudioSampleRate, |
|
||||||
NormalizeFramerate: NormalizeFramerate, |
|
||||||
DeinterlaceVideo: DeinterlaceVideo |
|
||||||
}; |
|
||||||
this.http.put('/api/ffmpeg/profiles/update', data); |
|
||||||
} |
|
||||||
|
|
||||||
public deleteRecord(id: string) { |
|
||||||
this.http.delete('/api/ffmpeg/delete/' + id); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export const ffmpegProfileApiService: FFmpegProfileApiService = |
|
||||||
new FFmpegProfileApiService(); |
|
@ -1,16 +0,0 @@ |
|||||||
import { AbstractApiService } from './AbstractApiService'; |
|
||||||
|
|
||||||
class VersionApiService extends AbstractApiService { |
|
||||||
public constructor() { |
|
||||||
super(); |
|
||||||
} |
|
||||||
|
|
||||||
public version(): Promise<string> { |
|
||||||
return this.http |
|
||||||
.get('/api/version') |
|
||||||
.then(this.handleResponse.bind(this)) |
|
||||||
.catch(this.handleError.bind(this)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export const versionApiService: VersionApiService = new VersionApiService(); |
|
@ -1,11 +0,0 @@ |
|||||||
import Vue, { VNode } from 'vue'; |
|
||||||
|
|
||||||
declare global { |
|
||||||
namespace JSX { |
|
||||||
interface Element extends VNode {} |
|
||||||
interface ElementClass extends Vue {} |
|
||||||
interface IntrinsicElements { |
|
||||||
[elem: string]: any; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,4 +0,0 @@ |
|||||||
declare module '*.vue' { |
|
||||||
import Vue from 'vue'; |
|
||||||
export default Vue; |
|
||||||
} |
|
@ -1,46 +0,0 @@ |
|||||||
import { defineStore } from 'pinia'; |
|
||||||
import { versionApiService } from '@/services/VersionService'; |
|
||||||
|
|
||||||
const originURL = `${window.location.origin}`; |
|
||||||
|
|
||||||
export const applicationState = defineStore('appState', { |
|
||||||
state: () => { |
|
||||||
return { |
|
||||||
miniMenu: false, |
|
||||||
currentVersion: 'unknown', |
|
||||||
m3uURL: originURL + '/iptv/channels.m3u', // this will need to be fixed for reverse proxies
|
|
||||||
xmlURL: originURL + '/iptv/xmltv.xml', // this will need to be fixed for reverse proxies
|
|
||||||
documentationURL: 'https://ersatztv.org/', |
|
||||||
githubURL: 'https://github.com/ErsatzTV/ErsatzTV', |
|
||||||
discordURL: 'https://discord.gg/hHaJm3yGy6' |
|
||||||
}; |
|
||||||
}, |
|
||||||
getters: { |
|
||||||
isNavigationMini(state) { |
|
||||||
return state.miniMenu; |
|
||||||
}, |
|
||||||
currentServerVersion(state) { |
|
||||||
return state.currentVersion; |
|
||||||
}, |
|
||||||
navBarURLs(state) { |
|
||||||
return { |
|
||||||
m3uURL: state.m3uURL, |
|
||||||
xmlURL: state.xmlURL, |
|
||||||
documentationURL: state.documentationURL, |
|
||||||
githubURL: state.githubURL, |
|
||||||
discordURL: state.discordURL |
|
||||||
}; |
|
||||||
} |
|
||||||
}, |
|
||||||
actions: { |
|
||||||
toggleMiniNavigation() { |
|
||||||
this.miniMenu = !this.miniMenu; |
|
||||||
}, |
|
||||||
disableMiniNavigation() { |
|
||||||
this.miniMenu = false; |
|
||||||
}, |
|
||||||
async getVersion() { |
|
||||||
this.currentVersion = await versionApiService.version(); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
@ -1,28 +0,0 @@ |
|||||||
import { defineStore } from 'pinia'; |
|
||||||
import VueI18n from '../plugins/i18n'; |
|
||||||
|
|
||||||
export const languageState = defineStore('languageState', { |
|
||||||
state: () => { |
|
||||||
return { |
|
||||||
language: 'en' |
|
||||||
}; |
|
||||||
}, |
|
||||||
getters: { |
|
||||||
currentLanguageCode: (state) => { |
|
||||||
return state.language; |
|
||||||
} |
|
||||||
}, |
|
||||||
actions: { |
|
||||||
setLanguage(languageCode) { |
|
||||||
this.language = languageCode; |
|
||||||
VueI18n.locale = languageCode; |
|
||||||
} |
|
||||||
}, |
|
||||||
persist: { |
|
||||||
afterRestore: (context) => { |
|
||||||
if (context.store.language) { |
|
||||||
VueI18n.locale = context.store.language; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
@ -1,27 +0,0 @@ |
|||||||
import { defineStore } from 'pinia'; |
|
||||||
|
|
||||||
export const snackbarState = defineStore('snackbarState', { |
|
||||||
state: () => { |
|
||||||
return { |
|
||||||
visible: false, |
|
||||||
message: '' |
|
||||||
}; |
|
||||||
}, |
|
||||||
getters: { |
|
||||||
isVisible(state) { |
|
||||||
return state.visible; |
|
||||||
}, |
|
||||||
currentMessage(state) { |
|
||||||
return state.message; |
|
||||||
} |
|
||||||
}, |
|
||||||
actions: { |
|
||||||
showSnackbar(message) { |
|
||||||
this.message = message; |
|
||||||
this.visible = true; |
|
||||||
}, |
|
||||||
closeSnackbar() { |
|
||||||
this.visible = false; |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
@ -1,909 +0,0 @@ |
|||||||
<template> |
|
||||||
<div id="AddEditFFmpegProfile"> |
|
||||||
<h1 id="title" class="mx-4" /> |
|
||||||
<v-divider color="success" class="ma-2"></v-divider> |
|
||||||
<v-app> |
|
||||||
<v-form |
|
||||||
ref="form" |
|
||||||
v-model="isFormValid" |
|
||||||
id="ffmpegForm" |
|
||||||
lazy-validation |
|
||||||
> |
|
||||||
<v-container> |
|
||||||
<v-row justify="center"> |
|
||||||
<v-flex shrink class="pb-10"> |
|
||||||
<v-container style="max-height: 80px"> |
|
||||||
<v-row justify="center"> |
|
||||||
<h2 class="mx"> |
|
||||||
{{ $t('edit-ffmpeg-profile.General') }} |
|
||||||
</h2> |
|
||||||
</v-row> |
|
||||||
</v-container> |
|
||||||
<v-container style="max-height: 80px"> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.name" |
|
||||||
:rules="textRules" |
|
||||||
:label="$t('edit-ffmpeg-profile.Name')" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
ref="myThreadCount" |
|
||||||
v-model="threadCount" |
|
||||||
:label=" |
|
||||||
$t('edit-ffmpeg-profile.thread-count') |
|
||||||
" |
|
||||||
:rules="validInt" |
|
||||||
number |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="threadCountShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container style="max-height: 40px"> |
|
||||||
<v-select |
|
||||||
v-model="selectedResolution" |
|
||||||
@change="preferredResolutionChange" |
|
||||||
:items="preferredResolutions" |
|
||||||
item-value="id" |
|
||||||
item-text="name" |
|
||||||
:label=" |
|
||||||
$t( |
|
||||||
'edit-ffmpeg-profile.preferred-resolution' |
|
||||||
) |
|
||||||
" |
|
||||||
required |
|
||||||
></v-select> |
|
||||||
</v-container> |
|
||||||
</v-flex> |
|
||||||
<v-divider |
|
||||||
style="max-height: 500px" |
|
||||||
vertical |
|
||||||
color="grey" |
|
||||||
></v-divider> |
|
||||||
<v-flex shrink class="pb-10"> |
|
||||||
<v-container style="max-height: 80px"> |
|
||||||
<v-row justify="center"> |
|
||||||
<h2 class="mx"> |
|
||||||
{{ $t('edit-ffmpeg-profile.Video') }} |
|
||||||
</h2> |
|
||||||
</v-row> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-select |
|
||||||
v-model="selectedVideoFormat" |
|
||||||
@change="videoFormatChanged" |
|
||||||
:items="videoFormats" |
|
||||||
item-value="id" |
|
||||||
item-text="name" |
|
||||||
id="videoFormatSelector" |
|
||||||
:label="$t('edit-ffmpeg-profile.format')" |
|
||||||
required |
|
||||||
></v-select> |
|
||||||
<v-tooltip v-model="videoFormatShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.videoBitRate" |
|
||||||
:label="$t('edit-ffmpeg-profile.bitrate')" |
|
||||||
:rules="validIntNonZero" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="videoBitRateShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.videoBufferSize" |
|
||||||
:label=" |
|
||||||
$t('edit-ffmpeg-profile.buffer-size') |
|
||||||
" |
|
||||||
:rules="validIntNonZero" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="videoBufferSizeShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-select |
|
||||||
v-model="selectedHardwareAcceleration" |
|
||||||
@change="hardwareAccelerationChanged" |
|
||||||
:items="hardwareAccelerations" |
|
||||||
item-value="id" |
|
||||||
item-text="name" |
|
||||||
:label=" |
|
||||||
$t( |
|
||||||
'edit-ffmpeg-profile.hardware-acceleration' |
|
||||||
) |
|
||||||
" |
|
||||||
required |
|
||||||
></v-select> |
|
||||||
<v-tooltip |
|
||||||
v-model="hardwareAccelerationShow" |
|
||||||
top |
|
||||||
> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-select |
|
||||||
v-model="selectedVaapiDriver" |
|
||||||
@change="vaapiDriverChanged" |
|
||||||
:items="vaapiDrivers" |
|
||||||
:disabled="vaapiDriverDisabled" |
|
||||||
item-value="id" |
|
||||||
item-text="name" |
|
||||||
:label=" |
|
||||||
$t('edit-ffmpeg-profile.vaapi-driver') |
|
||||||
" |
|
||||||
required |
|
||||||
></v-select> |
|
||||||
<v-tooltip v-model="vaapiDriverShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-select |
|
||||||
v-model="selectedVaapiDevice" |
|
||||||
@change="vaapiDeviceChanged" |
|
||||||
:items="vaapiDevices" |
|
||||||
:disabled="vaapiDriverDisabled" |
|
||||||
:label=" |
|
||||||
$t('edit-ffmpeg-profile.vaapi-device') |
|
||||||
" |
|
||||||
required |
|
||||||
></v-select> |
|
||||||
<v-tooltip v-model="vaapiDeviceShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 50px" |
|
||||||
> |
|
||||||
<v-checkbox |
|
||||||
class="mr-2" |
|
||||||
v-model="normalizeFrameRate" |
|
||||||
:label=" |
|
||||||
$t( |
|
||||||
'edit-ffmpeg-profile.normalize-framerate' |
|
||||||
) |
|
||||||
" |
|
||||||
color="green lighten-1" |
|
||||||
required |
|
||||||
></v-checkbox> |
|
||||||
<v-tooltip v-model="normalizeFrameRateShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pt-8" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 50px" |
|
||||||
> |
|
||||||
<v-checkbox |
|
||||||
class="mr-2" |
|
||||||
v-model="autoDeinterlaceVideo" |
|
||||||
:label=" |
|
||||||
$t( |
|
||||||
'edit-ffmpeg-profile.auto-deinterlace-video' |
|
||||||
) |
|
||||||
" |
|
||||||
color="green lighten-1" |
|
||||||
required |
|
||||||
></v-checkbox> |
|
||||||
<v-tooltip |
|
||||||
v-model="autoDeinterlaceVideoShow" |
|
||||||
top |
|
||||||
> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pt-8" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
</v-flex> |
|
||||||
<v-divider |
|
||||||
style="max-height: 500px" |
|
||||||
vertical |
|
||||||
color="grey" |
|
||||||
></v-divider> |
|
||||||
<v-flex shrink class="pb-10"> |
|
||||||
<v-container style="max-height: 80px"> |
|
||||||
<v-row justify="center"> |
|
||||||
<h2 class="mx"> |
|
||||||
{{ $t('edit-ffmpeg-profile.Audio') }} |
|
||||||
</h2> |
|
||||||
</v-row> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-select |
|
||||||
v-model="selectedAudioFormat" |
|
||||||
@change="audioFormatChanged" |
|
||||||
:items="audioFormats" |
|
||||||
item-value="id" |
|
||||||
item-text="name" |
|
||||||
:label="$t('edit-ffmpeg-profile.format')" |
|
||||||
ref="audioFormat" |
|
||||||
required |
|
||||||
></v-select> |
|
||||||
<v-tooltip v-model="audioFormatShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.audioBitRate" |
|
||||||
:label="$t('edit-ffmpeg-profile.bitrate')" |
|
||||||
:rules="validIntNonZero" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="audioBitRateShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.audioBufferSize" |
|
||||||
:label=" |
|
||||||
$t('edit-ffmpeg-profile.buffer-size') |
|
||||||
" |
|
||||||
:rules="validIntNonZero" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="audioBufferSizeShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.channels" |
|
||||||
:label="$t('edit-ffmpeg-profile.channels')" |
|
||||||
:rules="validIntNonZero" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="audioChannelsShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-text-field |
|
||||||
v-model="newProfile.audioSampleRate" |
|
||||||
:label=" |
|
||||||
$t('edit-ffmpeg-profile.sample-rate') |
|
||||||
" |
|
||||||
:rules="validIntNonZero" |
|
||||||
required |
|
||||||
></v-text-field> |
|
||||||
<v-tooltip v-model="audioSampleRateShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-container |
|
||||||
class="d-flex" |
|
||||||
style="max-height: 80px" |
|
||||||
> |
|
||||||
<v-checkbox |
|
||||||
v-model="normalizeLoudness" |
|
||||||
:label=" |
|
||||||
$t( |
|
||||||
'edit-ffmpeg-profile.normalize-loudness' |
|
||||||
) |
|
||||||
" |
|
||||||
color="green lighten-1" |
|
||||||
required |
|
||||||
></v-checkbox> |
|
||||||
<v-tooltip v-model="normalizeLoudnessShow" top> |
|
||||||
<template v-slot:activator="{ on, attrs }"> |
|
||||||
<v-icon |
|
||||||
class="pl-2" |
|
||||||
color="grey" |
|
||||||
v-ripple="false" |
|
||||||
:retain-focus="false" |
|
||||||
v-bind="attrs" |
|
||||||
v-on="on" |
|
||||||
> |
|
||||||
mdi-help-circle-outline |
|
||||||
</v-icon> |
|
||||||
</template> |
|
||||||
<span>Recommended Thread Count: 0</span> |
|
||||||
</v-tooltip> |
|
||||||
</v-container> |
|
||||||
<v-spacer style="height: 80px"></v-spacer> |
|
||||||
<v-container> |
|
||||||
<v-btn |
|
||||||
color="green lighten-1" |
|
||||||
class="ma-2" |
|
||||||
:disabled="!isFormValid" |
|
||||||
@click="saveFFmpegProfile()" |
|
||||||
> |
|
||||||
{{ $t('edit-ffmpeg-profile.save-profile') }} |
|
||||||
</v-btn> |
|
||||||
<v-btn |
|
||||||
color="cancel" |
|
||||||
class="ma-2" |
|
||||||
@click="cancelAdd()" |
|
||||||
> |
|
||||||
{{ $t('edit-ffmpeg-profile.cancel') }} |
|
||||||
</v-btn> |
|
||||||
<v-btn |
|
||||||
color="indigo accent-1" |
|
||||||
class="ma-2" |
|
||||||
@click="cancelAdd()" |
|
||||||
> |
|
||||||
{{ $t('edit-ffmpeg-profile.help') }} |
|
||||||
</v-btn> |
|
||||||
</v-container> |
|
||||||
</v-flex> |
|
||||||
</v-row> |
|
||||||
</v-container> |
|
||||||
</v-form> |
|
||||||
</v-app> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script lang="ts"> |
|
||||||
import { Vue, Component, Watch } from 'vue-property-decorator'; |
|
||||||
import { ffmpegProfileApiService } from '@/services/FFmpegProfileService'; |
|
||||||
|
|
||||||
@Component |
|
||||||
export default class AddEditFFmpegProfile extends Vue { |
|
||||||
//@Name({ default: 'AddEditFFmpegProfile' }) AddEditFFmpegProfile!: string; |
|
||||||
//@Prop({ default: -1 }) private id!: number; |
|
||||||
public newProfile: any = {}; |
|
||||||
private refForm: any = this.$refs.form; |
|
||||||
public isFormValid = false; |
|
||||||
public threadCountShow = false; |
|
||||||
public videoFormatShow = false; |
|
||||||
public videoBitRateShow = false; |
|
||||||
public videoBufferSizeShow = false; |
|
||||||
public hardwareAccelerationShow = false; |
|
||||||
public vaapiDriverShow = false; |
|
||||||
public vaapiDeviceShow = false; |
|
||||||
public normalizeFrameRateShow = false; |
|
||||||
public autoDeinterlaceVideoShow = false; |
|
||||||
public audioFormatShow = false; |
|
||||||
public audioBitRateShow = false; |
|
||||||
public audioBufferSizeShow = false; |
|
||||||
public audioChannelsShow = false; |
|
||||||
public audioSampleRateShow = false; |
|
||||||
public normalizeLoudnessShow = false; |
|
||||||
|
|
||||||
public AddEditFFmpegProfile() { |
|
||||||
console.log('test'); |
|
||||||
} |
|
||||||
|
|
||||||
public audioFormats: [ |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string } |
|
||||||
] = [ |
|
||||||
{ id: 1, name: 'aac' }, |
|
||||||
{ id: 2, name: 'ac3' } |
|
||||||
]; |
|
||||||
|
|
||||||
private _selectedAudioFormat: number = 2; |
|
||||||
public selectedAudioFormat: { id: number; name: string } = { |
|
||||||
id: 2, |
|
||||||
name: 'ac3' |
|
||||||
}; |
|
||||||
|
|
||||||
public preferredResolutions: [ |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string } |
|
||||||
] = [ |
|
||||||
{ id: 0, name: '720x480' }, |
|
||||||
{ id: 1, name: '1280x720' }, |
|
||||||
{ id: 2, name: '1920x1080' }, |
|
||||||
{ id: 3, name: '3840x2160' } |
|
||||||
]; |
|
||||||
|
|
||||||
private _selectedResolution: number = 2; |
|
||||||
public selectedResolution: { id: number; name: string } = { |
|
||||||
id: 2, |
|
||||||
name: '1920x1080' |
|
||||||
}; |
|
||||||
|
|
||||||
public videoFormats: [ |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string } |
|
||||||
] = [ |
|
||||||
{ id: 1, name: 'h264' }, |
|
||||||
{ id: 2, name: 'hevc' }, |
|
||||||
{ id: 3, name: 'mpeg-2' } |
|
||||||
]; |
|
||||||
|
|
||||||
private _selectedVideoFormat: number = 1; |
|
||||||
public selectedVideoFormat: { id: number; name: string } = { |
|
||||||
id: 1, |
|
||||||
name: 'h264' |
|
||||||
}; |
|
||||||
|
|
||||||
public hardwareAccelerations: [ |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string } |
|
||||||
] = [ |
|
||||||
{ id: 0, name: 'None' }, |
|
||||||
{ id: 1, name: 'Qsv' }, |
|
||||||
{ id: 2, name: 'Nvenc' }, |
|
||||||
{ id: 3, name: 'Vaapi' }, |
|
||||||
{ id: 4, name: 'VideoToolbox' } |
|
||||||
]; |
|
||||||
|
|
||||||
private _selectedHardwareAcceleration: number = 0; |
|
||||||
public selectedHardwareAcceleration: { id: number; name: string } = { |
|
||||||
id: 0, |
|
||||||
name: 'None' |
|
||||||
}; |
|
||||||
|
|
||||||
public vaapiDrivers: [ |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string }, |
|
||||||
{ id: number; name: string } |
|
||||||
] = [ |
|
||||||
{ id: 0, name: 'Default' }, |
|
||||||
{ id: 1, name: 'iHD' }, |
|
||||||
{ id: 2, name: 'i965' }, |
|
||||||
{ id: 3, name: 'RadeonSI' }, |
|
||||||
{ id: 4, name: 'Nouveau' } |
|
||||||
]; |
|
||||||
|
|
||||||
public vaapiDriverDisabled = true; |
|
||||||
private _selectedVaapiDriver: number = 0; |
|
||||||
public selectedVaapiDriver: { id: number; name: string } = { |
|
||||||
id: 0, |
|
||||||
name: 'Default' |
|
||||||
}; |
|
||||||
|
|
||||||
public selectedVaapiDevice: string = ''; |
|
||||||
public vaapiDevices: [string, string] = ['', '/dev/dri/renderD128']; |
|
||||||
|
|
||||||
public normalizeFrameRate: boolean = false; |
|
||||||
public autoDeinterlaceVideo: boolean = true; |
|
||||||
public normalizeLoudness: boolean = true; |
|
||||||
|
|
||||||
saveFFmpegProfile() { |
|
||||||
//this means we're adding |
|
||||||
if (isNaN(this.id)) { |
|
||||||
ffmpegProfileApiService.newFFmpegProfile( |
|
||||||
this.newProfile.name, |
|
||||||
this.threadCount, |
|
||||||
this._selectedHardwareAcceleration, |
|
||||||
this._selectedVaapiDriver, |
|
||||||
this.selectedVaapiDevice, |
|
||||||
this._selectedResolution, |
|
||||||
this._selectedVideoFormat, |
|
||||||
this.newProfile.videoBitRate, |
|
||||||
this.newProfile.videoBufferSize, |
|
||||||
this._selectedAudioFormat, |
|
||||||
this.newProfile.audioBitRate, |
|
||||||
this.newProfile.audioBufferSize, |
|
||||||
this.normalizeLoudness, |
|
||||||
this.newProfile.channels, |
|
||||||
this.newProfile.audioSampleRate, |
|
||||||
this.normalizeFrameRate, |
|
||||||
this.autoDeinterlaceVideo |
|
||||||
); |
|
||||||
} else { |
|
||||||
//this means we're editing |
|
||||||
ffmpegProfileApiService.updateFFmpegProfile( |
|
||||||
this.id, |
|
||||||
this.newProfile.name, |
|
||||||
this.threadCount, |
|
||||||
this._selectedHardwareAcceleration, |
|
||||||
this._selectedVaapiDriver, |
|
||||||
this.selectedVaapiDevice, |
|
||||||
this._selectedResolution, |
|
||||||
this._selectedVideoFormat, |
|
||||||
this.newProfile.videoBitRate, |
|
||||||
this.newProfile.videoBufferSize, |
|
||||||
this._selectedAudioFormat, |
|
||||||
this.newProfile.audioBitRate, |
|
||||||
this.newProfile.audioBufferSize, |
|
||||||
this.normalizeLoudness, |
|
||||||
this.newProfile.channels, |
|
||||||
this.newProfile.audioSampleRate, |
|
||||||
this.normalizeFrameRate, |
|
||||||
this.autoDeinterlaceVideo |
|
||||||
); |
|
||||||
} |
|
||||||
const Toast = this.$swal.mixin({ |
|
||||||
toast: true, |
|
||||||
position: 'top-end', |
|
||||||
showConfirmButton: false, |
|
||||||
timer: 3000, |
|
||||||
timerProgressBar: true, |
|
||||||
didOpen: (toast) => { |
|
||||||
toast.addEventListener('mouseenter', this.$swal.stopTimer); |
|
||||||
toast.addEventListener('mouseleave', this.$swal.resumeTimer); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
Toast.fire({ |
|
||||||
icon: 'success', |
|
||||||
title: this.$t('edit-ffmpeg-profile.profile-saved').toString() |
|
||||||
}); |
|
||||||
this.$router.push({ |
|
||||||
name: 'ffmpeg-profiles.title' |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
cancelAdd() { |
|
||||||
this.$router.push({ |
|
||||||
name: 'ffmpeg-profiles.title' |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
//~change events~// |
|
||||||
public audioFormatChanged(selectObj: number) { |
|
||||||
this._selectedAudioFormat = selectObj; |
|
||||||
} |
|
||||||
|
|
||||||
public preferredResolutionChange(selectObj: number) { |
|
||||||
this._selectedResolution = selectObj + 1; |
|
||||||
} |
|
||||||
|
|
||||||
public videoFormatChanged(selectObj: number) { |
|
||||||
this._selectedVideoFormat = selectObj; |
|
||||||
} |
|
||||||
|
|
||||||
public hardwareAccelerationChanged(selectObj: number) { |
|
||||||
this._selectedHardwareAcceleration = selectObj; |
|
||||||
this.applyVaapiValidation(); |
|
||||||
} |
|
||||||
|
|
||||||
public applyVaapiValidation() { |
|
||||||
//If they pick VAAPI as the hardware acceleration, |
|
||||||
//they can now choose a vaapi driver and device. |
|
||||||
//If not, they cannot change the default options. |
|
||||||
if (this._selectedHardwareAcceleration == 3) { |
|
||||||
this.vaapiDriverDisabled = false; |
|
||||||
} else { |
|
||||||
this.vaapiDriverDisabled = true; |
|
||||||
this._selectedVaapiDriver = 0; |
|
||||||
this.selectedVaapiDriver = { id: 0, name: 'Default' }; |
|
||||||
this.selectedVaapiDevice = ''; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public vaapiDriverChanged(selectObj: number) { |
|
||||||
this._selectedVaapiDriver = selectObj; |
|
||||||
} |
|
||||||
|
|
||||||
public vaapiDeviceChanged(selectObj: string) { |
|
||||||
this.selectedVaapiDevice = selectObj; |
|
||||||
} |
|
||||||
|
|
||||||
//~ end change events~// |
|
||||||
|
|
||||||
public threadCount = 0; |
|
||||||
|
|
||||||
get validIntNonZero() { |
|
||||||
return [ |
|
||||||
(v: any) => |
|
||||||
(v && /^[0-9]+$/.test(v)) || this.$t('Must be a valid number.'), |
|
||||||
(v: any) => (v && v > 0) || 'Must be greater than 0.' |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
get validInt() { |
|
||||||
return [ |
|
||||||
(v: any) => |
|
||||||
(v && /^[0-9]+$/.test(v)) || this.$t('Must be a valid number.') |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
get textRules() { |
|
||||||
return [(v: any) => (v && v.length > 0) || 'Value must not be empty.']; |
|
||||||
} |
|
||||||
|
|
||||||
props!: { id: number }; |
|
||||||
@Watch('id', { immediate: true }) async onItemChanged() { |
|
||||||
console.log('ID', this.id); |
|
||||||
this.id = Number(this.$route.query.id) ?? -1; |
|
||||||
await this.loadPage(); |
|
||||||
} |
|
||||||
|
|
||||||
private loaded = false; |
|
||||||
private id = -1; |
|
||||||
|
|
||||||
title: string = 'Modify FFmpeg Profile'; |
|
||||||
|
|
||||||
async loadPage(): Promise<void> { |
|
||||||
if (this.loaded) { |
|
||||||
return; |
|
||||||
} |
|
||||||
var title = document.getElementById('title'); |
|
||||||
if (title === null || title === undefined) { |
|
||||||
//sometimes the element isn't loaded yet, it'll come |
|
||||||
//back to this when it's good to go. So skip for now. |
|
||||||
return; |
|
||||||
} |
|
||||||
if (!isNaN(this.id)) { |
|
||||||
title.innerHTML = this.$t( |
|
||||||
'edit-ffmpeg-profile.edit-profile' |
|
||||||
).toString(); |
|
||||||
var ffmpegFullProfile = await ffmpegProfileApiService.getOne( |
|
||||||
this.id.toString() |
|
||||||
); |
|
||||||
var result = ffmpegFullProfile[0]; |
|
||||||
if (result !== undefined) { |
|
||||||
//We have a profile, let's load it. |
|
||||||
this.threadCount = result.threadCount; |
|
||||||
this.selectedVaapiDevice = result.vaapiDevice; |
|
||||||
this.autoDeinterlaceVideo = result.deinterlaceVideo; |
|
||||||
this.normalizeFrameRate = result.normalizeFramerate; |
|
||||||
this.normalizeLoudness = result.normalizeLoudness; |
|
||||||
this.newProfile = { |
|
||||||
name: result.name, |
|
||||||
videoBitRate: result.videoBitrate, |
|
||||||
videoBufferSize: result.videoBufferSize, |
|
||||||
audioBitRate: result.audioBitrate, |
|
||||||
audioBufferSize: result.audioBufferSize, |
|
||||||
channels: result.audioChannels, |
|
||||||
audioSampleRate: result.audioSampleRate |
|
||||||
}; |
|
||||||
|
|
||||||
this._selectedAudioFormat = result.audioFormat; |
|
||||||
this.selectedAudioFormat = |
|
||||||
this.audioFormats[result.audioFormat - 1]; |
|
||||||
this._selectedVideoFormat = result.videoFormat; |
|
||||||
this.selectedVideoFormat = |
|
||||||
this.videoFormats[result.videoFormat - 1]; |
|
||||||
this._selectedResolution = result.resolutionId; |
|
||||||
this.selectedResolution = |
|
||||||
this.preferredResolutions[result.resolutionId - 1]; |
|
||||||
this._selectedHardwareAcceleration = |
|
||||||
result.hardwareAcceleration; |
|
||||||
this.selectedHardwareAcceleration = |
|
||||||
this.hardwareAccelerations[result.hardwareAcceleration]; |
|
||||||
this._selectedVaapiDriver = result.vaapiDriver; |
|
||||||
this.selectedVaapiDriver = |
|
||||||
this.vaapiDrivers[result.vaapiDriver]; |
|
||||||
this.applyVaapiValidation(); |
|
||||||
this.loaded = true; |
|
||||||
} else { |
|
||||||
//an ID was entered (probably in the URL) that doesn't exist. Let's returnt to the profile list. |
|
||||||
console.log('No ffmpeg profile found for ID: ' + this.id); |
|
||||||
this.$router.push({ |
|
||||||
name: 'ffmpeg-profiles.title' |
|
||||||
}); |
|
||||||
} |
|
||||||
} else { |
|
||||||
//new profile! |
|
||||||
title.innerHTML = this.$t( |
|
||||||
'edit-ffmpeg-profile.add-profile' |
|
||||||
).toString(); |
|
||||||
this._selectedAudioFormat = 2; |
|
||||||
this._selectedResolution = 3; |
|
||||||
this._selectedVideoFormat = 1; |
|
||||||
this._selectedHardwareAcceleration = 0; |
|
||||||
this._selectedVaapiDriver = 0; |
|
||||||
this.selectedVaapiDevice = ''; |
|
||||||
this.newProfile = { |
|
||||||
name: 'New Profile', |
|
||||||
videoBitRate: 2000, |
|
||||||
videoBufferSize: 4000, |
|
||||||
audioBitRate: 192, |
|
||||||
audioBufferSize: 384, |
|
||||||
channels: 2, |
|
||||||
audioSampleRate: 48 |
|
||||||
}; |
|
||||||
this.loaded = true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
@ -1,62 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<v-data-table |
|
||||||
:headers="headers" |
|
||||||
:items="channels" |
|
||||||
:sort-by="['number']" |
|
||||||
class="elevation-1" |
|
||||||
> |
|
||||||
<template v-slot:[`item.actions`]="{ item }"> |
|
||||||
<v-btn icon class="mr-2" @click="editRow(item.id)"> |
|
||||||
<v-icon>mdi-lead-pencil</v-icon> |
|
||||||
</v-btn> |
|
||||||
|
|
||||||
<v-btn icon @click.stop="deleteRow(item.id)"> |
|
||||||
<v-icon>mdi-delete</v-icon></v-btn |
|
||||||
> |
|
||||||
</template> |
|
||||||
</v-data-table> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script lang="ts"> |
|
||||||
import { Vue, Component } from 'vue-property-decorator'; |
|
||||||
import { Channel } from '@/models/Channel'; |
|
||||||
import { channelApiService } from '@/services/ChannelService'; |
|
||||||
|
|
||||||
@Component |
|
||||||
export default class Channels extends Vue { |
|
||||||
private channels: Channel[] = []; |
|
||||||
|
|
||||||
get headers() { |
|
||||||
return [ |
|
||||||
{ text: this.$t('channels.table.number'), value: 'number' }, |
|
||||||
{ text: this.$t('channels.table.logo'), value: 'logo' }, |
|
||||||
{ text: this.$t('channels.table.name'), value: 'name' }, |
|
||||||
{ text: this.$t('channels.table.language'), value: 'language' }, |
|
||||||
{ text: this.$t('channels.table.mode'), value: 'streamingMode' }, |
|
||||||
{ |
|
||||||
text: this.$t('channels.table.ffmpeg-profile'), |
|
||||||
value: 'ffmpegProfile' |
|
||||||
}, |
|
||||||
{ text: 'Actions', value: 'actions', sortable: false } |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
deleteRow(item: any) { |
|
||||||
let index = this.channels.findIndex((it) => it.id === item.id); |
|
||||||
this.channels.splice(index, 1); |
|
||||||
} |
|
||||||
|
|
||||||
editRow(item: any) { |
|
||||||
let index = this.channels.findIndex((it) => it.id === item.id); |
|
||||||
this.channels.splice(index, 1); |
|
||||||
} |
|
||||||
|
|
||||||
title: string = 'Channels'; |
|
||||||
|
|
||||||
async mounted(): Promise<void> { |
|
||||||
this.channels = await channelApiService.getAll(); |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
@ -1,123 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<v-btn color="success" class="ma-4" @click="addRecord()">{{ |
|
||||||
$t('ffmpeg-profiles.add-profile') |
|
||||||
}}</v-btn> |
|
||||||
<v-data-table |
|
||||||
:headers="headers" |
|
||||||
:items="ffmpegProfiles" |
|
||||||
:sort-by="['name']" |
|
||||||
class="elevation-1" |
|
||||||
> |
|
||||||
<template v-slot:[`item.actions`]="{ item }"> |
|
||||||
<v-btn icon class="mr-2" @click="editRow(item.id)"> |
|
||||||
<v-icon>mdi-lead-pencil</v-icon> |
|
||||||
</v-btn> |
|
||||||
|
|
||||||
<v-btn icon @click.stop="deleteRecord(item.id, item.name)"> |
|
||||||
<v-icon>mdi-delete</v-icon></v-btn |
|
||||||
> |
|
||||||
</template> |
|
||||||
</v-data-table> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script lang="ts"> |
|
||||||
import { Vue, Component } from 'vue-property-decorator'; |
|
||||||
import { FFmpegProfile } from '@/models/FFmpegProfile'; |
|
||||||
import { ffmpegProfileApiService } from '@/services/FFmpegProfileService'; |
|
||||||
|
|
||||||
@Component |
|
||||||
export default class FFmpegProfiles extends Vue { |
|
||||||
public ffmpegProfiles: FFmpegProfile[] = []; |
|
||||||
|
|
||||||
get headers() { |
|
||||||
return [ |
|
||||||
{ text: this.$t('ffmpeg-profiles.table.name'), value: 'name' }, |
|
||||||
{ |
|
||||||
text: this.$t('ffmpeg-profiles.table.resolution'), |
|
||||||
value: 'resolution' |
|
||||||
}, |
|
||||||
{ text: this.$t('ffmpeg-profiles.table.video'), value: 'video' }, |
|
||||||
{ text: this.$t('ffmpeg-profiles.table.audio'), value: 'audio' }, |
|
||||||
{ |
|
||||||
text: this.$t('ffmpeg-profiles.actions'), |
|
||||||
value: 'actions', |
|
||||||
sortable: false |
|
||||||
} |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
addRecord() { |
|
||||||
this.$router.push({ |
|
||||||
name: 'add-ffmpeg-profile' |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
deleteRecord(record: any, recordName: any) { |
|
||||||
this.$swal |
|
||||||
.fire({ |
|
||||||
title: this.$t( |
|
||||||
'ffmpeg-profiles.delete-dialog-title' |
|
||||||
).toString(), |
|
||||||
// text: this.$t( |
|
||||||
// 'Delete "' + recordName + '" FFmpeg Profile?' |
|
||||||
// ).toString(), |
|
||||||
text: this.$t('ffmpeg-profiles.delete-dialog-text', { |
|
||||||
profileName: '"' + recordName + '"' |
|
||||||
}).toString(), |
|
||||||
icon: 'warning', |
|
||||||
//iconColor: '#4CAF50', |
|
||||||
showCancelButton: true, |
|
||||||
cancelButtonText: this.$t('ffmpeg-profiles.no').toString(), |
|
||||||
confirmButtonText: this.$t('ffmpeg-profiles.yes').toString() |
|
||||||
}) |
|
||||||
.then((result) => { |
|
||||||
if (result.isConfirmed) { |
|
||||||
let index = this.ffmpegProfiles.findIndex( |
|
||||||
(it) => it.id === record |
|
||||||
); |
|
||||||
this.ffmpegProfiles.splice(index, 1); |
|
||||||
ffmpegProfileApiService.deleteRecord(String(record)); |
|
||||||
const Toast = this.$swal.mixin({ |
|
||||||
toast: true, |
|
||||||
position: 'top-end', |
|
||||||
showConfirmButton: false, |
|
||||||
timer: 3000, |
|
||||||
timerProgressBar: true, |
|
||||||
didOpen: (toast) => { |
|
||||||
toast.addEventListener( |
|
||||||
'mouseenter', |
|
||||||
this.$swal.stopTimer |
|
||||||
); |
|
||||||
toast.addEventListener( |
|
||||||
'mouseleave', |
|
||||||
this.$swal.resumeTimer |
|
||||||
); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
Toast.fire({ |
|
||||||
icon: 'success', |
|
||||||
title: this.$t( |
|
||||||
'ffmpeg-profiles.profile-deleted' |
|
||||||
).toString() |
|
||||||
}); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
editRow(id: any) { |
|
||||||
this.$router.push({ |
|
||||||
name: 'edit-ffmpeg', |
|
||||||
query: { id: id } |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
title: string = 'FFMpeg Profiles'; |
|
||||||
|
|
||||||
async mounted(): Promise<void> { |
|
||||||
this.ffmpegProfiles = await ffmpegProfileApiService.getAll(); |
|
||||||
} |
|
||||||
} |
|
||||||
</script> |
|
@ -1,9 +0,0 @@ |
|||||||
<template> |
|
||||||
<div> |
|
||||||
<h1>{{ $t(this.$route.name) }}</h1> |
|
||||||
</div> |
|
||||||
</template> |
|
||||||
|
|
||||||
<script> |
|
||||||
export default {}; |
|
||||||
</script> |
|
@ -1,44 +0,0 @@ |
|||||||
{ |
|
||||||
"compilerOptions": { |
|
||||||
"target": "esnext", |
|
||||||
"module": "esnext", |
|
||||||
"strict": true, |
|
||||||
"allowJs": true, |
|
||||||
"experimentalDecorators": true, |
|
||||||
"jsx": "preserve", |
|
||||||
"moduleResolution": "node", |
|
||||||
"skipLibCheck": true, |
|
||||||
"esModuleInterop": true, |
|
||||||
"allowSyntheticDefaultImports": true, |
|
||||||
"forceConsistentCasingInFileNames": true, |
|
||||||
"useDefineForClassFields": true, |
|
||||||
"sourceMap": true, |
|
||||||
"baseUrl": ".", |
|
||||||
"types": [ |
|
||||||
"webpack-env", |
|
||||||
"vuetify", |
|
||||||
"vue-sweetalert2" |
|
||||||
], |
|
||||||
"paths": { |
|
||||||
"@/*": [ |
|
||||||
"src/*" |
|
||||||
] |
|
||||||
}, |
|
||||||
"lib": [ |
|
||||||
"esnext", |
|
||||||
"dom", |
|
||||||
"dom.iterable", |
|
||||||
"scripthost" |
|
||||||
] |
|
||||||
}, |
|
||||||
"include": [ |
|
||||||
"src/**/*.ts", |
|
||||||
"src/**/*.tsx", |
|
||||||
"src/**/*.vue", |
|
||||||
"tests/**/*.ts", |
|
||||||
"tests/**/*.tsx" |
|
||||||
], |
|
||||||
"exclude": [ |
|
||||||
"node_modules" |
|
||||||
] |
|
||||||
} |
|
@ -1,21 +0,0 @@ |
|||||||
const { defineConfig } = require('@vue/cli-service'); |
|
||||||
|
|
||||||
module.exports = defineConfig({ |
|
||||||
transpileDependencies: ['vuetify'], |
|
||||||
runtimeCompiler: true, |
|
||||||
|
|
||||||
pwa: { |
|
||||||
name: 'ErsatzTV' |
|
||||||
}, |
|
||||||
publicPath: '/v2/', |
|
||||||
outputDir: '../wwwroot/v2', |
|
||||||
filenameHashing: false, |
|
||||||
pluginOptions: { |
|
||||||
i18n: { |
|
||||||
locale: 'en', |
|
||||||
fallbackLocale: 'en', |
|
||||||
localeDir: 'locales', |
|
||||||
enableInSFC: true |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
Loading…
Reference in new issue