├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── app.vue ├── assets ├── fonts │ ├── DigitalNumbers-Regular.woff │ └── font-license.txt ├── images │ ├── ffplayout-small.png │ └── ffplayout.png └── scss │ └── main.scss ├── components ├── AlertMsg.vue ├── ConfigAdvanced.vue ├── ConfigChannel.vue ├── ConfigPlayout.vue ├── ConfigUser.vue ├── EventStatus.vue ├── GenericModal.vue ├── HeaderMenu.vue ├── MediaBrowser.vue ├── PlayerControl.vue ├── PlaylistGenerator.vue ├── PlaylistTable.vue ├── SvgIcon.vue ├── SystemStats.vue ├── TimePicker.vue └── VideoPlayer.vue ├── composables ├── helper.ts └── variables.ts ├── docs ├── README.md └── images │ ├── config-gui.png │ ├── dasboard.png │ ├── logging.png │ ├── login.png │ ├── media.png │ ├── message.png │ └── player.png ├── error.vue ├── eslint.config.mjs ├── i18n └── locales │ ├── README.md │ ├── de-DE.js │ ├── en-US.js │ ├── pt-BR.js │ └── ru-RU.js ├── layouts └── default.vue ├── middleware └── auth.global.ts ├── nuxt.config.ts ├── package-lock.json ├── package.json ├── pages ├── configure.vue ├── index.vue ├── logging.vue ├── media.vue ├── message.vue └── player.vue ├── plugins ├── dayjs.ts ├── lodash.client.ts ├── multiselect.client.ts ├── sortable.client.ts ├── splitpanes.client.js └── vue-datepicker.client.ts ├── public ├── favicon.ico ├── live │ └── .gitkeep └── robots.txt ├── stores ├── auth.ts ├── config.ts ├── index.ts ├── media.ts └── playlist.ts ├── tailwind.config.ts ├── tsconfig.json └── types └── index.d.ts /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Logging** 27 | If applicable, add logs to help explain your problem. 28 | 29 | **Server (please complete the following information):** 30 | - OS: [e.g. debian] 31 | - Version [e.g. 10] 32 | 33 | **Repo Version (please complete the following information):** 34 | - Version [e.g. v3.0.0] 35 | 36 | **Additional context** 37 | Add any other context about the problem here. 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | .nuxt 4 | .nitro 5 | .cache 6 | .output 7 | .env 8 | dist 9 | .eslintcache 10 | 11 | # Output of 'npm pack' 12 | *.tgz 13 | 14 | # Yarn Integrity file 15 | .yarn-integrity 16 | 17 | # Service worker 18 | sw.* 19 | 20 | # Mac OSX 21 | .DS_Store 22 | 23 | # Vim swap files 24 | *.swp 25 | 26 | master.m3u8 27 | tv-media 28 | tv-media/ 29 | Videos 30 | Videos/ 31 | *.tar* 32 | home 33 | home/ 34 | live1 35 | live1/ 36 | Musik 37 | Musik/ 38 | 39 | test.vue 40 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bradlc.vscode-tailwindcss", 4 | "dbaeumer.vscode-eslint", 5 | "esbenp.prettier-vscode", 6 | "hollowtree.vue-snippets", 7 | "vue.volar", 8 | "wscats.vue", 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.experimental.useFlatConfig": true, 3 | "prettier.tabWidth": 4, 4 | "prettier.printWidth": 120, 5 | "vue3snippets.semi": false, 6 | "vue3snippets.singleQuote": true, 7 | "vue3snippets.jsxSingleQuote": true, 8 | "vue3snippets.printWidth": 120, 9 | "vue3snippets.tabWidth": 4, 10 | "prettier.jsxSingleQuote": true, 11 | "prettier.semi": false, 12 | "prettier.singleQuote": true, 13 | "[css]": { 14 | "editor.defaultFormatter": "esbenp.prettier-vscode" 15 | }, 16 | "[html]": { 17 | "editor.defaultFormatter": "esbenp.prettier-vscode" 18 | }, 19 | "[javascript]": { 20 | "editor.defaultFormatter": "esbenp.prettier-vscode" 21 | }, 22 | "[scss]": { 23 | "editor.defaultFormatter": "esbenp.prettier-vscode" 24 | }, 25 | "[vue]": { 26 | "editor.defaultFormatter": "esbenp.prettier-vscode" 27 | }, 28 | "[typescript]": { 29 | "editor.defaultFormatter": "esbenp.prettier-vscode" 30 | }, 31 | "cSpell.words": [ 32 | "nuxt" 33 | ], 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ffplayout-frontend 2 | ===== 3 | 4 | This web application is used to manage the [ffplayout](https://github.com/ffplayout/ffplayout). 5 | 6 | The interface is mostly designed for 24/7 streaming. Other scenarios like streaming in folder mode or playlists with no start time will work but will not be displayed correctly. 7 | 8 | For a better understanding of the functionality, take a look at the screenshots below. 9 | 10 | ### Login 11 | ![login](/docs/images/login.png) 12 | 13 | ### System Dashboard 14 | ![login](/docs/images/dasboard.png) 15 | 16 | ### Control Page 17 | ![player](/docs/images/player.png) 18 | 19 | ### Media Page 20 | ![media](/docs/images/media.png) 21 | 22 | ### Message Page 23 | ![message](/docs/images/message.png) 24 | 25 | ### Logging Page 26 | ![logging](/docs/images/logging.png) 27 | 28 | ### Configuration Page 29 | ![config-gui](/docs/images/config-gui.png) 30 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 8 | 13 | -------------------------------------------------------------------------------- /assets/fonts/DigitalNumbers-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffplayout/ffplayout-frontend/c897226188cc289b0744bb721691a438dba0f5fd/assets/fonts/DigitalNumbers-Regular.woff -------------------------------------------------------------------------------- /assets/fonts/font-license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Stephan Ahlf (https://github.com/s-a/digital-numbers-font stephan.ahlf@googlemail.com) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/images/ffplayout-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffplayout/ffplayout-frontend/c897226188cc289b0744bb721691a438dba0f5fd/assets/images/ffplayout-small.png -------------------------------------------------------------------------------- /assets/images/ffplayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ffplayout/ffplayout-frontend/c897226188cc289b0744bb721691a438dba0f5fd/assets/images/ffplayout.png -------------------------------------------------------------------------------- /assets/scss/main.scss: -------------------------------------------------------------------------------- 1 | @import 'bootstrap-icons/font/bootstrap-icons.css'; 2 | 3 | #__nuxt, 4 | #__nuxt > div, 5 | main, 6 | main > div { 7 | height: 100%; 8 | width: 100%; 9 | } 10 | 11 | @font-face { 12 | font-family: 'DigitalNumbers'; 13 | src: url('@/assets/fonts/DigitalNumbers-Regular.woff') format('woff'); 14 | font-weight: normal; 15 | font-style: normal; 16 | } 17 | 18 | .splitpanes--horizontal > .splitpanes__splitter { 19 | position: relative; 20 | border-top: 1px solid var(--my-gray); 21 | border-bottom: 1px solid var(--my-gray); 22 | height: 6px !important; 23 | } 24 | 25 | .splitpanes--vertical > .splitpanes__splitter { 26 | position: relative; 27 | border-left: 1px solid var(--my-gray); 28 | border-right: 1px solid var(--my-gray); 29 | width: 6px !important; 30 | } 31 | 32 | .splitpanes--horizontal > .splitpanes__splitter::before { 33 | content: ''; 34 | position: absolute; 35 | background-color: var(--my-gray); 36 | transform: translateY(-50%); 37 | top: 50%; 38 | left: 50%; 39 | height: 2px; 40 | width: 30px; 41 | transition: background-color 0.3s; 42 | margin-left: -1px; 43 | } 44 | 45 | .splitpanes--vertical > .splitpanes__splitter::before { 46 | content: ''; 47 | position: absolute; 48 | background-color: var(--my-gray); 49 | transform: translateY(-50%); 50 | top: 50%; 51 | left: 50%; 52 | width: 2px; 53 | height: 30px; 54 | transition: background-color 0.3s; 55 | margin-left: -1px; 56 | } 57 | 58 | /* ---------------------------------------------------------------------------- 59 | multiselect customization 60 | -----------------------------------------------------------------------------*/ 61 | 62 | .multiselect-caret { 63 | background-image: none !important; 64 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'%3E%3C/path%3E%3C/svg%3E"); 65 | background-position: center; 66 | mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z'%3E%3C/path%3E%3C/svg%3E"); 67 | background-repeat: no-repeat; 68 | -webkit-mask-position: center; 69 | mask-position: center; 70 | -webkit-mask-repeat: no-repeat; 71 | mask-repeat: no-repeat; 72 | -webkit-mask-size: contain; 73 | mask-size: contain; 74 | background-color: var(--my-gray); 75 | opacity: 1 !important; 76 | } 77 | 78 | .multiselect-clear-icon { 79 | background-image: none !important; 80 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'%3E%3C/path%3E%3C/svg%3E"); 81 | mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'%3E%3C/path%3E%3C/svg%3E"); 82 | -webkit-mask-position: center; 83 | mask-position: center; 84 | -webkit-mask-repeat: no-repeat; 85 | mask-repeat: no-repeat; 86 | -webkit-mask-size: contain; 87 | mask-size: contain; 88 | background-color: var(--my-gray); 89 | opacity: 1 !important; 90 | transition: 0.3s; 91 | } 92 | 93 | .bg-multiselect-remove { 94 | background-image: none !important; 95 | -webkit-mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'%3E%3C/path%3E%3C/svg%3E"); 96 | mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'%3E%3C/path%3E%3C/svg%3E"); 97 | -webkit-mask-position: center; 98 | mask-position: center; 99 | -webkit-mask-repeat: no-repeat; 100 | mask-repeat: no-repeat; 101 | -webkit-mask-size: contain; 102 | mask-size: contain; 103 | background-color: var(--my-gray); 104 | opacity: 1 !important; 105 | transition: 0.3s; 106 | } 107 | 108 | .multiselect-tag-remove-icon { 109 | display: inline-block; 110 | height: 0.75rem; 111 | width: 0.75rem; 112 | background-image: none !important; 113 | -webkit-mask-image: url("data:image/svg+xml,%3csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'%3e%3c/path%3e%3c/svg%3e"); 114 | mask-image: url("data:image/svg+xml,%3csvg viewBox='0 0 320 512' fill='currentColor' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z'%3e%3c/path%3e%3c/svg%3e"); 115 | -webkit-mask-position: center; 116 | mask-position: center; 117 | -webkit-mask-repeat: no-repeat; 118 | mask-repeat: no-repeat; 119 | -webkit-mask-size: contain; 120 | mask-size: contain; 121 | background-color: var(--my-gray); 122 | opacity: 1 !important; 123 | transition: 0.3s; 124 | } 125 | -------------------------------------------------------------------------------- /components/AlertMsg.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /components/ConfigAdvanced.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 105 | -------------------------------------------------------------------------------- /components/ConfigChannel.vue: -------------------------------------------------------------------------------- 1 | 104 | 105 | 173 | -------------------------------------------------------------------------------- /components/ConfigPlayout.vue: -------------------------------------------------------------------------------- 1 |