Init WebCG

This commit is contained in:
2026-08-12 14:21:28 +09:00
commit 5c01660f97
170 changed files with 46248 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
+23
View File
@@ -0,0 +1,23 @@
# 가볍고 안정적인 Node.js 18 Alpine 이미지 사용
FROM node:18-alpine
# GitHub 소스를 가져오기 위해 git 설치
RUN apk add --no-cache git
# 컨테이너 내 작업 디렉토리 설정
WORKDIR /app
# 공식 WebCG 저장소 복제 (최신 소스코드)
RUN git clone https://gitea.shworld.uk/shinsh/WebCG.git .
# Node.js 의존성 패키지 설치
RUN npm install
# 데이터 영구 저장을 위한 볼륨 포인트 선언
VOLUME ["/app/ASSETS", "/app/DATAROOT"]
# 기본 웹 서비스 포트 노출
EXPOSE 5656
# 서버 실행
CMD ["node", "server.js"]
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
apps : [
{
'name': 'GC1',
'script': 'server.js',
'args': 'config.json'
},
{
'name': 'GC2',
'script': 'server.js',
'args': 'config2.json'
}
]
};
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "Hoi",
"slogan": "Bediening en beheer van afbeeldingen voor CasparCG en streamingtoepassingen.",
"folder": "Map",
"file": "Bestand",
"up": "Omhoog",
"field": "Veld",
"pcs": "stuks",
"help": "Help",
"home": "Home",
"yes": "Ja",
"no": "Nee",
"none": "None",
"all": "All",
"fromFolder": "van map"
},
"color": {
"red": "Rood",
"green": "Groen",
"grey": "Grijs",
"orange": "Oranje",
"blue": "Blauw",
"black": "Zwart",
"light": "Licht",
"white": "Wit",
"transparent": "Transparent"
},
"button": {
"add": "Nieuw",
"addplaylist": "Maak rundown",
"addRow": "Rij toevoegen",
"back": "◂ Terug",
"cancel": "Annuleren",
"config": "Instellingen applicatie",
"continue": "Verder ►",
"delete": "Wissen",
"deletetemplate": "Verwijder sjabloon",
"deletebutton": "Verwijder button",
"docs": "Documentatie",
"duplicate": "Duplicate",
"help": "Help",
"edit": "Wijzigen",
"edittemplates": "Wijzig sjablonen",
"exploredataroot": "Verken map dataroot",
"exploretemplates": "Verken map sjablonen",
"filterprojects": "Start typing to filter projects...",
"launch": "Projecten",
"overlayon": "FX play",
"overlayoff": "FX syop",
"open": "Open rundown",
"play": "Play ►",
"playmaster": "Play ► spatiebalk",
"rename": "Rename",
"replacetemplate": "Re-import",
"selectemplate": "Kiezen",
"select": "Open project",
"settings": "Projectinstellingen",
"shortcuts": "Sneltoetsen",
"showwebrenderer": "Open web playout venster",
"stop": "Stop ■",
"stopmaster": "Stop ◄ spatieblak",
"update": "Bijwerken ► F5",
"save": "Opslaan"
},
"headline": {
"edit": "Wijzig rundown",
"select": "Kies een project",
"selectfile": "Kies een rundown",
"createrundowntip": "Create a new rundown to this project"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "Configureer gebruikerstoegang",
"home": "Welkom",
"login": "Inloggen",
"pickAllTemplates": "Voeg alle sjablonen toe aan het overzicht",
"picktemplate": "Voeg sjabloon toe aan rundown",
"selectshow": "Kies een project",
"select": "Kies een rundown",
"selectshowextra": "Voeg een extra functie toe aan het project",
"selectemplate": "Kies een sjabloon",
"selectfile": "Choose a file",
"shortcuts": "Sneltoeten",
"controller": "Controller",
"configapp": "Instellingen applicatie",
"configshow": "Projectinstellingen",
"shows": "Projecten",
"f1ShortcutTip" : "Dit helpscherm",
"upDownShortcutTip" : "Focus op vorig / volgend item",
"homeEndShortcutTip" : "Focus op eerste / laatste item",
"enterShortcutTip" : "Open / sluit gefocust item om te bewerken",
"spaceShortcutTip" : "Afspelen / doorgaan / stoppen van gefocust playlistitem",
"tipContinue" : "Ondersteuning voor het next-commando moet in de template worden geïmplementeerd om continue te gebruiken",
"ctrlDShortcutTip" : "Dupliceer een template-item",
"shiftSpaceShortcutTip" : "Afspelen / stoppen van gefocust playlistitem",
"f5ShortcutTip" : "Stuur een directe update naar de template (om bijvoorbeeld typfouten te corrigeren).",
"supportUpdateCommandTip" : "Ondersteuning voor het update-commando moet in de template worden geïmplementeerd.",
"capsShortcutTip" : "Toon item-overlays voor snelle info en om API-commando's snel te kopiëren.",
"escShortcutTip": "Stop alle lagen die door de huidige rundown worden gebruikt."
},
"config": {
"addnewtip": "Voeg nieuw item toe door invullen en opslaan",
"addfirsttemplate": "Klik + om een sjabloon toe te voegen aan dit project",
"addfirstextra": "Klik + om een extra functie toe te voegen aan dit project",
"audioplayer": "Audiospeler",
"authpolicy": "Configuratie gbruikerstoegang. Gebruikersnaam en wachtwoord gebruiken?",
"authinfo": "Dit wordt getoond als er een gebruiksnaam is ingevoerd zoner een wachtwoord in het config-bestand.",
"authnone": "Gebruikersnaam of wachtwoord zijn niet vereist voor de applicatie.",
"backgroundImage": "Static background image (usually transparent)",
"playerflags": "Instellingen player",
"buttoncolor": "Buttonkleur",
"buttontext": "Buttontekst",
"buttontexts": "Buttonwteksten",
"buttontype": "Buttontype",
"casparcg": "CasparCG instellingen",
"ccgtemplates": "sjabloonmap (casparcg.config)",
"ccgplayout": "CasparCG playout",
"ccgservers": "CasparCG servers",
"coloraccent": "Accentkleur",
"dataformat": "Gegevensformaat",
"dataroot": "Gegevensmap",
"defaultvalue": "Standaardwaarde",
"description": "Beschrijving",
"extrabutton": "Extra button",
"extrabuttondescription": "Eenvoudige button die een functie uitvoert",
"extratoggle": "Toggle button",
"extratoggledescription": "Button voor aparte aan- en uit status",
"extraselectbutton": "Selectie keuzelijst met een button",
"extraselectdescription": "Keuzelijst met verschillende opties",
"functionlibrary": "Functiebibliotheek (/js/myFunctions.js)",
"functiondescription": "Beschrijving functie",
"functionhandler": "Event handler (functie)",
"general": "Algemene instellingen",
"globalbuttons": "Extra functies",
"inputfields": "Sjabloonvelden",
"javascriptfile": "Javascript-bestand (/js/...)",
"language": "Taal gebruikersinterface",
"logfolder": "Logmap",
"loglevel": "Logniveau",
"openchrome": "Open Chrome bij herstarten",
"options": "Opties keuzelijst (tekst, waarde)",
"optionalBgImage": "Optional background image from folder",
"restartinfo": "SPX moet opnieuw worden gestart om de wijzigingen door te voeren.",
"resolution": "Resolution",
"resolutioninfo": "Please note, templates are designed for HD by default and may need changes to support other resolutions.",
"serverport": "Poort",
"showextras": "Extra functies project",
"stopmode": "Stop animatie",
"templatefile": "Sjabloonbestand",
"templates": "Aan dit project gekoppelde sjablonen",
"templatepathinfo": "Let op, sjablonen moeten beschikbaar zijn voor zowel CasparCG- als SPX-applicaties. Configureer dit als de CasparCG-sjabloonmap of kopieer indien nodig sjabloonbestanden naar beide toepassingen.",
"templateroot": "HTML-sjablonenmap",
"text": "Zichtbare tekst",
"updated": "Updated",
"value": "Waarde",
"webplayout": "Web playout laag",
"webplayurl": "Browser URL",
"preview": "Preview action",
"projectvariables": "Project variables",
"autoplay": "Autoplay active layers on load"
},
"error": {
"fileExisted": "Bestand bestaat, probeer opnieuw aub",
"preventBrowseUp": "Dit is de basissjabloonmap.",
"templateDefinitionMissing": "SPXGCTemplateDefinition{} ontbreekt in het gekozen bestand. Raadpleeg het onderdeel sjablonen in de documentatie."
},
"warning": {
"notemplates": "There are no templates linked to this project yet",
"showprofilebackup": "Het wordt aangeraden om een reserverkopie van het profielbestand te maken voor wijzigingen aan te brengen.",
"severalControllers": "Er zijn meerdere controllers verbonden met de SPX-server! Slechts één aanbevolen."
},
"hover": {
"accent": "Accentkleur voor rundowns in sjabloon",
"addrundownitem": "Voeg een item to aan rundown",
"addshow": "Maak een nieuw project",
"addrundown": "Voeg nieuwe rundown toe aan project",
"addccgserver": "Voor nieuwe CasparCG-server toe door NAME, IP en PORT-gegevens toe te voegen",
"buttontype": "Extra functietype (knop, schakelknop ...)",
"browsetemplate": "Zoek een nieuw sjabloon om aan dit project te koppelen",
"browseextras": "Zoek een nieuwe functie om aan dit project te koppelen",
"browseglobalextras": "Voeg een nieuwe globale functie toe",
"cancelconfig": "Sluit config zonder de wijzigingen op te slaan",
"ccgserver": "CasparCG servernaam in SPX",
"ccgchannel": "CasparCG playout-kanaal",
"ccglayer": "CasparCG playout-laag",
"ccgtemplatepath": "Waarde sjabloonmap van casparcg.config-bestand",
"ccghost": "CasparCG server netwerk naam of IP-adres",
"ccgport": "CasparCG serverpoort (meestal 5250)",
"colors": "De standaardkleur van de functietoets in de gebruikersinterface",
"config": "Open applicatie-configuratie",
"confitem": "Sjablooninstellingen",
"confjsfile": "Javascript bibliotheek en algemene extra functies",
"dataformat": "In welk formaat worden de gegevens naar het sjabloon gestuurd?",
"dataroot": "Ggevensmap om project- en rundownbestanden op te slaan?",
"defaultvalue": "De standaardwaarde moet een van de opties zijn",
"delete": "Verwijder item",
"deletethis": "Verwijder dit item uit de rundown",
"deletepisode": "Wis gekozen rundown met alle items",
"deleteshow": "Wis gekozen projct met instellingen en alle rundowns",
"docs": "Open documentatie",
"duplicateitem": "Kopieer dit item",
"duplicaterundown": "Maakt een kopie van de gekozen rundown",
"epslist": "Kies of dubbelklik een rundown om te openen",
"extradescr": "Beschrijving extra functie",
"fcall": "Event handler functie in functiebibliotheek (bijvoorbeeld helloWorld)",
"fields": "Gescande SPXGCTemplateDefinition velden",
"github": "Open de SPX project pagina in GitHub",
"help": "Open help over dit onderdeel",
"htmlfolder": "Absoluut pad naar map met CasparCG-sjablonen?",
"item": "dubbelklik (of Enter) om item editor te openen of sluiten. Sleep om items te sorteren.",
"lang": "Taal gebruikersinterface? (U kunt desgewenst nieuwe taalbestanden toevoegen aan de map 'locales')",
"logfolder": "Map voor logbestanden",
"loglevel": "Logging lvel? Standaardlevel: info. Meste loginformatie: debug.",
"masterplay": "Play / Stop gekozen item (spatiebalk)",
"masterupdate": "Update teksten van item (F5)",
"mastercontinue": "Ga verder met volgende stap animatie (Shift + Spatiebalk)",
"navishows": "Projecten",
"navishow": "Rundowns",
"newversavailable": "Er is een nieuwe versie. Lees hier meer.",
"openbrowser": "Moet Chrome starten als de server start?",
"openrundown": "Open gekozen rundown in controller",
"options": "Instellingen keuzelijst",
"outmode": "Eindanimatie van de afbeelding",
"port": "Poort om de applicatie te gebruiken? (Bijvoorbeeld 3000 5000 ...) Let op: DIT PAGINA-ADRES zal ook veranderen!",
"profile": "Open instellingen van dit project",
"playthis": "Speel of stop dit item",
"playouturl": "Lokaal webplayout-adres gegenereerd door GC voor grafische afbeeldingen",
"removeglobalextra": "Verwijder deze button uit alle projecten",
"removetemplate": "Verwijder dit sjabloon uit dit project",
"remoteplayouturl": "Webrender geïnstalleerd op een externe server. (Let op dat machines openbare IP-adressen moeten hebben.)",
"replacetemplate": "Re-import template if changes made to source code.",
"resolution": "Output resolution. The default is HD (1920x1080).",
"saveconfig": "Sla instelingen op in een config bestand",
"saveprofile": "Opslaan projectinstellingen",
"showjsfile": "Javascript functiebibliotheek van dit projeect",
"showlist": "Kies of dubbelklik project om te openen",
"submitthis": "Opslaan en editor afsluiten",
"templatepath": "Relatief bestandspad HTML-sjablonen",
"templatedescr": "Korte beschrijving van het item for de GUI",
"text": "Buttontekst",
"text1": "Buttontekst als actief",
"updated": "Timestamp when template was last imported",
"updatethis": "Update dit item",
"weblayer": "Webplayout laag",
"preview": "Control when graphics are rendered to the preview channel",
"exportitem": "Create a CSV file from fields of this template ",
"importitems": "Create rundown items from a CSV file",
"renameepisode": "Rename selected rundown"
}
}
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "Howdy",
"slogan": "Manage and control graphics for live broadcast, live streaming and live events.",
"folder": "Folder",
"file": "File",
"up": "Up",
"field": "Field",
"pcs": "pcs",
"help": "Help",
"home": "Home",
"yes": "Yes",
"no": "No",
"none": "None",
"all": "All",
"fromFolder": "from folder"
},
"color": {
"red": "Red",
"green": "Green",
"grey": "Gray",
"orange": "Orange",
"blue": "Blue",
"black": "Black",
"light": "Light",
"white": "White",
"transparent": "Transparent"
},
"button": {
"add": "New",
"addplaylist": "Add rundown",
"addRow": "Add row",
"back": "◂ Back",
"cancel": "Cancel",
"config": "Application config",
"continue": "Continue ►",
"delete": "Delete",
"deletetemplate": "Remove template",
"deletebutton": "Remove button",
"docs": "Documentation",
"duplicate": "Duplicate",
"help": "Help",
"edit": "Edit",
"edittemplates": "Edit templates",
"exploredataroot": "Explore dataroot folder",
"exploretemplates": "Explore templates folder",
"filterprojects": "Start typing to filter projects...",
"launch": "Projects",
"overlayon": "FX PLAY",
"overlayoff": "FX STOP",
"open": "Open rundown",
"play": "Play ►",
"playmaster": "Play ►",
"rename": "Rename",
"replacetemplate": "Re-import",
"selectemplate": "Select",
"select": "Open project",
"settings": "Project settings",
"shortcuts": "Keyboard shortcuts",
"showwebrenderer": "Open output window...",
"stop": "Stop ■",
"stopmaster": "Stop ■",
"update": "Update ► F5",
"save": "Save"
},
"headline": {
"createrundowntip": "Create a new rundown to this project",
"edit": "Edit rundown",
"select": "Choose a project",
"selectfile": "Choose a rundown"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "Configure user access",
"home": "Welcome",
"login": "Login",
"pickAllTemplates": "Add all templates to the rundown",
"picktemplate": "Add template to rundown",
"selectshow": "Choose a project",
"select": "Choose a rundown",
"selectshowextra": "Add extra control to the project",
"selectemplate": "Choose a template",
"selectfile": "Choose a file",
"shortcuts": "Keyboard shortcuts",
"controller": "Controller",
"configapp": "Application configuration",
"configshow": "Project settings",
"shows": "Projects",
"f1ShortcutTip" : "This help screen",
"upDownShortcutTip" : "Focus previous / next item",
"homeEndShortcutTip" : "Focus first / last item",
"enterShortcutTip" : "Open / close focused item for editing",
"spaceShortcutTip" : "Play / continue / stop focused playlist item",
"tipContinue" : "Support for next -command must be implemented in the template",
"ctrlDShortcutTip" : "Duplicate a template item",
"shiftSpaceShortcutTip" : "Play / stop focused playlist item",
"f5ShortcutTip" : "Send an instant update to the template (to fix typos, for instance).",
"supportUpdateCommandTip" : "Support for update command must be implemented in the template.",
"capsShortcutTip" : "Show item overlays for quick info and for copying API commands quickly.",
"escShortcutTip": "Stop all layers used by current rundown."
},
"config": {
"addnewtip": "Add new item by filling in and saving",
"addfirsttemplate": "Click + to add a template to this project",
"addfirstextra": "Click + to add an extra control to this project",
"audioplayer": "Audioplayer",
"authpolicy": "User access configuration. Should username and password be used?",
"authinfo": "This dialog will be shown if a username is set without a password in config-file.",
"authnone": "Application does not require username or password.",
"backgroundImage": "Static background image (usually transparent)",
"playerflags": "Player parameters",
"preview": "Preview action",
"buttoncolor": "Button color",
"buttontext": "Button caption",
"buttontexts": "Button captions",
"buttontype": "Button type",
"casparcg": "CasparCG settings",
"ccgtemplates": "template-folder (casparcg.config)",
"ccgplayout": "CasparCG playout",
"ccgservers": "CasparCG servers",
"coloraccent": "Accent color",
"dataformat": "Data format",
"dataroot": "Data folder",
"defaultvalue": "Default value",
"description": "Description",
"extrabutton": "Button",
"extrabuttondescription": "Simple push button which executes a function",
"extratoggle": "Toggle button",
"extratoggledescription": "Button with separate functions for on and off states",
"extraselectbutton": "Selection dropdown with a button",
"extraselectdescription": "Dropdown selector with multiple options",
"functionlibrary": "Function library (/js/myFunctions.js)",
"functiondescription": "Function description",
"functionhandler": "Handler function",
"general": "General settings",
"globalbuttons": "Global extras",
"inputfields": "Template fields",
"javascriptfile": "Javascript file (/js/...)",
"language": "User interface language",
"logfolder": "Log folder",
"loglevel": "Log level",
"openchrome": "Open Chrome at restart",
"options": "Dropdown options (text, value)",
"optionalBgImage": "Optional background image from folder",
"projectvariables": "Project variables",
"resolution": "Output resolution",
"resolutioninfo": "Please note, most templates are designed for HD by default and may need changes to support other resolutions.",
"restartinfo": "SPX process will need to restart for changes to have effect",
"serverport": "Port",
"showextras": "Project extras",
"stopmode": "Stop animation",
"templatefile": "Template file",
"templates": "Templates assigned to this project",
"templatepathinfo": "Note, templates must be available to both CasparCG- and SPX-applications. Configure this to be the CasparCG template folder or copy templatefiles to both applications if necessary.",
"templateroot": "HTML template-folder",
"text": "Visible text",
"updated": "Updated",
"value": "Value",
"webplayout": "Web playout layer",
"webplayurl": "Browser URL",
"autoplay": "Autoplay active layers on load"
},
"error": {
"fileExisted": "File existed, please try again.",
"preventBrowseUp": "This is the root template folder.",
"templateDefinitionMissing": "SPXGCTemplateDefinition{} missing from chosen file. Refer to section templates in documentation."
},
"warning": {
"notemplates": "There are no templates linked to this project yet",
"showprofilebackup": "It is recommended to make a backup copy of profile file before making changes.",
"severalControllers": "Several controllers are connected to the SPX server! Only one recommended."
},
"hover": {
"accent": "Template accent color on rundowns.",
"addrundownitem": "Add item to rundown",
"addshow": "Create new project",
"addrundown": "Add new rundown to project",
"addccgserver": "Add new CasparCG -server by entering NAME, IP and PORT -info",
"buttontype": "Type of an extra control.",
"browsetemplate": "Browse for a new template to assign to this project",
"browseextras": "Browse for a new extra control to assign to this project",
"browseglobalextras": "Add a global extra control",
"cancelconfig": "Close config without saving changes",
"ccgserver": "CasparCG server name in SPX",
"ccgchannel": "CasparCG playout channel",
"ccglayer": "CasparCG playout layer",
"ccgtemplatepath": "template-folder value from casparcg.config -file",
"ccghost": "CasparCG server network name or IP-address",
"ccgport": "CasparCG server port (usually 5250)",
"colors": "Extra control color in user interface",
"config": "Open application configuration",
"confitem": "Templaten settings",
"confjsfile": "Javascript -library of global extras",
"dataformat": "Data format to send to template",
"dataroot": "Data folder for storing project- and rundown files?",
"defaultvalue": "Default must be one of the available option values",
"delete": "Remove item",
"deletethis": "Remove this item from the rundown",
"deletepisode": "Delete selected rundown including all items",
"deleteshow": "Delete selected project including settings and all rundowns",
"docs": "Open documentation",
"duplicateitem": "Duplicate this item",
"duplicaterundown": "Creates a duplicate of the selected rundown",
"epslist": "Select or doubleclick rundown to open it",
"extradescr": "Extra function description",
"fcall": "Event handler function in function library (for example helloWorld)",
"fields": "Scanned SPXGCTemplateDefinition fields",
"github": "Open SPX project page in GitHub",
"help": "Open help about this section",
"htmlfolder": "Absolute folder path to CasparCG template folder?",
"item": "Double click (or Enter) to open / close item editor. Drag to sort items.",
"lang": "User interface language? (You can create new language files to locales-folder as needed)",
"logfolder": "Folder for log files",
"loglevel": "Logging level? Default level: info. Most log information: debug.",
"masterplay": "Play / stop selected item (Space)",
"masterupdate": "Update texts from selected item (F5)",
"mastercontinue": "Continue animation to next step (Shift + Space)",
"navishows": "Projects",
"navishow": "Rundowns",
"newversavailable": "A newer version is available. Click to read more.",
"openbrowser": "Should Chrome browser launch at server startup?",
"openrundown": "Open selected rundown in controller",
"options": "Dropdown settings",
"outmode": "Out animation of the graphic",
"port": "Application port to use? (For instance 3000 5000...) Please note: THIS PAGES ADDRESS will also change!",
"profile": "Open settings of this project",
"playthis": "Play or stop this item",
"playouturl": "Web playout URL for streaming graphics applications.",
"preview": "Control when graphics are rendered to the preview channel",
"removeglobalextra": "Remove this extra from all projects",
"removetemplate": "Remove this template from this project",
"remoteplayouturl": "Webrender installed to en external server. (Note, public IP addresses required)",
"replacetemplate": "Re-import template if changes made to source code.",
"resolution": "Output resolution. The default is HD (1920x1080).",
"saveconfig": "Save configurations to a config file",
"saveprofile": "Save project settings",
"showjsfile": "Javascript function library of this project",
"showlist": "Select or doubleclick project to open it",
"submitthis": "Save and close editor",
"templatepath": "HTML-templates relative filepath",
"templatedescr": "Short description of the item for the GUI",
"text": "Button text",
"text1": "Button text when active",
"updated": "Timestamp when template was last imported",
"updatethis": "Update this item",
"weblayer": "Webplayout layer",
"exportitem": "Create a CSV file from fields of this template ",
"importitems": "Create rundown items from a CSV file",
"renameepisode": "Rename selected rundown"
}
}
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "Moikka",
"slogan": "Hallinnoi ja ohjaa grafiikoita CasparCG:lle tai streaming sovelluksiin.",
"folder": "Kansio",
"file": "Tiedosto",
"up": "Ylös",
"field": "Kenttä",
"pcs": "kpl",
"help": "Ohje",
"home": "Alkuun",
"yes": "Kyllä",
"no": "Ei",
"none": "Ei mitään",
"all": "Kaikki",
"fromFolder": "kansiosta"
},
"color": {
"red": "Punainen",
"green": "Vihreä",
"grey": "Harmaa",
"orange": "Oranssi",
"blue": "Sininen",
"black": "Musta",
"light": "Vaalea",
"white": "Valkoinen",
"transparent": "Läpinäkyvä"
},
"button": {
"add": "Uusi",
"addplaylist": "Lisää ajolista",
"addRow": "Lisää rivi",
"back": "◂ Takaisin",
"cancel": "Peruuta",
"config": "Sovelluksen asetukset",
"continue": "Jatka ►",
"delete": "Poista",
"deletetemplate": "Poista template",
"deletebutton": "Poista painike",
"docs": "Dokumentaatio",
"duplicate": "Monista",
"help": "Ohje",
"edit": "Muokkaa",
"edittemplates": "Muokkaa templateja",
"exploredataroot": "Selaa dataroot kansiota",
"exploretemplates": "Selaa templates kansiota",
"filterprojects": "Kirjoita suodattaaksesi projekteja...",
"launch": "Projektit",
"overlayon": "FX PLAY",
"overlayoff": "FX STOP",
"open": "Avaa ajolista",
"play": "Toista ►",
"playmaster": "Toista ► VÄLI",
"rename": "Nimeä uudelleen",
"replacetemplate": "Tuo uudelleen",
"selectemplate": "Valitse",
"select": "Avaa projekti",
"settings": "Projektin asetukset",
"shortcuts": "Näytä pikakomennot",
"showwebrenderer": "Avaa web playout ikkuna",
"stop": "Pois ■",
"stopmaster": "Pois ◄ VÄLI",
"update": "Päivitä ► F5",
"save": "Tallenna"
},
"headline": {
"createrundowntip": "Luo uusi lista tähän projektiin",
"edit": "Muokkaa sisältölistaa",
"select": "Valitse projekti",
"selectfile": "Valitse ajolista"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "Kirjautumisen määrittely",
"home": "Tervetuloa",
"login": "Kirjaudu",
"picktemplate": "Lisää template ajolistalle",
"pickAllTemplates": "Lisää kaikki templates ajolistalle",
"selectshow": "Valitse projekti",
"select": "Valitse sisältötiedosto",
"selectshowextra": "Lisää projektiin lisätoiminto",
"selectemplate": "Valitse templatetiedosto",
"selectfile": "Valitse tiedosto",
"shortcuts": "Pikakomennot",
"controller": "Controller",
"configapp": "Sovelluksen asetukset",
"configshow": "Projektin asetukset",
"shows": "Projektit",
"f1ShortcutTip" : "Tämä ohjenäyttö",
"upDownShortcutTip" : "Siirrä kohdistus edelliseen / seuraavaan kohteeseen",
"homeEndShortcutTip" : "Siirrä kohdistus ensimmäiseen / viimeiseen kohteeseen",
"enterShortcutTip" : "Avaa / sulje kohdistettu kohde muokkausta varten",
"spaceShortcutTip" : "Toista / jatka / pysäytä kohdistettu soittolistan kohde",
"tipContinue" : "Tuki next-komennolle täytyy toteuttaa templatessa käyttääksesi continue-komentoa",
"ctrlDShortcutTip" : "Monista templaten kohde",
"shiftSpaceShortcutTip" : "Toista / pysäytä kohdistettu soittolistan kohde",
"f5ShortcutTip" : "Lähetä välitön päivitys templateen (esimerkiksi kirjoitusvirheiden korjaamiseksi).",
"supportUpdateCommandTip" : "Tuki update-komennolle täytyy toteuttaa templatessa.",
"capsShortcutTip" : "Näytä kohdepeitteet nopeaa tietoa ja API-komentojen kopiointia varten.",
"escShortcutTip": "Pysäytä kaikki nykyisen rundownin käyttämät kerrokset."
},
"config": {
"addnewtip": "Lisää uusi vaihtoehto täyttämällä tyhjä rivi ja tallentamalla",
"addfirsttemplate": "Klikkaa + lisätäksesi templaten tähän projektiin",
"addfirstextra": "Klikkaa + lisätäksesi extra kontrollin tähän projektiin",
"audioplayer": "Audioplayer",
"authpolicy": "Sisäänkirjautumisen määrittely: käytetäänkö käyttäjätunnusta ja salasanaa?",
"authinfo": "Tämä asetus tulee näkyiin, jos config -tiedostossa on määritettynä username mutta ei salasanaa.",
"authnone": "Sovelluksen käyttö ei vaadi käyttäjätunnusta ja salasanaa.",
"backgroundImage": "Staattinen tausta (yleensä läpinäkyvä)",
"playerflags": "Player parametrit",
"buttoncolor": "Painikkeen väri",
"buttontext": "Painikkeen teksti",
"buttontexts": "Painikkeen tekstit",
"buttontype": "Painikkeen tyyppi",
"casparcg": "CasparCG asetukset",
"ccgtemplates": "template-folder (casparcg.config)",
"ccgplayout": "CasparCG playout",
"ccgservers": "CasparCG palvelimet",
"coloraccent": "Korostusväri",
"dataformat": "Datan lähetysmuoto",
"dataroot": "Datakansio",
"defaultvalue": "Oletusarvo",
"description": "Kuvaus",
"extrabutton": "Painike",
"extrabuttondescription": "Yksinkertainen painike joka suorittaa klikatessa funktion",
"extratoggle": "On/Off painike",
"extratoggledescription": "Painike joka suorittaa erilliset päälle / pois funktiot",
"extraselectbutton": "Valintalista ja painike",
"extraselectdescription": "Alasvetovalikko jossa useita vaihtoehtoja sekä toimintopainike",
"functionlibrary": "Funktiokirjasto (/js/myFunctions.js)",
"functiondescription": "Toiminnon kuvaus",
"functionhandler": "Tapahtumakäsittelijä (funktio)",
"general": "Yleiset asetukset",
"globalbuttons": "Globaalit lisäpainikkeet",
"inputfields": "Templatekenttiä",
"javascriptfile": "Javascript tiedosto (/js/...)",
"language": "Käyttöliittymän kieli",
"logfolder": "Logituskansio",
"loglevel": "Logitustaso",
"openchrome": "Avaa Chrome käynnistyksessä",
"options": "Valikon vaihtoehdot (teksti, arvo)",
"optionalBgImage": "Vaihtoehtoinen taustakuva kansiosta",
"restartinfo": "SPX prosessi pitää uudelleenkäynnistää muutosten jälkeen.",
"resolution": "Resoluutio",
"resolutioninfo": "Huomaa: templatet on suunniteltu yleensä HD resoluutioon ja voivat vaatia muutoksia eri kokoja varten.",
"serverport": "Tämän sovelluksen portti",
"showextras": "Projektin lisätoiminnot",
"stopmode": "Stop animaatio",
"templatefile": "Template tiedosto",
"templates": "Projektiin linkitetyt templatet",
"templatepathinfo": "Huomaa, että templatet tulee olla käytettävissä sekä CasparCG- että SPX-sovelluksille. Konfiguroi tästä CasparCG kansio tai tarvittaessa kopioi templatet molempien sovelluksien kansioihin.",
"templateroot": "HTML template-kansio",
"text": "Näkyvä teksti",
"updated": "Päivitetty",
"value": "Arvo",
"webplayout": "Web playout taso",
"webplayurl": "Browser URL",
"preview": "Esikatselutoiminto",
"projectvariables": "Projektin muuttujat",
"autoplay": "Soita aktiiviset tasot ladattaessa"
},
"error": {
"fileExisted": "Tiedosto oli jo olemassa. Yritä uudelleen.",
"preventBrowseUp": "Tämä on template kansio.",
"templateDefinitionMissing": "Valitusta template-tiedostosta puuttui window.SPXGCTemplateDefinition{}. Katso dokumentaatiosta kohta template-tiedostot."
},
"warning": {
"notemplates": "Tähän projektiin ei ole linkitetty templateja",
"showprofilebackup": "On suositeltavaa ottaa profile.json tiedostosta varmuuskopio ennen muutosten tekemistä.",
"severalControllers": "Useita kontrollereita yhdistyneenä SPX serveriin! Vain yhtä suositellaan."
},
"hover": {
"accent": "Templaten korostusväri ajolistoissa",
"addrundownitem": "Lisää elementti ajolistalle",
"addshow": "Luo uusi projekti",
"addrundown": "Lisää uusi ajolista projektiin",
"addccgserver": "Lisää uusi CasparCG -palvelin syöttämällä NIMI IP ja PORT -tiedot",
"buttontype": "Lisätoiminnon tyyppi (button, togglebutton...)",
"browsetemplate": "Lisää tähän projektiin uusi template selaamalla templatekansiota...",
"browseextras": "Lisää tähän projektiin uusi lisätoiminto...",
"browseglobalextras": "Lisää uusi globaali lisätoiminto...",
"cancelconfig": "Sulje config tallentamatta muutoksia",
"ccgserver": "CasparCG ulosajopalvelimen nimi",
"ccgchannel": "CasparCG kanava palvelimella",
"ccglayer": "CasparCG taso kanavalla",
"ccgtemplatepath": "Parametrin template-folder arvo casparcg.config -tiedostosta",
"ccghost": "CasparCG palvelimen verkkonimi tai IP-osoite",
"ccgport": "CasparCG palvelimen portti (yleensä 5250)",
"colors": "Toimintopainikkeen oletusväri käyttöliittymässä",
"config": "Avaa sovelluksen yleiset asetukset",
"confitem": "Templaten asetukset",
"confjsfile": "Kaikissa ohjelmaprofiileissa näkyvien lisäpainikkeiden javascript-kirjasto",
"dataformat": "Missä muodossa data lähetetään templatelle?",
"dataroot": "Mihin kansioon tallennetaan projekti- ja ajolistatiedostot?",
"defaultvalue": "Oletusarvon tulee olla yksi vaihtoehdoista",
"delete": "Poista elementti",
"deletethis": "Poista tämä elementti listalta",
"deletepisode": "Poista valittu ajolista ja kaikki sen elementit",
"deleteshow": "Poista valittu projekti ja kaikki sen asetukset ja ajolistat",
"docs": "Avaa dokumentaatio",
"duplicateitem": "Monista tämä elementti",
"duplicaterundown": "Luo valitusta ajolistasta kopion",
"epslist": "Valitse tai tuplaklikkaa haluamasi ajolista auki",
"extradescr": "Toimintopainikeen lyhyt kuvaus käyttöliittymään",
"fcall": "Funktiokirjastossa oleva tapahtumakäsittelijä funktio (esim helloWorld)",
"fields": "Templatesta skannatut SPXGCTemplateDefinition -tiedot",
"github": "Avaa SPX -projektin sivu Githubissa",
"help": "Avaa help tästä aihealueesta",
"htmlfolder": "Absoluuttinen polku CasparCG:n template-folder -kansioon?",
"item": "Tuplaklikkaa (tai Enter) muokkaustila auki / kiinni tai raahaa eri järjestykseen",
"lang": "Käyttöliittymän kieliversio-tiedosto? (Voit tarvittaessa lisätä kielitiedostoja kansioon locales.)",
"logfolder": "Kansio johon logitiedostot generoidaan",
"loglevel": "Mitä logitasoa käytetään? Oletus: info. Eniten tietoja tallentuu: debug.",
"masterplay": "Toista / pysäytä aktiivinen elementti (Space)",
"masterupdate": "Päivitä aktiivisen elementit tekstit (F5)",
"mastercontinue": "Jatka animaatiota seuraavaan vaiheeseen (Shift + Space)",
"navishows": "Projektilistaukseen",
"navishow": "Ajolistat",
"newversavailable": "Uudempi versio saatavilla. Klikkaa ja lue lisää.",
"openbrowser": "Avataanko Chrome-selain automaattisesti kun GC-palvelin käynnistetään?",
"openrundown": "Avaa valittu ajolista muokkausta tai ulosajoa varten",
"options": "Alasvetovalikon asetukset",
"outmode": "Grafiikan poistuminen",
"port": "Mitä tietoliikenneporttia käytetään? (Esim 3000 5000...) Huomaa että TÄMÄN SIVUN url-osoite muuttuu!",
"profile": "Avaa tämän projektin asetukset",
"playthis": "Toista / pysäytä tämä elementti",
"playouturl": "GC:n generoima paikallinen webplayout osoite streamgrafiikoita varten",
"removeglobalextra": "Poista tämä painike kaikista projekteista",
"removetemplate": "Poista tämä template projektista",
"remoteplayouturl": "Julkiselle palvelimelle asennettu webrender. (Huomaa että koneilla oltava julkiset IP-osoitteet.)",
"replacetemplate": "Tuo template uudelleen kun lähdekoodiin tehty muutoksia.",
"resolution": "Resoluutio. Oletus on HD (1920x1080).",
"saveconfig": "Tallenna asetukset sovelluksen config-tiedostoon",
"saveprofile": "Tallenna projektin asetukset",
"showjsfile": "Tämän projektin lisätoimintojen javascript-kirjasto",
"showlist": "Valitse tai tuplaklikkaa haluamasi projekti auki",
"submitthis": "Tallenna ja sulje muokkausnäkymä",
"templatepath": "HTML-grafiikkatemplaten suhteellinen tiedostopolku template-kansiossa",
"templatedescr": "Elementin lyhyt kuvaus GC:n käyttöliittymässä",
"text": "Painikkeen teksti",
"text1": "Painikkeen teksti painettuna",
"updated": "Viimeisimmän päivityksen aikaleima",
"updatethis": "Päivitä tämä elementti",
"weblayer": "Webplayout kanava",
"preview": "Määrittelee koska esikatselu päivitetään",
"exportitem": "Luo CSV tiedosto tämän templaten kentistä",
"importitems": "Tuo ajolistalle elementtejä CSV tiedostosta",
"renameepisode": "Nimeä ajolista uudelleen"
}
}
+252
View File
@@ -0,0 +1,252 @@
{
"general": {
"hello": "Hallo",
"slogan": "Verwaltung und Steuerung von Grafiken für Live-Übertragungen, Live-Streaming und Live-Events.",
"folder": "Ordner",
"file": "Datei",
"up": "Nach oben",
"field": "Feld",
"pcs": "Stk.",
"help": "Hilfe",
"home": "Start",
"yes": "Ja",
"no": "Nein",
"none": "None",
"all": "Alle",
"fromFolder": "von Ordner"
},
"color": {
"red": "Rot",
"green": "Grün",
"grey": "Grau",
"orange": "Orange",
"blue": "Blau",
"black": "Schwarz",
"light": "Light",
"white": "Weiss",
"transparent": "Transparent"
},
"button": {
"add": "Neu",
"addplaylist": "Hinzufügen",
"addRow": "Reihe hinzufügen",
"back": "◂ Zurück",
"cancel": "Abbrechen",
"config": "Anwendung konfigurieren",
"continue": "Weiter ►",
"delete": "Löschen",
"deletetemplate": "Template entfernen",
"deletebutton": "Taste entfernen",
"docs": "Dokumentation",
"duplicate": "Duplizieren",
"help": "Hilfe",
"edit": "Bearbeiten",
"edittemplates": "Templates bearbeiten",
"exploredataroot": "Dataroot Ordner durchsuchen",
"exploretemplates": "Templates Ordner durchsuchen",
"filterprojects": "Texteingabe zur Projektsuche...",
"launch": "Projekte",
"overlayon": "FX PLAY",
"overlayoff": "FX STOP",
"open": "Rundown öffnen",
"play": "Play ►",
"playmaster": "Play ► Leertaste",
"rename": "Umbenennen",
"replacetemplate": "Erneut Importieren",
"selectemplate": "Wählen",
"select": "Projekt öffnen",
"settings": "Projekteinstellungen",
"shortcuts": "Tastaturkürzel",
"showwebrenderer": "Ausgabefenster öffnen...",
"stop": "Stop ■",
"stopmaster": "Stop ■ Leertaste",
"update": "Aktualisieren ► F5",
"save": "Speichern"
},
"headline": {
"createrundowntip": "Neuen Rundown für dieses Projekt erstellen",
"edit": "Rundown bearbeiten",
"select": "Wählen Sie ein Projekt aus",
"selectfile": "Wählen Sie einen Rundown aus"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "Benutzerzugang konfigurieren",
"home": "Willkommen",
"login": "Login",
"pickAllTemplates": "Alle Templates zu Rundown hinzufügen",
"picktemplate": "Template zu Rundown hinzufügen",
"selectshow": "Wählen Sie ein Projekt aus",
"select": "Wählen Sie einen Rundown aus",
"selectshowextra": "Fügen Sie dem Projekt ein extra Steuerelement hinzu",
"selectemplate": "Wähle Sie ein Template aus",
"selectfile": "Wählen Sie eine Datei aus",
"shortcuts": "Tastaturkürzel",
"controller": "Controller",
"configapp": "Anwendung konfigurieren",
"configshow": "Projekteinstellungen",
"shows": "Projekte",
"f1ShortcutTip" : "Dieser Hilfebildschirm",
"upDownShortcutTip" : "Fokus auf vorheriges / nächstes Element",
"homeEndShortcutTip" : "Fokus auf erstes / letztes Element",
"enterShortcutTip" : "Fokussiertes Element zum Bearbeiten öffnen / schließen",
"spaceShortcutTip" : "Fokussiertes Playlist-Element abspielen / fortsetzen / stoppen",
"tipContinue" : "Unterstützung für den next-Befehl muss im Template implementiert werden um continue zu verwenden",
"ctrlDShortcutTip" : "Template-Element duplizieren",
"shiftSpaceShortcutTip" : "Fokussiertes Playlist-Element abspielen / stoppen",
"f5ShortcutTip" : "Sofortiges Update an das Template senden (z. B. um Tippfehler zu korrigieren).",
"supportUpdateCommandTip" : "Unterstützung für den update-Befehl muss im Template implementiert werden.",
"capsShortcutTip" : "Element-Overlays für schnelle Infos und zum schnellen Kopieren von API-Befehlen anzeigen.",
"escShortcutTip": "Alle vom aktuellen Rundown verwendeten Layer stoppen."
},
"config": {
"addnewtip": "Neues Item durch Ausfüllen und Speichern hinzufügen",
"addfirsttemplate": "Klicke auf +, um diesem Projekt ein Template hinzuzufügen",
"addfirstextra": "Klicke auf +, um diesem Projekt ein extra Steuerelement hinzuzufügen",
"audioplayer": "Audio-Player",
"authpolicy": "Konfiguration des Benutzerzugangs. Sollen Benutzername und Passwort verwendet werden?",
"authinfo": "Dieser Dialog wird angezeigt, wenn in der Konfigurationsdatei ein Benutzername ohne Passwort angegeben wurde.",
"authnone": "Anwendung erfordert keinen Benutzernamen oder Passwort.",
"backgroundImage": "Statisches Hintergrundbild (normalerweise Transparent)",
"playerflags": "Player-Parameter",
"preview": "Vorschau-Aktion",
"buttoncolor": "Tastenfarbe",
"buttontext": "Tastenbeschriftung",
"buttontexts": "Tastenbeschriftungen",
"buttontype": "Tastentyp",
"casparcg": "CasparCG Einstellungen",
"ccgtemplates": "template-folder (casparcg.config)",
"ccgplayout": "CasparCG Playout",
"ccgservers": "CasparCG Server",
"coloraccent": "Akzentfarbe",
"dataformat": "Datenformat",
"dataroot": "Datenordner",
"defaultvalue": "Standardwert",
"description": "Beschreibung",
"extrabutton": "Taste",
"extrabuttondescription": "Einfache Taste, die eine Funktion ausführt",
"extratoggle": "Toggle-Taste",
"extratoggledescription": "Taste mit getrennten Funktionen für Ein- und Aus-Status",
"extraselectbutton": "Auswahl-Dropdown mit einer Taste",
"extraselectdescription": "Dropdown-Selektor mit mehreren Optionen",
"functionlibrary": "Funktionsbibliothek (/js/myFunctions.js)",
"functiondescription": "Funktionsbeschreibung",
"functionhandler": "Handler-Funktion",
"general": "Allgemeine Einstellungen",
"globalbuttons": "Globale Extras",
"inputfields": "Template Felder",
"javascriptfile": "Javascript Datei (/js/...)",
"language": "Sprache der Benutzeroberfläche",
"logfolder": "Log Ordner",
"loglevel": "Log Level",
"openchrome": "Chrome-Browser beim Neustart öffnen",
"options": "Dropdown-Optionen (Text, Wert)",
"optionalBgImage": "Optionales Hintergrundbild aus Ordner",
"projectvariables": "Projektvariablen",
"resolution": "Ausgabeauflösung",
"resolutioninfo": "Bitte beachten Sie, dass die meisten Templates standardmäßig für HD ausgelegt sind und möglicherweise geändert werden müssen, um andere Auflösungen zu unterstützen.",
"restartinfo": "Der SPX-Prozess muss neu gestartet werden, damit die Änderungen wirksam werden.",
"serverport": "Port",
"showextras": "Projekt-Extras",
"stopmode": "Animation stoppen",
"templatefile": "Template Datei",
"templates": "Diesem Projekt zugewiesene Templates",
"templatepathinfo": "Beachten Sie, dass die Vorlagen sowohl für die CasparCG- als auch für die SPX-Anwendung verfügbar sein müssen. Konfigurieren Sie dazu den CasparCG-Vorlagenordner oder kopieren Sie ggf. Vorlagendateien in beide Anwendungen.",
"templateroot": "HTML Template-Ordner",
"text": "Sichtbarer Text",
"updated": "Aktualisiert",
"value": "Wert",
"webplayout": "Web Playout Ebene",
"webplayurl": "Browser URL",
"autoplay": "Autoplay aktiver Ebenen beim Laden"
},
"error": {
"fileExisted": "Datei existiert bereits, bitte versuchen Sie es erneut.",
"preventBrowseUp": "Dies ist der Root Template Ordner.",
"templateDefinitionMissing": "SPXGCTemplateDefinition{} fehlt in der gewählten Datei. Siehe Abschnitt Templates in der Dokumentation."
},
"warning": {
"notemplates": "Es sind noch keine Templates mit diesem Projekt verknüpft.",
"showprofilebackup": "Es wird empfohlen, eine Sicherungskopie der Profildatei zu erstellen, bevor Sie Änderungen vornehmen.",
"severalControllers": "Mehrere Controller sind mit dem SPX-Server verbunden! Es wird nur ein Controller empfohlen."
},
"hover": {
"accent": "Template Akzentfarbe in Rundowns.",
"addrundownitem": "Item zu Rundown hinzufügen",
"addshow": "Neues Projekt erstellen",
"addrundown": "Neuer Rundown zu Projekt hinzufügen",
"addccgserver": "Neuen CasparCG Server hinzufügen, durch Angabe von NAME, IP und PORT Informationen",
"buttontype": "Art des extra Steuerelements.",
"browsetemplate": "Suchen Sie nach einem neuen Template, das Sie diesem Projekt zuweisen möchten",
"browseextras": "Suchen Sie nach einem neuen extra Steuerelement, das Sie diesem Projekt zuweisen möchten.",
"browseglobalextras": "Hinzufügen eines globalen extra Steuerelements",
"cancelconfig": "Konfiguration schliessen ohne zu Speichern",
"ccgserver": "CasparCG Servername in SPX",
"ccgchannel": "CasparCG Playout Channel",
"ccglayer": "CasparCG Playout Ebene",
"ccgtemplatepath": "template-folder Wert der casparcg.config -Datei",
"ccghost": "CasparCG Server Netzwerkname oder IP-Adresse",
"ccgport": "CasparCG Server Port (üblicherweise 5250)",
"colors": "Extra Kontrollfarbe im User Interface",
"config": "Anwendungskonfiguration öffnen",
"confitem": "Template-Einstellungen",
"confjsfile": "Javascript -library der globalen Extras",
"dataformat": "An das Template zu sendendes Datenformat",
"dataroot": "Datenordner zur Speicherung von Projekt- und Rundown-Dateien?",
"defaultvalue": "Standard muss einer der verfügbaren Optionswerte sein",
"delete": "Item entfernen",
"deletethis": "Dieses Item vom Rundown entfernen",
"deletepisode": "Ausgewählten Rundown einschließlich aller Items löschen",
"deleteshow": "Ausgewähltes Projekt einschließlich Einstellungen und aller Rundowns löschen",
"docs": "Dokumentation öffnen",
"duplicateitem": "Item duplizieren",
"duplicaterundown": "Erstellt Duplikat des gewählten Rundowns",
"epslist": "Wählen Sie den Rundown oder doppelklicken Sie darauf, um ihn zu öffnen.",
"extradescr": "Extra Funktionsbeschreibung",
"fcall": "Event Handler Function in Funktionsbibliothek (z.B. helloWorld)",
"fields": "Gescannte SPXGCTemplateDefinition Felder",
"github": "SPX-Projektseite auf GitHub öffnen",
"help": "Hilfe zu diesem Abschnitt öffnen",
"htmlfolder": "Absoluter Ordnerpfad zum CasparCG-Templateordner?",
"item": "Doppelklick (oder Enter) zum Öffnen/Schließen des Item-Editors. Ziehen zum Sortieren von Items.",
"lang": "Sprache der Benutzeroberfläche? (Sie können nach Bedarf neue Sprachdateien im locales-Ordner erstellen)",
"logfolder": "Order für Log-Dateien",
"loglevel": "Logging Level? Standard Level: info. Erweiterte Log-Informationen: debug.",
"masterplay": "Ausgewähltes Item abspielen/anhalten (Leertaste)",
"masterupdate": "Aktualisieren von Texten aus dem ausgewählten Item (F5)",
"mastercontinue": "Animation zum nächsten Schritt fortsetzen (Shift + Leertaste)",
"navishows": "Projekte",
"navishow": "Rundowns",
"newversavailable": "Eine neuere Version ist verfügbar. Klicken Sie, um mehr zu erfahren.",
"openbrowser": "Soll der Chrome-Browser beim Start des Servers gestartet werden?",
"openrundown": "Ausgewählten Rundown im Controller öffnen",
"options": "Dropdown-Einstellungen",
"outmode": "Out-Animation der Grafik",
"port": "Zu verwendender Anwendungs-Port? (z.B. 3000 5000...) Bitte beachten: Die Adresse dieser Seite wird sich ebenfalls ändern!",
"profile": "Einstellungen für dieses Projekt öffnen",
"playthis": "Dieses Item abspielen oder anhalten",
"playouturl": "Web-Playout-URL für Streaming-Grafikanwendungen.",
"preview": "Steuern, wann Grafiken in den Vorschaukanal gerendert werden",
"removeglobalextra": "Dieses extra Element aus allen Projekten entfernen",
"removetemplate": "Dieses Template von diesem Projekt entfernen",
"remoteplayouturl": "Webrender auf einem externen Server installiert. (Hinweis: öffentliche IP-Adressen erforderlich)",
"replacetemplate": "Template erneut importieren, wenn Änderungen am Quellcode vorgenommen wurden.",
"resolution": "Ausgabeauflösung. Die Standardeinstellung ist HD (1920x1080).",
"saveconfig": "Konfigurationen in einer Konfigurationsdatei speichern",
"saveprofile": "Projekteinstellungen speichern",
"showjsfile": "Javascript-Funktionsbibliothek dieses Projekts",
"showlist": "Wählen Sie ein Projekt aus oder doppelklicken Sie darauf, um es zu öffnen",
"submitthis": "Speichern und Editor schließen",
"templatepath": "HTML-Templates relativer Dateipfad",
"templatedescr": "Kurzbeschreibung des Items für die GUI",
"text": "Text der Taste",
"text1": "Text der Taste wenn Sie aktiv ist",
"updated": "Zeitstempel des letzten Imports des Templates",
"updatethis": "Dieses Item aktualisieren",
"weblayer": "Webplayout Ebene",
"exportitem": "Erstellen Sie eine CSV-Datei aus den Feldern dieses Templates ",
"importitems": "Erstellen von Rundown-Items aus einer CSV-Datei",
"renameepisode": "Ausgewählten Rundown umbenennen"
}
}
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "ようこそ",
"slogan": "あらゆるライブ配信向けのCGグラフィクス管理を提供",
"folder": "フォルダー",
"file": "ファイル",
"up": "アップ",
"field": "フィールド",
"pcs": "個数",
"help": "ヘルプ",
"home": "ホーム",
"yes": "はい",
"no": "いいえ",
"none": "無し",
"all": "すべて",
"fromFolder": "from folder"
},
"color": {
"red": "赤",
"green": "緑",
"grey": "グレー",
"orange": "オレンジ",
"blue": "青",
"black": "黒",
"light": "ライト",
"white": "白",
"transparent": "透過"
},
"button": {
"add": "新規",
"addplaylist": "新規 rundown",
"addRow": "新規 row",
"back": "◂ 戻る",
"cancel": "キャンセル",
"config": "アプリケーション設定",
"continue": "続き ►",
"delete": "削除",
"deletetemplate": "テンプレート削除",
"deletebutton": "ボタン削除",
"docs": "ドキュメント",
"duplicate": "複製",
"help": "ヘルプ",
"edit": "編集",
"edittemplates": "テンプレート編集",
"exploredataroot": "dataroot フォルダ確認",
"exploretemplates": "templates フォルダ確認",
"filterprojects": "プロジェクト検索フィルタ...",
"launch": "プロジェクト",
"overlayon": "FX 再生",
"overlayoff": "FX 停止",
"open": "開く rundown",
"play": "再生 ►",
"playmaster": "再生 ►",
"rename": "リネーム",
"replacetemplate": "再インポート",
"selectemplate": "選択",
"select": "プロジェクトを開く",
"settings": "プロジェクト設定",
"shortcuts": "ショートカット",
"showwebrenderer": "playout ウインドウを開く",
"stop": "停止 ■",
"stopmaster": "停止 ■",
"update": "更新 ► F5",
"save": "保存"
},
"headline": {
"createrundowntip": "新規の rundown を作成",
"edit": "rundown 編集",
"select": "プロジェクトを選択",
"selectfile": "rundown の選択"
},
"pagetitle": {
"admin": "管理者",
"authpolicy": "ユーザアクセス設定",
"home": "ようこそ",
"login": "ログイン",
"pickAllTemplates": "rundown へ全てのテンプレートを追加",
"picktemplate": "rundown へテンプレートを追加",
"selectshow": "プロジェクトを選択",
"select": "rundown を選択",
"selectshowextra": "プロジェクトへ拡張操作を追加",
"selectemplate": "テンプレートを選択",
"selectfile": "ファイル選択",
"shortcuts": "ショートカット",
"controller": "コントローラー",
"configapp": "アプリケーション設定",
"configshow": "プロジェクト設定",
"shows": "プロジェクト",
"f1ShortcutTip" : "このヘルプ画面",
"upDownShortcutTip" : "前 / 次の項目にフォーカス",
"homeEndShortcutTip" : "最初 / 最後の項目にフォーカス",
"enterShortcutTip" : "フォーカス中の項目を編集用に開く / 閉じる",
"spaceShortcutTip" : "フォーカス中のプレイリスト項目を再生 / 続行 / 停止",
"tipContinue" : "next コマンドのサポートはテンプレートに実装する必要があります continue を使用するには",
"ctrlDShortcutTip" : "テンプレート項目を複製",
"shiftSpaceShortcutTip" : "フォーカス中のプレイリスト項目を再生 / 停止",
"f5ShortcutTip" : "テンプレートへ即時更新を送信(例:タイプミスの修正)。",
"supportUpdateCommandTip" : "update コマンドのサポートはテンプレートに実装する必要があります。",
"capsShortcutTip" : "クイック情報表示や API コマンドのコピー用に項目オーバーレイを表示。",
"escShortcutTip": "現在のランダウンで使用中のすべてのレイヤーを停止。"
},
"config": {
"addnewtip": "新規アイテムの書き込みと保存",
"addfirsttemplate": "+ をクリックしてこのプロジェクトにテンプレートを追加します",
"addfirstextra": "+ をクリックして、このプロジェクトに追加のコントロールを追加します",
"audioplayer": "オーディオプレイヤー",
"authpolicy": "ユーザーアクセス構成。ユーザー名とパスワードを使用する必要がありますか?",
"authinfo": "このダイアログは、設定ファイルにパスワードなしでユーザー名が設定されている場合に表示されます。",
"authnone": "アプリケーションにはユーザー名やパスワードは必要ありません。",
"backgroundImage": "静的な背景画像 (通常は透過)",
"playerflags": "プレーヤーパラメータ",
"preview": "プレビューアクション",
"buttoncolor": "ボタンの色",
"buttontext": "ボタンのキャプション",
"buttontexts": "ボタンのキャプション",
"buttontype": "ボタンの種類",
"casparcg": "CasparCGの設定",
"ccgtemplates": "テンプレートフォルダー (casparcg.config)",
"ccgplayout": "CasparCG 出力先",
"ccgservers": "CasparCGサーバー",
"coloraccent": "アクセントの色",
"dataformat": "データ形式",
"dataroot": "データフォルダ",
"defaultvalue": "デフォルト値",
"description": "概要説明",
"extrabutton": "ボタン",
"extrabuttondescription": "機能を実行するシンプルな押しボタン",
"extratoggle": "トグルボタン",
"extratoggledescription": "オンとオフの状態に個別の機能を備えたボタン",
"extraselectbutton": "ボタン付きの選択ドロップダウン",
"extraselectdescription": "複数のオプションを備えたドロップダウン セレクター",
"functionlibrary": "javascriptライブラリ (/js/myFunctions.js)",
"functiondescription": "機能説明",
"functionhandler": "ハンドラー関数",
"general": "一般設定",
"globalbuttons": "グローバルエクストラ",
"inputfields": "テンプレートフィールド",
"javascriptfile": "Javascript ファイル (/js/...)",
"language": "ユーザーインターフェース言語",
"logfolder": "ログフォルダー",
"loglevel": "ログレベル",
"openchrome": "再起動時にChromeを開く",
"options": "ドロップダウン オプション (テキスト、値)",
"optionalBgImage": "フォルダーからのオプションの背景画像",
"projectvariables": "プロジェクト変数",
"resolution": "出力解像度",
"resolutioninfo": "ほとんどのテンプレートはデフォルトで HD 用に設計されており、他の解像度をサポートするには変更が必要な場合があることに注意してください。",
"restartinfo": "変更を有効にするには、SPX プロセスを再起動する必要があります",
"serverport": "ポート",
"showextras": "プロジェクトのエキストラ",
"stopmode": "アニメーションを停止する",
"templatefile": "テンプレートファイル",
"templates": "このプロジェクトに割り当てられたテンプレート",
"templatepathinfo": "テンプレートは、CasparCG アプリケーションと SPX アプリケーションの両方で使用できる必要があることに注意してください。これを CasparCG テンプレート フォルダーになるように設定するか、必要に応じてテンプレート ファイルを両方のアプリケーションにコピーします。",
"templateroot": "HTMLテンプレートフォルダー",
"text": "表示されるテキスト",
"updated": "更新しました",
"value": "値",
"webplayout": "Web 出力先レイヤー",
"webplayurl": "ブラウザのURL",
"autoplay": "Autoplay active layers on load"
},
"error": {
"fileExisted": "ファイルが存在しました。もう一度試してください。",
"preventBrowseUp": "これはルート テンプレート フォルダーです。",
"templateDefinitionMissing": "選択したファイルに SPXGCTemplateDefinition{} がありません。ドキュメントのセクション テンプレートを参照してください。"
},
"warning": {
"notemplates": "There are no templates linked to this project yet",
"showprofilebackup": "変更を加える前に、プロファイル ファイルのバックアップ コピーを作成することをお勧めします。",
"severalControllers": "複数のコントローラーが SPX サーバーに接続されています。推奨されるのは 1 つだけです。"
},
"hover": {
"accent": "rundown のテンプレートのアクセントカラー。",
"addrundownitem": "項目を概要に追加",
"addshow": "新しいプロジェクトを作成する",
"addrundown": "新しいrundown をプロジェクトに追加",
"addccgserver": "NAME、IP、PORT 情報を入力して、新しい CasparCG サーバーを追加します。",
"buttontype": "追加のコントロールのタイプ。",
"browsetemplate": "このプロジェクトに割り当てる新しいテンプレートを参照します",
"browseextras": "このプロジェクトに割り当てる新しい追加コントロールを参照します",
"browseglobalextras": "グローバル追加コントロールを追加する",
"cancelconfig": "変更を保存せずに構成を閉じます",
"ccgserver": "SPX の CasparCG サーバー名",
"ccgchannel": "CasparCG 出力先 チャンネル",
"ccglayer": "CasparCG 出力先 レイヤー",
"ccgtemplatepath": "casparcg.config -file からの template-folder の値",
"ccghost": "CasparCG サーバーのネットワーク名または IP アドレス",
"ccgport": "CasparCG サーバー ポート (通常は 5250)",
"colors": "ユーザーインターフェイスの追加のコントロールカラー",
"config": "アプリケーション構成を開く",
"confitem": "テンプレートの設定",
"confjsfile": "Javascript - グローバル エクストラのライブラリ",
"dataformat": "テンプレートに送信するデータ形式",
"dataroot": "プロジェクト ファイルとrundown ファイルを保存するためのデータ フォルダー?",
"defaultvalue": "デフォルトは、使用可能なオプション値のいずれかである必要があります",
"delete": "アイテムを削除する",
"deletethis": "この項目を概要から削除します",
"deletepisode": "すべてのアイテムを含む選択したrundown を削除します",
"deleteshow": "設定とすべての概要を含む選択したプロジェクトを削除します",
"docs": "オープンドキュメント",
"duplicateitem": "このアイテムを複製する",
"duplicaterundown": "選択したrundown の複製を作成します",
"epslist": "rundown を選択またはダブルクリックして開きます",
"extradescr": "追加機能の説明",
"fcall": "関数ライブラリ内のイベント ハンドラー関数 (例: helloWorld)",
"fields": "スキャンされた SPXGCTemplateDefinition フィールド",
"github": "GitHub で SPX プロジェクト ページを開く",
"help": "このセクションに関するヘルプを開く",
"htmlfolder": "CasparCGテンプレートフォルダーへの絶対フォルダーパス?",
"item": "ダブルクリック (または Enter) して、項目エディターを開く/閉じます。ドラッグして項目を並べ替えます。",
"lang": "ユーザーインターフェース言語? (必要に応じて、locales-folder に新しい言語ファイルを作成できます)",
"logfolder": "ログファイル用のフォルダー",
"loglevel": "ロギングレベル:デフォルトのレベル: 情報。ほとんどのログ情報: デバッグ。",
"masterplay": "選択した項目の再生/停止(スペース)",
"masterupdate": "選択した項目のテキストを更新 (F5)",
"mastercontinue": "アニメーションを次のステップに継続します (Shift + Space)",
"navishows": "プロジェクト",
"navishow": "rundown ",
"newversavailable": "新しいバージョンが利用可能です。クリックして詳細をご覧ください。",
"openbrowser": "Chrome ブラウザはサーバーの起動時に起動する必要がありますか?",
"openrundown": "選択したrundown をコントローラーで開く",
"options": "ドロップダウン設定",
"outmode": "グラフィックのアウトアニメーション",
"port": "使用するアプリケーションポート (例: 3000 5000...) 注意: このページのアドレスも変更されます。",
"profile": "このプロジェクトの設定を開く",
"playthis": "このアイテムを再生または停止します",
"playouturl": "ストリーミング グラフィックス アプリケーションの Web 出力先 URL",
"preview": "グラフィックスをプレビュー チャネルにいつレンダリングするかを制御します",
"removeglobalextra": "全てのプロジェクトからエクストラを削除",
"removetemplate": "このプロジェクトからテンプレートを削除",
"remoteplayouturl": "外部サーバへ Webrender をインストール完了 (※Public IP アドレスが必要)",
"replacetemplate": "ソースの変更によりテンプレートの再インポート",
"resolution": "出力解像度. デフォルト FHD (1920x1080).",
"saveconfig": "設定をコンフィグファイルへ保存",
"saveprofile": "プロジェクト設定の保存",
"showjsfile": "プロジェクト向け Javascript ファンクションライブラリ",
"showlist": "選択またはプロジェクトをダブルクリックで開く",
"submitthis": "エディターの保存と終了",
"templatepath": "HTMLテンプレート関連のファイルパス",
"templatedescr": "GUI向けアイテムの簡単な概要",
"text": "ボタンテキスト",
"text1": "アクティブなボタンテキスト",
"updated": "最後にインポートされたテンプレートのタイムスタンプ",
"updatethis": "このアイテムの保存",
"weblayer": "Web 出力先 レイヤー",
"exportitem": "Create a CSV file from fields of this template ",
"importitems": "Create rundown items from a CSV file",
"renameepisode": "Rename selected rundown"
}
}
+250
View File
@@ -0,0 +1,250 @@
{
"general": {
"hello": "안녕하세요",
"slogan": "실시간 방송, 스트리밍 및 이벤트의 그래픽을 관리하고 제어하세요.",
"folder": "폴더",
"file": "파일",
"up": "위로",
"field": "필드",
"pcs": "개",
"help": "도움말",
"home": "홈",
"yes": "예",
"no": "아니오",
"none": "없음",
"all": "전체",
"fromFolder": "폴더에서"
},
"color": {
"red": "빨강",
"green": "초록",
"grey": "회색",
"orange": "주황",
"blue": "파랑",
"black": "검정",
"light": "밝음",
"white": "흰색",
"transparent": "투명"
},
"button": {
"add": "추가",
"addplaylist": "룬 추가",
"addRow": "행 추가",
"back": "◂ 뒤로",
"cancel": "취소",
"config": "애플리케이션 설정",
"continue": "계속 ►",
"delete": "삭제",
"deletetemplate": "템플릿 제거",
"deletebutton": "버튼 제거",
"docs": "문서",
"duplicate": "복제",
"help": "도움말",
"edit": "편집",
"edittemplates": "템플릿 편집",
"exploredataroot": "데이터 루트 폴더 탐색",
"exploretemplates": "템플릿 폴더 탐색",
"filterprojects": "프로젝트 필터링을 위해 입력하세요...",
"launch": "프로젝트",
"overlayon": "FX 재생 중",
"overlayoff": "FX 정지",
"open": "룬 열기",
"play": "재생 ►",
"playmaster": "재생 ►",
"rename": "이름 변경",
"replacetemplate": "템플릿 재불러오기",
"selecttemplate": "선택",
"select": "프로젝트 열기",
"settings": "프로젝트 설정",
"shortcuts": "키보드 단축키",
"showwebrenderer": "출력 창 열기...",
"stop": "정지 ■",
"stopmaster": "마스터 정지 ■",
"update": "업데이트 ► F5",
"save": "저장"
},
"headline": {
"createrundowntip": "이 프로젝트에 새로운 룬을 생성하세요",
"edit": "룬 편집",
"select": "프로젝트 선택",
"selectfile": "룬 선택"
},
"pagetitle": {
"admin": "관리자",
"authpolicy": "사용자 액세스 구성",
"home": "환영합니다",
"login": "로그인",
"pickAllTemplates": "모든 템플릿을 룬에 추가",
"picktemplate": "템플릿을 룬에 추가",
"selectshow": "프로젝트 선택",
"select": "룬 선택",
"selectshowextra": "프로젝트에 추가 컨트롤 설정",
"selecttemplate": "템플릿 선택",
"selectfile": "파일 선택",
"shortcuts": "키보드 단축키",
"controller": "컨트롤러",
"configapp": "애플리케이션 구성",
"configshow": "프로젝트 설정",
"shows": "프로젝트 목록",
"f1ShortcutTip" : "이 도움말 화면입니다.",
"upDownShortcutTip" : "이전/다음 항목에 포커스",
"homeEndShortcutTip" : "첫 번째/마지막 항목에 포커스",
"enterShortcutTip" : "선택된 항목을 편집하기 위해 열기/닫기",
"spaceShortcutTip" : "포커스된 재생 목록 항목 재생/계속/중지",
"tipContinue" : "다음 명령 지원은 템플릿에서 구현되어야 합니다.",
"ctrlDShortcutTip" : "템플릿 항목 복제",
"shiftSpaceShortcutTip" : "포커스된 재생 목록 항목 재생/정지",
"f5ShortcutTip" : "항목에 즉시 업데이트 전송 (예: 오타 수정).",
"supportUpdateCommandTip" : "업데이트 명령 지원은 템플릿에서 구현되어야 합니다.",
"capsShortcutTip" : "빠른 정보 확인 및 API 명령어 복사를 위해 항목 오버레이 표시.",
"escShortcutTip": "현재 룬에 사용되는 모든 레이어 중지."
},
"config": {
"addnewtip": "채우고 저장하여 새 항목 추가",
"addfirsttemplate": "클릭 + 를 눌러 이 프로젝트에 템플릿을 추가하세요.",
"addfirstextra": "클릭 + 를 눌러 이 프로젝트에 추가 컨트롤을 추가하세요.",
"audioplayer": "오디오 플레이어",
"authpolicy": "사용자 액세스 구성. 사용자 이름과 비밀번호를 사용해야 하나요?",
"authinfo": "config-file에 비밀번호 없이 사용자 이름이 설정된 경우 이 대화 상자가 표시됩니다.",
"authnone": "애플리케이션은 사용자 이름이나 암호를 요구하지 않습니다.",
"backgroundImage": "정적 배경 이미지 (보통 투명)",
"playerflags": "플레이어 매개변수",
"preview": "미리 보기 작업",
"buttoncolor": "버튼 색상",
"buttontext": "버튼 캡션",
"buttontexts": "버튼 캡션 목록",
"buttontype": "버튼 유형",
"casparcg": "CasparCG 설정",
"ccgtemplates": "템플릿 폴더 (casparcg.config)",
"ccgplayout": "CasparCG 플레이아웃",
"ccgservers": "CasparCG 서버",
"coloraccent": "강조 색상",
"dataformat": "데이터 형식",
"dataroot": "데이터 폴더",
"defaultvalue": "기본 값",
"description": "설명",
"extrabutton": "추가 버튼",
"extrabuttondescription": "함수를 실행하는 간단한 푸시 버튼",
"extratoggle": "토글 버튼",
"extratoggledescription": "켜짐 및 꺼짐 상태에 대한 별도의 기능을 가진 버튼",
"extraselectbutton": "버튼이 있는 선택 드롭다운",
"extraselectdescription": "여러 옵션을 가진 드롭다운 셀렉터",
"functionlibrary": "함수 라이브러리 (/js/myFunctions.js)",
"functiondescription": "함수 설명",
"functionhandler": "핸들러 함수",
"general": "일반 설정",
"globalbuttons": "전역 추가 컨트롤",
"inputfields": "템플릿 필드",
"javascriptfile": "자바스크립트 파일 (/js/...) ",
"language": "사용자 인터페이스 언어",
"logfolder": "로그 폴더",
"loglevel": "로그 수준",
"openchrome": "재시작 시 Chrome 열기",
"options": "드롭다운 옵션 (텍스트, 값)",
"optionalBgImage": "폴더의 선택적 배경 이미지",
"projectvariables": "프로젝트 변수",
"resolution": "출력 해상도",
"resolutioninfo": "참고: 대부분의 템플릿은 기본적으로 HD(1920x1080)용으로 설계되었으며 다른 해상도를 지원하려면 변경이 필요할 수 있습니다.",
"restartinfo": "변경 사항을 적용하려면 SPX 프로세스를 재시작해야 합니다.",
"serverport": "포트",
"showextras": "프로젝트 추가 기능",
"stopmode": "애니메이션 정지 모드",
"templatefile": "템플릿 파일",
"templates": "이 프로젝트에 할당된 템플릿",
"templatepathinfo": "참고: 템플릿은 CasparCG 및 SPX 애플리케이션 모두에서 사용 가능해야 합니다. 이를 위해 CasparCG 템플릿 폴더를 구성하거나 두 애플리케이션에 템플릿 파일을 복사하세요.",
"templateroot": "HTML 템플릿 폴더",
"text": "표시되는 텍스트",
"updated": "업데이트됨",
"value": "값",
"webplayout": "웹 플레이아웃 레이어",
"webplayurl": "브라우저 URL",
"autoplay": "로딩 시 자동 재생 활성화"
},
"error": {
"fileExisted": "파일이 이미 존재합니다. 다시 시도해 주세요.",
"preventBrowseUp": "여기가 루트 템플릿 폴더입니다.",
"templateDefinitionMissing": "선택한 파일에서 SPXGCTemplateDefinition{}을 찾을 수 없습니다. 문서의 '템플릿' 섹션을 참조하세요."
},
"warning": {
"notemplates": "이 프로젝트에 연결된 템플릿이 아직 없습니다.",
"showprofilebackup": "변경하기 전에 프로필 파일의 백업 복사본을 만드는 것이 좋습니다.",
"severalControllers": "SPX 서버에 여러 컨트롤러가 연결되어 있습니다! 하나만 권장됩니다."
},
"hover": {
"accent": "룬에서의 템플릿 강조 색상.",
"addrundownitem": "룬 항목 추가",
"addshow": "새 프로젝트 생성",
"addrundown": "프로젝트에 새 룬 추가",
"addccgserver": "이름, IP 및 포트를 입력하여 새로운 CasparCG 서버를 추가하세요.",
"buttontype": "추가 컨트롤 유형.",
"browsetemplate": "이 프로젝트에 할당할 새 템플릿 검색",
"browseextras": "이 프로젝트에 할 새 추가 컨트롤 검색",
"browseglobalextras": "전역 추가 컨트롤 추가",
"cancelconfig": "변경 사항을 저장하지 않고 설정 닫기",
"ccgserver": "SPX 내 CasparCG 서버 이름",
"ccgchannel": "CasparCG 플레이아웃 채널",
"ccglayer": "CasparCG 플레이아웃 레이어",
"ccgtemplatepath": "casparcg.config 파일의 템플릿 폴더 값",
"ccghost": "CasparCG 서버 네트워크 이름 또는 IP 주소",
"ccgport": "CasparCG 서버 포트 (일반적으로 5250)",
"colors": "사용자 인터페이스의 추가 컨트롤 색상",
"config": "애플리케이션 설정 열기",
"confitem": "템플릿 설정",
"confjsfile": "전역 추가 기능 라이브러리 자바스크립트 파일",
"dataformat": "템플릿으로 전송할 데이터 형식",
"dataroot": "프로젝트 및 룬 파일을 저장할 데이터 폴더?",
"defaultvalue": "기본값은 사용 가능한 옵션 값 중 하나여야 합니다.",
"description": "설명",
"extrabutton": "버튼",
"extrabuttondescription": "함수를 실행하는 간단한 푸시 버튼",
"extratoggle": "토글 버튼",
"extratoggledescription": "켜짐 및 꺼짐 상태에 대한 별도의 기능을 가진 버튼",
"extraselectbutton": "버튼이 있는 선택 드롭다운",
"extraselectdescription": "여러 옵션을 가진 드롭다운 셀렉터",
"functionlibrary": "함수 라이브러리 (/js/myFunctions.js)",
"functiondescription": "함수 설명",
"functionhandler": "핸들러 함수",
"general": "일반 설정",
"globalbuttons": "전역 추가 기능",
"inputfields": "템플릿 필드",
"javascriptfile": "자바스크립트 파일 (/js/...)",
"language": "사용자 인터페이스 언어?",
"logfolder": "로그 파일 폴더",
"loglevel": "로깅 수준? 기본 수준: info. 대부분의 로그 정보: debug.",
"masterplay": "선택된 항목 재생/정지 (Space)",
"masterupdate": "선택된 항목 텍스트 업데이트 (F5)",
"mastercontinue": "다음 단계로 애니메이션 계속 진행 (Shift + Space)",
"navishows": "프로젝트 목록",
"navishow": "룬 목록",
"newversavailable": "새로운 버전이 있습니다. 클릭하여 더 읽기.",
"openbrowser": "서버 시작 시 Chrome 브라우저를 실행해야 하나요?",
"openrundown": "컨트롤러에서 선택된 룬 열기",
"options": "드롭다운 설정",
"outmode": "그래픽의 애니메이션 출력 모드",
"port": "사용할 애플리케이션 포트? (예: 3000 5000...). 참고: 이 페이지 주소도 변경될 수 있습니다!",
"profile": "이 프로젝트 설정 열기",
"playthis": "이 항목 재생 또는 정지",
"playouturl": "스트리밍 그래픽 애플리케이션을 위한 웹 플레이아웃 URL.",
"preview": "그래픽이 미리 보기 채널에 렌더링되는 시점 제어",
"removeglobalextra": "이 추가 기능을 모든 프로젝트에서 제거",
"removetemplate": "이 프로젝트에서 이 템플릿 제거",
"remoteplayouturl": "웹렌더가 외부 서버에 설치됨. (참고: 공용 IP 주소 필요)",
"replacetemplate": "소스 코드 변경 사항을 반영하여 템플릿 재불러오기.",
"resolution": "출력 해상도. 기본값은 HD(1920x1080)입니다.",
"saveconfig": "설정을 구성 파일에 저장",
"saveprofile": "프로젝트 설정 저장",
"showjsfile": "이 프로젝트의 자바스크립트 함수 라이브러리",
"showlist": "선택하거나 더블 클릭하여 프로젝트 열기",
"submitthis": "저장 및 편집기 닫기",
"templatepath": "HTML 템플릿 상대 경로",
"templatedescr": "GUI용 항목의 짧은 설명",
"text": "버튼 텍스트",
"text1": "활성화 시 버튼 텍스트",
"updated": "템플릿이 마지막으로 가져온 타임스탬프",
"updatethis": "이 항목 업데이트",
"weblayer": "웹 플레이아웃 레이어",
"exportitem": "이 템플릿 필드에서 CSV 파일 생성 ",
"importitems": "CSV 파일에서 룬 항목 생성",
"renameepisode": "선택된 룬 이름 변경"
}
}
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "Olá",
"slogan": "Controle e opere gráficos com CasparCG e aplicações de streaming.",
"folder": "Pasta",
"file": "Ficheiro",
"up": "Topo",
"field": "Campo",
"pcs": "peças",
"help": "Ajuda",
"home": "Home",
"yes": "Sim",
"no": "Não",
"none": "None",
"all": "All",
"fromFolder": "da pasta"
},
"color": {
"red": "Vermelho",
"green": "Verde",
"grey": "Cinzento",
"orange": "Laranja",
"blue": "Azul",
"black": "Preto",
"light": "Claro",
"white": "Branco",
"transparent": "Transparent"
},
"button": {
"add": "Novo",
"addplaylist": "Adicionar rundown",
"addRow": "Adicionar linha",
"back": "◂ Voltar",
"cancel": "Cancelar",
"config": "Configurações da aplicação",
"continue": "Continuar ►",
"delete": "Apagar",
"deletetemplate": "Remover template",
"deletebutton": "Remover botão",
"docs": "Documentação",
"duplicate": "Duplicate",
"help": "Ajuda",
"edit": "Editar",
"edittemplates": "Editar templates",
"exploredataroot": "Explorar pasta de dataroot",
"exploretemplates": "Explorar pasta de templates",
"filterprojects": "Start typing to filter projects...",
"launch": "Projetos",
"overlayon": "INICIAR FX",
"overlayoff": "PARA FX",
"open": "Abir rundown",
"play": "Iniciar ►",
"playmaster": "Iniciar ► Espaço",
"rename": "Rename",
"replacetemplate": "Re-import",
"selectemplate": "Selecionar",
"select": "Abrir projeto",
"settings": "Definições do projeto",
"shortcuts": "Atalhos do teclado",
"showwebrenderer": "Abrir janela de playout web",
"stop": "Parar ■",
"stopmaster": "Parar ◄ ESPAÇO",
"update": "Atualizar ► F5",
"save": "Guardar"
},
"headline": {
"edit": "Editar rundown",
"select": "Escolher projeto",
"selectfile": "Escolher rundown",
"createrundowntip": "Create a new rundown to this project"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "Configurar acesso dos utilizadores",
"home": "Bem-vindo",
"login": "Autenticar",
"pickAllTemplates": "Adicione template os modelos ao resumo",
"picktemplate": "Adicionar template ao rundown",
"selectshow": "Escolher projeto",
"select": "Escolher rundown",
"selectshowextra": "Adicionar controlo extra ao projeto",
"selectemplate": "Escolher template",
"selectfile": "Choose a file",
"shortcuts": "Atalhos do teclado",
"controller": "Controlador",
"configapp": "Configurações da aplicação",
"configshow": "Definições do projeto",
"shows": "Projetos",
"f1ShortcutTip" : "Esta tela de ajuda",
"upDownShortcutTip" : "Focar item anterior / próximo",
"homeEndShortcutTip" : "Focar primeiro / último item",
"enterShortcutTip" : "Abrir / fechar item focado para edição",
"spaceShortcutTip" : "Reproduzir / continuar / parar item da playlist focado",
"tipContinue" : "O suporte para o comando next deve ser implementado no template para usar o comando continue",
"ctrlDShortcutTip" : "Duplicar um item do template",
"shiftSpaceShortcutTip" : "Reproduzir / parar item da playlist focado",
"f5ShortcutTip" : "Enviar uma atualização instantânea para o template (por exemplo, para corrigir erros de digitação).",
"supportUpdateCommandTip" : "O suporte para o comando update deve ser implementado no template.",
"capsShortcutTip" : "Mostrar sobreposições dos itens para informações rápidas e cópia rápida de comandos da API.",
"escShortcutTip": "Parar todas as camadas usadas pelo rundown atual."
},
"config": {
"addnewtip": "Adicionar novo item preenchendo e gravando",
"addfirsttemplate": "Clique + para adicionar uma template a este projeto",
"addfirstextra": "Clique + para adicionar um contolo extra a este projeto",
"audioplayer": "Player de áudio",
"authpolicy": "Configuração de acesso de utilizador. Deverão ser utilizados nome de utilizador e palavra-passe?",
"authinfo": "Esta caixa de diálogo será apresentada se houver um utilizador sem palavra-passe definida em config-file.",
"authnone": "A apliação não necessida de nome de utilizador ou palavra-passe.",
"backgroundImage": "Static background image (usually transparent)",
"playerflags": "Parametros do player",
"buttoncolor": "Cor do botão",
"buttontext": "Texto do botão",
"buttontexts": "Legenda do botão",
"buttontype": "Tipo de botão",
"casparcg": "Configurações do CasparCG",
"ccgtemplates": "template-folder (casparcg.config)",
"ccgplayout": "Playout CasparCG",
"ccgservers": "Servidores CasparCG",
"coloraccent": "Cor de destaque",
"dataformat": "Formato de dados",
"dataroot": "Pasta de dados",
"defaultvalue": "Valor padrão",
"description": "Descrição",
"extrabutton": "Extra button",
"extrabuttondescription": "Um simples botão que executa uma função quando clicado",
"extratoggle": "Botão ligar/desligar",
"extratoggledescription": "Botão que executa funções separadas para ligar/desligar",
"extraselectbutton": "Lista de seleção com botão",
"extraselectdescription": "Menu suspenso com várias opções ",
"functionlibrary": "Biblioteca de funções (/js/myFunctions.js)",
"functiondescription": "Descrição da função",
"functionhandler": "Manipulador de eventos (função)",
"general": "Configurações gerais",
"globalbuttons": "Botões globais adicionais",
"inputfields": "Campos da template",
"javascriptfile": "Ficheiro Javascript (/js/…)",
"language": "Idioma da interface de utilizador",
"logfolder": "Pasta Log",
"loglevel": "Nível Log",
"openchrome": "Abrir o Chrome ao reiniciar",
"options": "Opções de menu (texto valor)",
"optionalBgImage": "Optional background image from folder",
"restartinfo": "Os processos do SPX irão necessitar de reiniciar para que as alterações tenham efeito",
"resolution": "Resolution",
"resolutioninfo": "Please note, templates are designed for HD by default and may need changes to support other resolutions.",
"serverport": "Porta",
"showextras": "Extras do projeto",
"stopmode": "Parar animação",
"templatefile": "Ficheiro template",
"templates": "Templates atribuídas ao projeto",
"templatepathinfo": "Nota: os modelos devem estar disponíveis para as aplicações CasparCG e GC. Configure esta pasta para ser a pasta de modelo CasparCG ou se necessário copie o modelo para a pasta de ambos os aplicativos.",
"templateroot": "Pasta de modelos HTML",
"text": "Texto visível",
"updated": "Updated",
"value": "Valor",
"webplayout": "Camada de playout web",
"webplayurl": "URL do browser",
"preview": "Preview action",
"projectvariables": "Project variables",
"autoplay": "Autoplay active layers on load"
},
"error": {
"fileExisted": "Ficheiro existente, por favor tente novamente.",
"preventBrowseUp": "Esta é a pasta raiz do modelo.",
"templateDefinitionMissing": "SPXGCTemplateDefinition{} não encontrado no arquivo escolhido. Consulte na documentação a secção sobre os modelos."
},
"warning": {
"notemplates": "There are no templates linked to this project yet",
"showprofilebackup": "É recomendado fazer um backup do arquivo profile.json antes de fazer qualquer alteração",
"severalControllers": "Vários controladores estão conectados ao servidor SPX! Apenas um recomendado."
},
"hover": {
"accent": "Cor de destaque da template nos rundowns",
"addrundownitem": "Adicionar item ao rundown",
"addshow": "Criar novo projeto",
"addrundown": "Adicionar novo rundown ao projeto",
"addccgserver": "Para adicionar um novo servidor CasparCG insira as informações de NOME IP e PORTA",
"buttontype": "Tipo de controlo adicional",
"browsetemplate": "Procurar uma nova template para associar a este projeto",
"browseextras": "Procurar um novo controlo extra para associar a este projeto",
"browseglobalextras": "Adicionar um controlo extra global",
"cancelconfig": "Fechar config sem gravar as alterações",
"ccgserver": "Nome do servidor CasparCG na SPX",
"ccgchannel": "Canal de playout do CasparCG",
"ccglayer": "Camada de playout do CasparCG",
"ccgtemplatepath": "Valor do parâmetro template-folder no arquivo casparcg.config",
"ccghost": "Nome da rede ou endereço IP do servidor CasparCG",
"ccgport": "Porta do servidor CasparCG (geralmente 5250)",
"colors": "Cor do controlo extra na interface do utilizador",
"config": " Abrir configurações da aplicação",
"confitem": "Configurações da template",
"confjsfile": "Biblioteca de extras globais - Javascript",
"dataformat": "Formato dos dados a serem enviados para a template",
"dataroot": " Pasta de dados para armazenar projetos e ficheiros de rundown?",
"defaultvalue": " Prédefinido deverá ser um valor de opção disponível",
"delete": "Remover item",
"deletethis": "Remover este item do rundown",
"deletepisode": "Apagar o rundown selecionado incluindo todos os itens",
"deleteshow": "Apagar o projeto selecionado incluindo as configurações e todos os rundowns",
"docs": "Abrir documentação",
"duplicateitem": "Duplicar este item",
"duplicaterundown": "Criar uma cópia do rundown selecionado",
"epslist": "Selecione ou clique duas vezes para abir o rundown",
"extradescr": "Descrição da função extra / tecla de função na interface do utilizador",
"fcall": "Função de manipulador de eventos na biblioteca de funções (por exemplo helloWorld)",
"fields": "Campos de SPXGCTemplateDefinition digitalizados | digitalizado da template",
"github": "Abrir no GitHub a ppágina de projeto da SPX",
"help": "Abrir ajuda acerca desta secção",
"htmlfolder": "Endereço Absoluto para a pasta de modelos do CasparCG?",
"item": "Faça duplo clique (ou Enter) para abrir / fechar o item no editor. Arraste para ordenar os itens.",
"lang": "Idioma da interface de utilizador (Pode criar novos arquivos de idioma conforme necessário para tal basta adicionar à pasta locales)",
"logfolder": "Pasta para log files",
"loglevel": "Qual o nível de registo usado? Padrão: informações. A maioria das informações de log: depuração.",
"masterplay": "Iniciar / para o item selecionado (Espaço)",
"masterupdate": "Atualizar textos do item selecionado",
"mastercontinue": "Continuar a animação para o próximo passo (Shift + Espaço)",
"navishows": "Projetos",
"navishow": "Rundowns",
"newversavailable": "Está disponível uma nova versão. Clique aqui para ler mais.",
"openbrowser": "Deverá o browser Chrome abrir ao iniciar o servidor?",
"openrundown": "Abrir o rundown selecionado no controlador",
"options": "Definições de dropdown / lista",
"outmode": "Animação de saída do gráfico",
"port": "Porta da aplicação a utilizar (Por exemplo 3000 5000 …) Atenção: O ENDEREÇO DESTA PÁGINA (também) irá mudar / sofrer (a) alteração!",
"profile": "Abrir definições deste projeto",
"playthis": "Reproduzir ou para este item",
"playouturl": "URL do web playout para transmitir/a transmissão de gráficos / programa de transmissão de gráficos",
"removeglobalextra": "Remover este extra de todos os projetos",
"removetemplate": "Remover do projeto esta template deste projeto",
"remoteplayouturl": "Webrender instalado num servidor externo. (Atenção é necessário um adereço IP público)",
"replacetemplate": "Re-import template if changes made to source code.",
"resolution": "Re-import template from html file (changes will be overwritten with defaults)",
"saveconfig": "Guardar as configurações num config file / ficheiro config",
"saveprofile": "Guardar as configurações do projeto",
"showjsfile": "Biblioteca de funções Javascript deste projeto",
"showlist": "Selecione ou clique duas vezes para abrir o projeto / no projeto a abrir",
"submitthis": "Gravar e fechar o editor",
"templatepath": "Endereço / caminho de arquivo das templates HTML",
"templatedescr": "Breve descrição do item para a interface gráfica GUI",
"text": "Texto do botão",
"text1": "Texto do botão (quando ativo)",
"updated": "Atualizar este item",
"updatethis": "Timestamp when template was last imported",
"weblayer": "Camada / Canal / Layer de webplayout",
"preview": "Control when graphics are rendered to the preview channel",
"exportitem": "Create a CSV file from fields of this template ",
"importitems": "Create rundown items from a CSV file",
"renameepisode": "Rename selected rundown"
}
}
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "欢迎",
"slogan": "这是一款管理和控制 Caspar CG 和直播流图形的应用。",
"folder": "文件夹",
"file": "文件",
"up": "上一层",
"field": "字段",
"pcs": "数量",
"help": "帮助",
"yes": "是",
"no": "否",
"none": "无",
"all": "All",
"fromFolder": "从文件夹",
"home": "Home"
},
"color": {
"red": "红色",
"green": "绿色",
"grey": "灰色",
"orange": "橙色",
"blue": "蓝色",
"black": "黑色",
"light": "亮色",
"white": "白色",
"transparent": "透明"
},
"button": {
"add": "新建",
"addplaylist": "新建播出表",
"addRow": "新建行",
"back": "◂ 返回",
"cancel": "取消",
"config": "配置",
"continue": "继续 ►",
"delete": "删除",
"deletetemplate": "删除模板",
"deletebutton": "移除按钮",
"docs": "文档",
"duplicate": "复制",
"help": "帮助",
"edit": "编辑",
"edittemplates": "编辑模板",
"exploredataroot": "探索根数据文件夹",
"exploretemplates": "探索模板文件夹",
"launch": "工程",
"overlayon": "播放特技",
"overlayoff": "停止特辑",
"open": "打开播出表",
"play": "播放 ►",
"playmaster": "播放 ►",
"rename": "重命名",
"replacetemplate": "重新导入",
"selectemplate": "选择",
"select": "打开工程",
"settings": "工程设置",
"shortcuts": "快捷键设置",
"stop": "停止 ■",
"stopmaster": "停止 ◄",
"showwebrenderer": "打开网页预览窗口",
"update": "更新 ► F5",
"save": "保存",
"filterprojects": "Start typing to filter projects..."
},
"headline": {
"createrundowntip": "在这里新建播出表",
"edit": "编辑播出表",
"select": "选择工程",
"selectfile": "选择播出表"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "管理用户访问",
"home": "欢迎",
"login": "登录",
"pickAllTemplates": "将所有模板添加到列表中",
"picktemplate": "新建模板到播出表",
"selectshow": "选择工程",
"select": "选择播出表",
"selectshowextra": "为工程添加额外控制",
"selectemplate": "选择模板",
"selectfile": "选择一个文件",
"shortcuts": "键盘快捷键",
"controller": "控制器",
"configapp": "配置",
"configshow": "工程设置",
"shows": "工程",
"f1ShortcutTip" : "此帮助界面",
"upDownShortcutTip" : "聚焦上一个 / 下一个项目",
"homeEndShortcutTip" : "聚焦第一个 / 最后一个项目",
"enterShortcutTip" : "打开 / 关闭当前聚焦项目进行编辑",
"spaceShortcutTip" : "播放 / 继续 / 停止当前聚焦的播放列表项目",
"tipContinue" : "必须在模板中实现对 next 命令的支持 要使用 continue",
"ctrlDShortcutTip" : "复制一个模板项目",
"shiftSpaceShortcutTip" : "播放 / 停止当前聚焦的播放列表项目",
"f5ShortcutTip" : "立即向模板发送更新(例如修正拼写错误)。",
"supportUpdateCommandTip" : "必须在模板中实现对 update 命令的支持。",
"capsShortcutTip" : "显示项目覆盖层以快速查看信息并快速复制 API 命令。",
"escShortcutTip": "停止当前 rundown 使用的所有图层。"
},
"config": {
"addnewtip": "填写新工程并保存",
"addfirsttemplate": "单击+将模板添加到该工程",
"addfirstextra": "单击+将额外控件添加到该工程",
"audioplayer": "音频播放器",
"authpolicy": "用户访问配置。是否需要使用用户名和密码?",
"authinfo": "如果在 config-file 中设置了用户名但未设置密码,则会显示此提示。",
"authnone": "应用程序不需要用户名或密码。",
"backgroundImage": "静态背景图像(默认透明)",
"buttoncolor": "按钮颜色",
"buttontext": "按钮文字",
"buttontexts": "按钮文字",
"buttontype": "按钮类型",
"casparcg": "CasparCG 设置",
"ccgtemplates": "模板文件目录 casparcg.config",
"ccgplayout": "CasparCG 播放列表",
"ccgservers": "CasparCG 服务器",
"coloraccent": "强调色",
"dataformat": "数据格式",
"dataroot": "数据文件夹",
"defaultvalue": "默认数据值",
"description": "描述",
"extrabutton": "按钮",
"extrabuttondescription": "通过简单的按钮执行功能",
"extratoggle": "开关按钮",
"extratoggledescription": "开关按钮可以切换状态",
"extraselectbutton": "下拉菜单按钮",
"extraselectdescription": "具有多个选项的下拉菜单",
"functionlibrary": "函数库",
"functiondescription": "函数介绍",
"functionhandler": "处理函数",
"general": "通用设置",
"globalbuttons": "全局附加",
"inputfields": "模板字段",
"javascriptfile": "在 ASSETS 文件夹中的 Javascript 文件路径",
"language": "选择界面语言",
"logfolder": "日志文件夹",
"loglevel": "日志等级",
"openchrome": "重启时打开 Chrome",
"options": "下拉选项(文本,值)",
"optionalBgImage": "背景图像文件夹(可选)",
"playerflags": "播放器参数",
"restartinfo": "SPX 必须重新启动才能使更改生效",
"serverport": "端口",
"showextras": "工程附加",
"stopmode": "停止动画",
"templatefile": "模板文静",
"templates": "将模板分配给此工程",
"templatepathinfo": "注意,模板必须对 CasparCG 和 SPX 服务器均可用。将其配置为 CasparCG 模板文件夹,或者在必要时将模板文件复制到两个应用程序中。",
"templateroot": "HTML 模板文件夹",
"text": "可见文本",
"updated": "更新",
"value": "值",
"webplayout": "网络播出层",
"webplayurl": "浏览器 URL",
"resolution": "Resolution",
"resolutioninfo": "Please note, templates are designed for HD by default and may need changes to support other resolutions.",
"preview": "Preview action",
"projectvariables": "Project variables",
"autoplay": "Autoplay active layers on load"
},
"error": {
"fileExisted": "文件已存在,请重试。",
"preventBrowseUp": "这是根模板文件夹。",
"templateDefinitionMissing": "所选文件中缺少 SPXGCTemplateDefinition{} 参数。请参考帮助文档中的《HTML 模板》章节。"
},
"warning": {
"showprofilebackup": "建议在进行更改之前对工程文件进行备份。",
"notemplates": "There are no templates linked to this project yet",
"severalControllers": "多个控制器连接到 SPX 服务器!只推荐一款。"
},
"hover": {
"accent": "播出表模板强调色",
"addrundownitem": "添加项到播出表",
"addshow": "创建新工程",
"addrundown": "创建新播出表到工程",
"addccgserver": "输入名称,IP地址和端口号来添加新的 Caspar CG 的服务器",
"buttontype": "按钮类型",
"browsetemplate": "浏览并将新模板分配给当前工程",
"browseextras": "浏览并将新的额外控件分配给当前工程",
"browseglobalextras": "添加全局额外控制",
"cancelconfig": "关闭配置,不保存更改",
"ccgserver": "SPX 中 CasparCG 服务器名称",
"ccgchannel": "CasparCG 播出频道",
"ccglayer": "CasparCG 播出层级",
"ccgtemplatepath": "Caspar CG 中模板文件夹路径",
"ccghost": "CasparCG 服务器网络名称或 IP 地址",
"ccgport": "CasparCG 服务器端口(默认为 5250",
"colors": "额外控制的显示色彩",
"config": "打开应用配置",
"confitem": "模板设置",
"confjsfile": "附加全局的 Javascript 库",
"dataformat": "发送到模板的数据格式",
"dataroot": "用于存储工程和播出表的数据文件夹?",
"defaultvalue": "默认值必须是可用的选项值之一",
"delete": "移除项",
"deletethis": "将此项从播出表中移除",
"deletepisode": "删除所选择的播出表,包括播出表中的项",
"deleteshow": "删除选择的工程,包括工程中所有的播出表和设置",
"docs": "打开文档",
"duplicateitem": "复制此项",
"duplicaterundown": "创建所选播出表的副本",
"epslist": "选择或双击打开播出表",
"exportitem": "将此项导出为 CSV 文件",
"extradescr": "额外的函数描述",
"fcall": "函数库中的事件处理函数(例如 helloWorld",
"fields": "扫描 SPXGCTemplateDefinition 字段",
"github": "打开网站",
"help": "打开关于此段的帮助内容",
"htmlfolder": "使用 CasparCG 模板文件夹的绝对路径?",
"importitems": "导入多个播出表项的 CVS 文件",
"item": "双击(或回车)打开/关闭工程编辑器。拖动可对工程进行排序。",
"lang": "用户界面语言。(您可以根据需要在 locale 文件夹中创建新的语言文件)",
"logfolder": "日志文件目录",
"loglevel": "日志级别。默认级别:信息。多数日志信息:调试。",
"masterplay": "播放/停止选定项(空格键)",
"masterupdate": "更新所选项的文本(F5",
"mastercontinue": "继续执行动画到下一步(Shift+空格键)。仅在包含多步骤模板时操作有效。",
"navishows": "工程",
"navishow": "播出表",
"newversavailable": "有更新的版本。点击查看更多信息。",
"openbrowser": "Chrome 浏览器应该在服务器启动时启动吗?",
"openrundown": "在控制器中打开选定的播出表",
"options": "下拉菜单设置",
"outmode": "图形的出场动画",
"port": "要使用的应用程序端口(例如3000 5000…)。请注意:更改端口会导致网页地址更改。",
"profile": "打开工程设置",
"playthis": "播放或停止此项",
"playouturl": "流媒体图形应用程序的 Web 播放 URL。",
"removeglobalextra": "移除工程中的所有额外内容",
"removetemplate": "在此工程中移除该模板",
"remoteplayouturl": "Webrender 安装到外部服务器。(注意:需要公网IP地址)",
"renameepisode": "更改播出表名称",
"replacetemplate": "从 .html 文件重新导入模板(参数和值将被默认值覆盖)",
"saveconfig": "将程序配置保存到配置文件",
"saveprofile": "保存工程设置",
"showjsfile": "本工程的 Javascript 函数库",
"showlist": "选择或双击工程以打开它",
"submitthis": "保存并关闭编辑器",
"templatepath": "HTML 模板的相对文件路径",
"templatedescr": "GUI 工程的简短描述",
"text": "按钮文本",
"text1": "按钮激活时的文本",
"updated": "上次导入模板时的时间戳",
"updatethis": "更新此项",
"weblayer": "网络播出层",
"resolution": "Output resolution. The default is HD (1920x1080).",
"preview": "Control when graphics are rendered to the preview channel"
}
}
+251
View File
@@ -0,0 +1,251 @@
{
"general": {
"hello": "Hejssan",
"slogan": "Hantera och kontrollera grafik för CasparCG eller streamingapplikationer.",
"folder": "Mapp",
"file": "Fil",
"up": "Upp",
"field": "Fält",
"pcs": "st",
"help": "Hjälp",
"yes": "Ja",
"no": "Nej",
"none": "Ingen",
"all": "All",
"fromFolder": "från mappen",
"home": "Home"
},
"color": {
"red": "Röd",
"green": "Grön",
"grey": "Grå",
"orange": "Orange",
"blue": "Blå",
"black": "Svart",
"light": "Ljus",
"white": "Vit",
"transparent": "Transparent"
},
"button": {
"add": "Ny",
"addplaylist": "Lägg till rundown",
"addRow": "Lägg till en rad",
"back": "◂ Tillbaka",
"cancel": "Avbryt",
"config": "Applikationsinställningar",
"continue": "Fortsätt ►",
"delete": "Radera",
"deletetemplate": "Ta bort templaten ",
"deletebutton": "Ta bort knappen",
"docs": "Dokumentation",
"duplicate": "Duplicera",
"help": "Hjälp",
"edit": "Redigera",
"edittemplates": "Redigera templates / mallar",
"exploredataroot": "Utforska dataroot-mappen",
"exploretemplates": "Utforska template/mallarmappen",
"filterprojects": "Börja skriva för att filtrera projekt...",
"launch": "Projekt",
"overlayon": "FX PLAY",
"overlayoff": "FX STOP",
"open": "Öppna rundown",
"play": "Spela►",
"playmaster": "Spela►Mellanslag",
"rename": "Byt namn",
"replacetemplate": "Återimport",
"selectemplate": "Välj",
"select": "Öppna projekten",
"settings": "Projektinställningar",
"shortcuts": "Visa genvägar",
"showwebrenderer": "Öppna web playout fönster",
"stop": "Sluta ■",
"stopmaster": "Sluta ◄ Mellanslag",
"update": "Uppdatera ► F5",
"save": "Spara"
},
"headline": {
"edit": "Redigera innehållslistan",
"select": "Välj projekten",
"selectfile": "Välj rundown",
"createrundowntip": "Create a new rundown to this project"
},
"pagetitle": {
"admin": "Admin",
"authpolicy": "Inloggningsdefinition",
"home": "Välkommen",
"login": "Logga in",
"pickAllTemplates": "Lägg till alla templaten i rundown",
"picktemplate": "Lägg till templaten i rundown",
"selectshow": "Välj projekten",
"select": "Välj en innehållsfil",
"selectshowextra": "Lägg till extra kontroll till projektet",
"selectemplate": "Välj en template",
"selectfile": "Välj en fil",
"shortcuts": "Genvägar",
"controller": "Controller",
"configapp": "Applikationsinställningar",
"configshow": "Projektinställningar",
"shows": "Projekt",
"f1ShortcutTip" : "Den här hjälpskärmen",
"upDownShortcutTip" : "Fokusera föregående / nästa objekt",
"homeEndShortcutTip" : "Fokusera första / sista objektet",
"enterShortcutTip" : "Öppna / stäng fokuserat objekt för redigering",
"spaceShortcutTip" : "Spela / fortsätt / stoppa fokuserat spellisteobjekt",
"tipContinue" : "Stöd för next-kommandot måste implementeras i mallen för att använda continue",
"ctrlDShortcutTip" : "Duplicera ett mallobjekt",
"shiftSpaceShortcutTip" : "Spela / stoppa fokuserat spellisteobjekt",
"f5ShortcutTip" : "Skicka en omedelbar uppdatering till mallen (för att till exempel rätta stavfel).",
"supportUpdateCommandTip" : "Stöd för update-kommandot måste implementeras i mallen.",
"capsShortcutTip" : "Visa objektöverlägg för snabb info och snabb kopiering av API-kommandon.",
"escShortcutTip": "Stoppa alla lager som används av den aktuella rundownen."
},
"config": {
"addnewtip": "Lägg till ett nytt alternativ genom att fylla i en tom rad och spara",
"addfirsttemplate": "Klicka + för att lägga till en template till detta projekt",
"addfirstextra": "Klicka + för att lägga till extra kontroll / funktion till projektet",
"audioplayer": "Audioplayer",
"authpolicy": "Inloggningsinställningar: Använd ett användarnamn och lösenord?",
"authinfo": "Den här inställningen visas om ett användarnamn men inget lösenord anges i konfigurationsfilen.",
"authnone": "Användning av applikationen kräver inget användarnamn eller lösenord.",
"backgroundImage": "Statisk bakgrund (vanligtvis genomskinlig)",
"playerflags": "Player parametrar",
"buttoncolor": "Knappens färg",
"buttontext": "Kanpp-text",
"buttontexts": "Knapptexter",
"buttontype": "Knapptyp",
"casparcg": "CasparCG-inställningar",
"ccgtemplates": "template-mapp (casparcg.config)",
"ccgplayout": "CasparCG playout",
"ccgservers": "CasparCG-servrar",
"coloraccent": "Markeringsfärg",
"dataformat": "Dataöverföringsformat",
"dataroot": "Datamapp",
"defaultvalue": "Standardvärde",
"description": "Beskrivning",
"extrabutton": "Knapp",
"extrabuttondescription": "Enkel tryckknapp som utför en funktion",
"extratoggle": "På/av-knapp",
"extratoggledescription": "Knapp som utför separata på/av-funktioner",
"extraselectbutton": "Urvalslista och knapp",
"extraselectdescription": "Rullgardinsmeny med flera alternativ och en funktionsknapp",
"functionlibrary": "Funktionsbibliotek (/js/myFunctions.js)",
"functiondescription": "Funktionsbeskrivning",
"functionhandler": "Händelsehanterare (funktion)",
"general": "Allmänna inställningar",
"globalbuttons": "Ytterligare globala knappar",
"inputfields": "Templatefält",
"javascriptfile": "Javascript-fil (/ js / ...)",
"language": "Användargränssnittsspråk",
"logfolder": "Loggmapp",
"loglevel": "Loggnivå",
"openchrome": "Öppna Chrome vid omstart",
"options": "Alternativ för rullgardinsmeny (text, värde)",
"optionalBgImage": "Valfri bakgrundsbild från mapp",
"restartinfo": "SPX-processen måste startas om efter ändringarna.",
"resolution": "Upplösning",
"resolutioninfo": "Observera att templaten är designade för HD som standard och kan behöva ändringar för att stödja högre upplösningar.",
"serverport": "Porten för denna applikation",
"showextras": "Avancerade projektfunktioner",
"stopmode": "Stop animation",
"templatefile": "Template fil",
"templates": "Templaten kopplade till projektet",
"templatepathinfo": "Observera att templaten måste vara tillgänglig för både CasparCG- och SPX-applikationer. Konfigurera CasparCG-mappen här eller, om nödvändigt, kopiera templaten till mapparna för båda applikationerna.",
"templateroot": "HTML-templatemapp",
"text": "Synlig text",
"updated": "Uppdaterad",
"value": "Värde",
"webplayout": "Webb playout nivå",
"webplayurl": "Browser URL",
"preview": "Preview action",
"projectvariables": "Project variables",
"autoplay": "Autoplay active layers on load"
},
"error": {
"fileExisted": "Filen fanns redan. Försök igen.",
"preventBrowseUp": "Detta är en template mapp.",
"templateDefinitionMissing": "Den valda templatefilen saknade window.SPXGCTemplateDefinition {}. Se avsnittet templatefiler för dokumentation."
},
"warning": {
"notemplates": "There are no templates linked to this project yet",
"showprofilebackup": "Det rekommenderas att du säkerhetskopierar filen profile.json innan du gör några ändringar.",
"severalControllers": "Flera kontroller är anslutna till SPX-servern! Endast en rekommenderas."
},
"hover": {
"accent": "Templatens markeringsfärg i rundown",
"addrundownitem": "Lägg till ett element i rundown",
"addshow": "Skapa ett nytt projekt",
"addrundown": "Lägg till ett nytt rundown till projektet",
"addccgserver": "Ange NAME IP och PORT information för att lägga till en ny CasparCG server",
"buttontype": "Avancerad funktionstyp (knapp, växlingsknapp ...)",
"browsetemplate": "Lägg till en ny template till det här projektet genom att bläddra i templatemappen ...",
"browseextras": "Lägg till en ny funktion till det här projektet ...",
"browseglobalextras": "Lägg till en ny global funktion ...",
"cancelconfig": "Stäng konfigurationen utan att spara ändringarna",
"ccgserver": "CasparCG utloggningsservernamn",
"ccgchannel": "CasparCG-kanal på servern",
"ccglayer": "CasparCG-nivåkanal",
"ccgtemplatepath": "Template-mappvärde från casparcg.config -fil",
"ccghost": "CasparCG server nätverksnamn eller IP-adress",
"ccgport": "CasparCG serverport (vanligtvis 5250)",
"colors": "Extra kontrollfärg i användargränssnittet",
"config": "Öppna applikationskonfigurationen",
"confitem": "Template inställningar",
"confjsfile": "Javascript - bibliotek med globala extramaterial",
"dataformat": "I vilket format skickas data till templaten?",
"dataroot": "I vilken mapp lagras projekt- och rundownfiler?",
"defaultvalue": "Standardvärdet bör vara ett av alternativen",
"delete": "Ta bort objektet",
"deletethis": "Ta bort det här objektet från listan",
"deletepisode": "Ta bort det valda rundown och alla dess element",
"deleteshow": "Ta bort det valda projektet och alla dess inställningar och rundown",
"docs": "Öppna dokumentationen",
"duplicateitem": "Duplicera detta element",
"duplicaterundown": "Skapa en kopia av det valda rundownt",
"epslist": "Välj eller dubbelklicka på önskat rundown",
"extradescr": "Extra funktionsbeskrivning",
"fcall": "Händelsehanterarfunktion i funktionsbiblioteket (t.ex. helloWorld)",
"fields": "Skannade SPXGCTemplateDefinition-fält",
"github": "Öppna SPX-projektsidan i GitHub",
"help": "Öppna hjälp om detta avsnitt",
"htmlfolder": "Absolut mappsökväg till CasparCG templatemapp?",
"item": "Dubbelklicka (eller Enter) för att öppna/stänga objektredigeraren. Dra för att sortera objekt.",
"lang": "Gränssnittsspråkversionsfil? (Om det behövs kan du lägga till språkfiler i språkmappen.)",
"logfolder": "Mapp för loggfiler",
"loglevel": "Vilken loggnivå används? Standard: info. De flesta data lagras: debug.",
"masterplay": "Spela / stoppa aktivt element (mellanslag/Space)",
"masterupdate": "Uppdatera texter från valt objekt (F5)",
"mastercontinue": "Fortsätt animeringen till nästa steg (Skift + Mellanslag)",
"navishows": "Projekt",
"navishow": "Rundowns",
"newversavailable": "En nyare version finns tillgänglig. Klicka för att läsa mer.",
"openbrowser": "Bör webbläsaren Chrome startas vid serverstart?",
"openrundown": "Öppna det valda rundown för redigering eller avsluta",
"options": "Inställningar för rullgardinsmeny",
"outmode": "Ut animering av grafiken",
"port": "Programport att använda? (Till exempel 3000 5000...) Observera: ADRESSEN FÖR DENNA SIDA kommer också att ändras!",
"profile": "Öppna inställningarna för detta projekt",
"playthis": "Spela / stoppa detta objekt",
"playouturl": "Lokal webplayout-adress genererad av GC för stream-grafik",
"removeglobalextra": "Ta bort den här knappen från alla projekt",
"removetemplate": "Ta bort den här templaten från projektet",
"remoteplayouturl": "Webrender installerad på en extern server. (Obs, offentliga IP-adresser krävs)",
"replacetemplate": "Återimportera templaten om ändringar görs i källkoden.",
"resolution": "Upplösning. Standard är HD (1920x1080).",
"saveconfig": "Spara inställningarna i programmets konfigurationsfil",
"saveprofile": "Spara projektinställningarna",
"showjsfile": "Javascript-funktionsbibliotek för detta projekt",
"showlist": "Välj eller dubbelklicka på projekt för att öppna det",
"submitthis": "Spara och stäng editorn",
"templatepath": "Den relativa sökvägen till HTML-grafikmallen i templatemappen",
"templatedescr": "En kort beskrivning av elementet i GC-gränssnittet",
"text": "Knapp-text",
"text1": "Knapptext nedtryckt",
"updated": "Tidsstämpel för den senaste uppdateringen",
"updatethis": "Uppdatera detta objekt",
"weblayer": "Webplayout-kanal",
"preview": "Control when graphics are rendered to the preview channel",
"exportitem": "Create a CSV file from fields of this template ",
"importitems": "Create rundown items from a CSV file",
"renameepisode": "Rename selected rundown"
}
}
+3521
View File
File diff suppressed because it is too large Load Diff
+55
View File
@@ -0,0 +1,55 @@
{
"name": "spx-gc",
"version": "1.4.1",
"description": "SPX Graphics Controller",
"homepage": "https://spxgraphics.com",
"main": "server.js",
"bin": "server.js",
"pkg": {
"assets": [
"views/**/*",
"static/**/*"
]
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"devORG": "nodemon server.js -i config.json -i DATAROOT/",
"dev": "nodemon server.js -i DATAROOT/* -i ASSETS/* -i config.json -i DATAROOT_VIDEO/*",
"devdemo": "nodemon server.js config_demodataroot.json -i DATAROOT_DEMO/* -i config_demodataroot.json",
"dev2": "nodemon server.js config2.json -i DATAROOT/* -i config2.json",
"start": "pm2 start server.js --name GC",
"stop": "pm2 stop server.js",
"build": "BUILD_PACKAGES.BAT"
},
"keywords": [],
"author": "Tuomo Kulomaa <tuomo@spxgraphics.com> (https://spxgraphics.com)",
"license": "MIT",
"dependencies": {
"axios": "^0.30.0",
"body-parser": "^1.20.3",
"cors": "^2.8.5",
"express": "^4.21.2",
"express-handlebars": "^8.0.1",
"express-session": "^1.18.1",
"glob": "^11.0.0",
"ip": "^2.0.1",
"jsdom": "^25.0.1",
"macaddress": "^0.5.1",
"moment": "^2.29.4",
"morgan": "^1.9.1",
"node-html-parser": "^6.1.13",
"node-wav-player": "^1.0.0",
"node-xlsx": "^0.24.0",
"open": "^10.1.0",
"openurl": "^1.1.1",
"osc": "^2.4.5",
"path": "^0.12.7",
"rotating-file-stream": "^3.2.5",
"socket.io": "^4.8.0",
"sox-play": "0.0.2",
"winston": "^3.17.0"
},
"devDependencies": {
"nodemon": "^3.1.7"
}
}
+786
View File
@@ -0,0 +1,786 @@
/* ------------------------------------------
Public API routes for external controllers
such as Stream Deck or similar.
/api/v1/
Home route is a list of available commands.
--------------------------------------------- */
var express = require("express");
const router = express.Router();
const path = require('path');
const fs = require('fs');
const moment = require('moment');
const directoryPath = path.normalize(config.general.dataroot);
const logger = require('../utils/logger');
logger.debug('API-v1 route loading...');
const spx = require('../utils/spx_server_functions.js');
const xlsx = require('node-xlsx').default;
const axios = require('axios')
const PlayoutCCG = require('../utils/playout_casparCG.js');
const { query } = require("../utils/logger");
const spxAuth = require('../utils/spx_auth.js');
let apiCache = [] // used to cache [undocumented] API calls
let ack = 'Sent request to SPX server. Acknowledgement is not to be expected.'
let ack2 = 'Sent request to SPX Controller. Acknowledgement is not to be expected.'
const apiHandler = require('../utils/api-handlers.js');
const notInSolo = {
status: 501,
message: 'Not Implemented',
info: 'This API endpoint is not available in WebCG.',
more: 'For advanced API features, please see WebCG Production or Broadcast'
}
// ROUTES -------------------------------------------------------------------------------------------
router.get('/', function (req, res) {
let functionsDoc = {
"sections": [
{
"section": "Common API",
"info": "Generic API endpoints and utilities available in all WebCG versions without a license.",
"commands": [
{
"vers": "v1.1.2",
"method": "GET",
"param": "/api/v1/version",
"info": "Returns SPX version info and current host-id"
},
{
"vers": "v1.1.0",
"method": "GET",
"param": "/api/v1/panic",
"info": "Force clear to all output layers without out-animations. (Note, this does NOT save on-air state of rundown items to false, so when UI is reloaded the items will show the state before panic was triggered.) This is to be used for emergency situations only and not as a normal STOP command substitute."
},
{
"vers": "v1.0.14",
"method": "GET",
"param": "/api/v1/feedproxy?url=https://feeds.bbci.co.uk/news/rss.xml&format=xml",
"info": "A proxy endpoint for passing feed data from CORS protected datasources. (If you need to pass url parameters use <code>%26</code> instead of <code>&</code> to separate them)."
},
{
"vers": "v1.3.0",
"method": "POST",
"param": "/api/v1/feedproxy",
"info": "A POST version of the feedproxy endpoint. This endpoint is a helper for outgoing GET or POST requests requiring custom headers, such as <code>Authorization</code> or similar. Data is passed to the helper in the <code>body</code> of the POST request, in which <code>url</code> is the actual URL of the external endpoint. If the body contains a <code>postBody</code> -object, it will be passed to the outgoing API request as <code>body</code>. See principle in the example below, or search SPX Knowledge Base for more info with keyword <code>feedproxy</code>.",
"code": { url: "https://api.endpoint.com/requiring/customheaders/", headers: { "key1": "my first value", "key2": "second value" }, postBody: { info: "If postBody is found, the outgoing request will be done using POST method, otherwise as GET." } }
},
]
},
{
"section": "Controller API",
"restriction": "Some endpoints require WebCG Production license",
"info": "API endpoints for controlling the WebCG Graphics Controller rundown. Please note some endpoints will require a valid WebCG Production license.",
"commands": [
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/focusPrevious",
"info": "Move focus up to previous item, will not circle back to bottom when top is reached."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/focusNext",
"info": "Move focus down to next item, will not circle back to top when end is reached."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/stopAllLayers",
"info": "Animate all layers (used by the current rundown) out, but does not clear layers."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/item/play",
"info": "Start focused item."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/item/continue",
"info": "Issue continue command to selected item. Notice this needs support from the template itself and does not work as play or stop."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/item/stop",
"info": "Stop focused item."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/load?file=MyFirstProject/MyFirstRundown",
"info": "Open rundown from project / file."
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/focusFirst",
"info": "Move focus to the first item on the rundown.",
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/focusLast",
"info": "Move focus to the last item on the rundown.",
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/rundown/focusByID/1234567890",
"info": "Move focus by ID on the rundown.",
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/item/play/1234567890",
"info": "Start item by ID on the active rundown.",
"active": false,
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/item/continue/1234567890",
"info": "Continue to item by ID on the active rundown. Notice this needs support from the template itself and does not work as play or stop.",
"active": false,
},
{
"vers": "1.0",
"method": "GET",
"param": "/api/v1/item/stop/1234567890",
"info": "Stop item by ID on the active rundown.",
"active": false,
},
{
"vers": "v1.0.12",
"method": "GET",
"param": "/api/v1/invokeTemplateFunction?playserver=OVERLAY&playchannel=1&playlayer=19&webplayout=19&function=myCustomTemplateFunction&params=Hello%20World",
"info": "Uses an invoke handler to call a function in a template. See required parameters in the example call above. JSON objects can be passed as params by urlEncoding stringified JSON. Search SPX Knowledge Base for more info with keyword <code>invoke</code>.",
"active": false,
},
{
"vers": "v1.3.0",
"method": "GET",
"param": "/api/v1/invokeExtensionFunction?function=sendCmd&params=incrementNumber",
"info": "Uses SPX's messaging system to call a function in an extension. JSON objects can be passed as params by urlEncoding stringified JSON. The extension will need to implement SPX's messaging system, search SPX Knowledge Base for more info with keyword <code>invokeExtensionFunction</code>.",
"active": false,
},
]
},
{
"section": "Server API",
"restriction": "SPX Broadcast license required",
"info": "API endpoints targeting the SPX server directly without going through the rundown controller. These endpoints are for advanced uses and require a SPX Broadcast license.",
"commands": [
{
"vers": "v1.0.12, v.1.3.2",
"method": "POST",
"active": false,
"param": "/api/v1/directplayout",
"info": "Populate template and execute a play/continue/stop -command to it. Please note the optional <code>updateRundownItem</code> property. <code>updateRundownItemitemID</code> is an optional object for forcing UI updates and persisting to defined rundown file. <b>Please note: special charaters in values does not work at the moment!</b> Post request body example as JSON:",
"code": { casparServer: "OVERLAY", casparChannel: "1", casparLayer: "20", webplayoutLayer: "20", relativeTemplatePath: "/vendor/pack/template.html", out: "manual", DataFields: [{ field: "f0", value: "Firstname" }, { field: "f1", value: "Lastname" }], command: "play", updateRundownItem: { updateUI: true, itemID: "myItemID", persist: true, "project": "myFirstProject", "rundown": "myFirstRundown" } }
},
{
"vers": "v1.1.0",
"active": false,
"method": "GET",
"param": "/api/v1/controlRundownItemByID?file=MyProject/FirstRundown&item=1234567890&command=play",
"info": "Play / stop an item from a known rundown. (Remember you can rename rundown items from WebCG GUI)"
},
{
"vers": "v1.1.1",
"method": "GET",
"active": false,
"param": "/api/v1/getprojects",
"info": "Returns projects as an array of strings."
},
{
"vers": "v1.1.1",
"method": "GET",
"active": false,
"param": "/api/v1/getrundowns?project=MyProject",
"info": "Returns rundown names of a given project as an array of strings."
},
{
"vers": "v1.3.0",
"method": "GET",
"active": false,
"param": "/api/v1/allrundowns",
"info": "Returns all projects and rundowns"
},
{
"vers": "v1.1.1",
"method": "GET",
"active": false,
"param": "/api/v1/rundown/get",
"info": "Returns current rundown as json. "
},
{
"vers": "v1.3.0",
"method": "GET",
"active": false,
"param": "/api/v1/rundown/json?project=MyProject&rundown=FirstRundown",
"info": "Returns content of a specific rundown as json data."
},
{
"vers": "v1.3.0",
"method": "POST",
"active": false,
"param": "/api/v1/rundown/json",
"info": "Creates or updates a rundown file. This can be used for example with application extensions. POST <code>body:content</code> must contain valid rundown JSON data, otherwise SPX controller may not be able to read it. For more info search SPX Knowledge Base with keyword <code>api rundown/json</code>",
"code": { project: "myProjectName", file: "newRundown.json", content: { comment: "Playlist generated by MyApp", templates: [{ "description": "First template", "playserver": "OVERLAY", "etc": "..." }, { "description": "Second template", "playserver": "OVERLAY", "etc": "..." }] } }
},
{
"vers": "v1.1.1",
"method": "GET",
"active": false,
"param": "/api/v1/getlayerstate",
"info": "Returns current memory state of web-playout layers of the server (not UI). Please note, if API commands are used to load templates, this may not return them as expected!"
},
{
"vers": "v1.1.3",
"method": "GET",
"active": false,
"param": "/api/v1/gettemplates?project=MyProject",
"info": "Returns templates and their settings from a given project."
},
{
"vers": "v1.3.0",
"method": "GET",
"active": false,
"param": "/api/v1/executeScript?file=win-open-calculator.bat",
"info": "Execute a shell script/batch file in <code>ASSETS/scripts</code> folder using a shell associated with a given file extension."
},
{
"vers": "v1.3.0",
"method": "GET",
"active": false,
"param": "/api/v1/getFileList?assetsfolder=excel",
"info": "Returns an array of filenames fround in a given subfolder of ASSETS, such as <code>excel</code>."
},
{
"vers": "v1.3.0",
"method": "POST",
"active": false,
"param": "/api/v1/saveCustomJSON",
"info": "Creates or updates a JSON file in ASSETS/json folder. This can be used for persisting arbitrary data to a JSON file. The <code>content</code> property of the below example gets saved to <code>ASSETS/json/todoApp/myTodo.json</code>. Note the subfolder property is optional.",
"code": { subfolder: "todoApp", filename: "myData.json", content: { note: "Get these done by the end of month", items: [{ "task": "Grow a beard", "done": false }, { "task": "Get a haircut", "done": true }] } }
},
]
}
]
}
res.render('view-api-v1', {
layout: false,
functionList: functionsDoc,
version: global.vers
});
});
// COMMON API ----------------------------------------------------------
router.get('/version', async (req, res) => {
// let data = {};
// data.vendor = "SPX Graphics";
// data.product = "SPX Solo";
// data.version = global.vers;
// data.id = global.pmac;
// data.os = process.platform;
// return res.status(200).json(data)
let data = {};
data.vendor = 'SPX Graphics';
data.product = 'SPX Solo';
data.version = global.vers;
data.id = global.pmac;
data.hostname = config.general.hostname || '';
data.os = process.platform;
data.rootFolder = spx.getStartUpFolder();
data.uptime = {};
let uptime = spx.getUpTime();
data.uptime.seconds = uptime[0];
data.uptime.text = uptime[1];
// await spx.checkLicense();
// if (global.licensed) {
// data.license.type = global.license.productBrand;
// data.license.expiration = global.license.expiration;
// data.license.days = spx.getLicenseDaysRemaining();
// }
if (global.env && global.env.vendor) {
data.env = {};
data.env.vendor = global.env.vendor;
data.env.product = global.env.product;
data.env.version = global.env.version;
}
return res.status(200).json(data);
}); // end version
router.get('/panic', spxAuth.CheckAPIKey, async (req, res) => {
io.emit('SPXMessage2Client', { spxcmd: 'clearAllLayers' }); // clear webrenderers
io.emit('SPXMessage2Controller', { APIcmd: 'RundownAllStatesToStopped' }); // stop UI and save stopped values to rundown
if (spx.CCGServersConfigured) {
PlayoutCCG.clearChannelsFromGCServer(req.body.server) // server is optional
}
return res.status(200).json({ status: 200, message: 'OK', info: 'Panic executed. Layers cleared forcefully.' })
}); // end panic
router.get('/feedproxy', spxAuth.CheckAPIKey, async (req, res) => {
if (!req.query.url) {
logger.error('URL missing from feedproxy parameters');
return res.status(500).json({ type: 'error', message: 'URL missing from parameters' })
}
let URL = req.query.url
URL = URL.replace(/meta-data/g, '');
URL = URL.replace(/&path=/g, '###');
axios.get(URL)
.then(function (response) {
res.header('Access-Control-Allow-Origin', '*')
switch (req.query.format) {
case 'xml':
res.set('Content-Type', 'application/rss+xml')
break;
default:
res.set('Content-Type', 'application/json')
break
}
res.send(response.data)
})
.catch(function (error) {
console.log(error);
return res.status(500).json({ type: 'error', message: error.message })
});
}); // end feedproxy
router.post('/feedproxy', spxAuth.CheckAPIKey, async (req, res) => {
try {
if (!req.body.url) {
let errMsg = "url missing from feedproxy request. Make sure to have the correct JSON in your request.";
throw { status: 404, message: errMsg };
}
if (req.body.postBody) {
executePOSTRequest(req, res); // res handled in the function
} else {
executeGETRequest(req, res); // res handled in the function
}
} catch (error) {
res.status(error.status || 500).json({
status: error.status || 500,
error: error.message,
info: "Search SPX Knowledge Base for more using keyword 'feedproxy'."
});
}
}); // end feedproxy post handler
async function executePOSTRequest(req, res) {
try {
const { url, headers, postBody } = req.body;
if (!url || !headers || !postBody) {
return res.status(400).json({ error: 'Missing required parameters in request body' });
}
let requestBody;
try {
requestBody = JSON.parse(postBody);
} catch (error) {
return res.status(400).json({ error: 'Invalid postBody format' });
}
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(requestBody),
headers: headers
});
if (!response.ok) {
return res.status(response.status).json({ error: `Target API returned an error: ${response.status} ${response.statusText}` });
}
const data = await response.json();
res.status(response.status).json(data);
} catch (error) {
console.error('Error in executePOSTRequest:', error);
res.status(500).json({ error: 'Failed to forward POST request' });
}
}
function executeGETRequest(req, res) {
// This handles response to the client
// console.log('Using GET method', req.body);
axios.
get(req.body.url, {
headers: req.body.headers
})
.then((response) => {
res.header('Access-Control-Allow-Origin', '*')
// console.log('Response status:', response.statusText, response.status, response.data);
logger.verbose('executeGETRequest response: ' + response.status);
res.status(response.status || 200).send(response.data);
})
.catch((error) => {
if (error.response) {
// console.log('data......', error.response.data);
// console.log('status....', error.response.status);
// console.log('headers...', error.response.headers);
res.status(error.response.status || 500).send(error.response.data);
} else if (error.request) {
// The request was made but no response was received
// console.log('request...', error.request);
res.status(500).send(error.request);
} else {
// Something happened in setting up the request
// console.log('Error msg...', error.message);
res.status(500).send(error.message);
}
});
} // end executeGETRequest
router.get('/rundown/load/', spxAuth.CheckAPIKey, async (req, res) => {
// Improved in 1.3.1 to sanitize input
let file = await spx.strip(req.query.file);
console.log('Loading rundown: ' + file);
// Added in 1.3.1 check if it exists
let project = file.split('/')[0];
let rundown = file.split('/')[1];
let fullPath = path.resolve(spx.getDatarootFolder(), project, 'data', rundown + '.json');
if (!fs.existsSync(fullPath)) {
let errMsg = 'Rundown [' + file + '] not found, cannot load it.';
logger.error(errMsg);
return res.status(404).json({ status: 404, message: errMsg });
}
let dataOut = {};
dataOut.info = ack2
dataOut.APIcmd = 'RundownLoad';
dataOut.file = file;
dataOut.apikey = req.query.apikey || '';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end load
router.get('/rundown/stopAllLayers', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2
dataOut.APIcmd = 'RundownStopAll';
dataOut.apikey = req.query.apikey || '';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end stopAllLayers
router.get('/changeItemID', async (req, res) => {
// Added in 1.0.15 - undocumented intentionally
// ID button in SPX controller uses this API endpoint:
// /api/v1/changeItemID?rundownfile=C:/SPX/DATAROOT/PROJECT/data/list.json&ID=0000001&newID=0000002
try {
let file = req.query.rundownfile || '';
let oldI = req.query.ID || '';
let newI = req.query.newID || '';
if (!file || !oldI || !newI) {
throw 'Missing data: file [' + file + '], old [' + oldI + '], new [' + newI + ']';
}
let datafile = path.normalize(file);
let RundownData = await spx.GetJsonData(datafile);
// First check for conflicts
RundownData.templates.forEach((item, index) => {
if (item.itemID === newI) {
throw 'ID-conflict'
}
});
RundownData.templates.forEach((item, index) => {
if (item.itemID === oldI) {
item.itemID = newI
}
});
RundownData.updated = new Date().toISOString();
RundownData = await spx.appendProjectFile(RundownData, datafile, "from changeItemID");
global.rundownData = RundownData;
await spx.writeFile(datafile, RundownData);
logger.verbose('Changed item ID to ' + newI);
return res.status(200).send('ID changed to ' + newI);
} catch (error) {
switch (error) {
case 'ID-conflict':
msg = 'ID conflict, ID was not changed.'
lvl = 'warn'
break;
default:
msg = 'Error in changeItemID: ' + error
lvl = 'error'
}
logger[lvl](msg);
return res.status(409).send('ID not changed');
}
});
router.get('/changeItemData', async (req, res) => {
// Added in 1.1.1
// ID button in SPX controller uses this API endpoint:
// /api/v1/changeItemData?rundownfile=C:/SPX/DATAROOT/PROJECT/data/list.json & ID=1234567890 & key=out & newValue=manual
try {
let file = req.query.rundownfile || '';
let epoc = req.query.ID || '';
let prop = req.query.key || '';
let valu = req.query.newValue || '';
if (!file || !epoc || !prop || !valu) {
logger.warn('Missing data from changeItemData: file: [' + file + '], ID: [' + epoc + '], key: [' + prop + '], value: [' + valu + ']')
throw 'Missing data, see log.';
}
let datafile = path.normalize(file);
let RundownData = await spx.GetJsonData(datafile);
RundownData.templates.forEach((item, index) => {
if (item.itemID === epoc) {
item[prop] = valu
}
});
RundownData.updated = new Date().toISOString();
RundownData = await spx.appendProjectFile(RundownData, datafile, "from changeItemData");
global.rundownData = RundownData; // push to memory also for next take
await spx.writeFile(datafile, RundownData);
logger.verbose('ChangeItemData: file: [' + file + '], ID: [' + epoc + '], key: [' + prop + '], value: [' + valu + ']')
return res.status(200).send(prop + ' changed to ' + valu);
} catch (error) {
console.log('Error', error);
logger.error('changeItemData error', error);
return res.status(409).send('Failed to change ' + prop + ' to ' + valu + '!');
}
});
// CONTROLLER API -----------------------------------------------------------------------------
router.get('/item/play', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2
dataOut.status = 200;
dataOut.message = 'OK';
dataOut.APIcmd = 'ItemPlay';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
});
router.get('/item/continue', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.status = 200;
dataOut.message = 'OK';
dataOut.info = ack2
dataOut.APIcmd = 'ItemContinue';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end item continue
router.get('/item/stop', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.status = 200;
dataOut.message = 'OK';
dataOut.info = ack2
dataOut.APIcmd = 'ItemStop';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end item stop
router.get('/rundown/focusNext/', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2
dataOut.APIcmd = 'RundownFocusNext';
dataOut.apikey = req.query.apikey || '';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end focusNext
router.get('/rundown/focusPrevious/', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2
dataOut.APIcmd = 'RundownFocusPrevious';
dataOut.apikey = req.query.apikey || '';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end focusPrevious
router.get('/rundown/focusFirst/', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2
dataOut.APIcmd = 'RundownFocusFirst';
dataOut.apikey = req.query.apikey || '';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end focusFirst
router.get('/rundown/focusLast/', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2
dataOut.APIcmd = 'RundownFocusLast';
dataOut.apikey = req.query.apikey || '';
io.emit('SPXMessage2Controller', dataOut);
res.status(200).json(dataOut);
}); // end focusLast
router.get('/rundown/focusByID/:id', spxAuth.CheckAPIKey, async (req, res) => {
let dataOut = {};
dataOut.info = ack2;
dataOut.APIcmd = 'RundownFocusByID';
dataOut.apikey = req.query.apikey || '';
dataOut.itemID = req.params.id;
io.emit('SPXMessage2Controller', dataOut);
res.status(200).send('Sent request to controller: ' + JSON.stringify(dataOut));
}); // end focusByID
// router.get('/rundown/focusFirst/', spxAuth.CheckAPIKey, async (req, res) => {
// res.status(501).json(notInSolo);
// }); // end focusFirst
// router.get('/rundown/focusLast/', spxAuth.CheckAPIKey, async (req, res) => {
// res.status(501).json(notInSolo);
// }); // end focusLast
// router.get('/rundown/focusByID/:id', spxAuth.CheckAPIKey, async (req, res) => {
// res.status(501).json(notInSolo);
// }); // end focusByID
router.get('/item/play/:id', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end item play by ID
router.get('/item/continue/:id', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end item continue by ID
router.get('/item/stop/:id', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end item stop by ID
router.get('/invokeTemplateFunction/', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end invokeTemplateFunction
router.get('/invokeExtensionFunction/', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end invokeExtensionFunction
// SERVER API ----------------------------------------------------------------------------------
router.post('/directplayout', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end directplayout
router.get('/directplayout', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end directplayout
router.get('/getprojects', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end getprojects
router.get('/allrundowns', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end allrundowns
router.get('/getrundowns', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end getrundowns
router.get('/gettemplates', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end gettemplates
router.get('/getlayerstate', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end getlayerstate
router.get('/executeScript', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end executeScript file
router.get('/getFileList', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end getFileList
router.post('/saveCustomJSON', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end saveCustomJSON
router.get('/controlRundownItemByID', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end controlRundownItemByID
router.get('/rundown/get', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end get current rundown as JSON
router.get('/rundown/json', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end get specific rundown as JSON
router.post('/rundown/json', spxAuth.CheckAPIKey, async (req, res) => {
res.status(501).json(notInSolo);
}); // end create or update rundown as JSON
module.exports = router;
+440
View File
@@ -0,0 +1,440 @@
// -----------------------------------------
// Handle Express server routes for the API (at "/api/")
// -----------------------------------------
var express = require("express");
const router = express.Router();
const path = require('path');
const fs = require('fs');
const moment = require('moment');
const directoryPath = path.normalize(config.general.dataroot);
const logger = require('../utils/logger');
logger.debug('API-route loading...');
const spx = require('../utils/spx_server_functions.js');
const xlsx = require('node-xlsx').default;
// --- WATCHOUT!!!! v1.3.3 disabled --------
// const { now } = require("moment");
// const { constants } = require("buffer");
// ROUTES -------------------------------------------------------------------------------------------
router.get('/', function (req, res) {
res.send('Looking for this <a href="/api/v1/">api/v1</a>?');
});
router.get('/files', async (req, res) => {
const fileListAsJSON = await GetDataFiles();
res.send(fileListAsJSON);
}); // file
router.get('/openFileFolder/', async (req, res) => {
// Added in 1.3.1. And fixed in 1.3.2...
// Added optional parameter forceFolder to open a different folder than templates.
if (config.general.disableOpenFolderCommand == true) {
let msg = 'openFileFolder -command disabled in config.';
logger.warn(msg);
return res.status(403).send(msg);
}
let dirpath, folder = null;
if (req.query.openFolderOnly) {
folder = path.join(spx.getStartUpFolder(), 'ASSETS', req.query.openFolderOnly);
// folder = path.dirname(dirpath);
} else {
let relpath = req.query.file || '';
if (!req.query.file) {
let msg = 'openFileFolder -command requires a file parameter.';
logger.warn(msg);
return res.status(403).send(msg);
}
filepath = path.join(spx.getStartUpFolder(), 'ASSETS', 'templates', relpath);
folder = path.dirname(filepath);
}
// Added in 1.3.1 for security: if not found nothing is done.
if (!fs.existsSync) {
logger.error('Folder ' + folder + ' does not exist.');
return res.status(404).send('Folder ' + folder + ' does not exist.');
}
// open folder in each operating system
if (process.platform === 'darwin') {
require('child_process').exec('open "' + folder + '"');
} else if (process.platform === 'win32') {
require('child_process').exec('explorer "' + folder + '"');
} else if (process.platform === 'linux') {
require('child_process').exec('xdg-open "' + folder + '"');
} else {
logger.error('Unknown operating system: ' + process.platform);
}
res.sendStatus(200)
}); // openFileFolder of a template for editing
router.get('/licBasic/', async (req, res) => {
// added in 1.1.1 - license check. Minor tweaks in 1.1.3
// Format:
// 4x?rot(pmac)10x? | AA-AA-33-UQ-GZ-ZX-BB-BB-BB-BB
// Not safe because it is not encrypted.
let stripd = req.query.str.replace(/-/g, ''); // strip dashes
let rotlic = stripd.substring(4,12); // get 8 chars
// console.log('rotlic // from: ' + rotlic + ' to ' + spx.rot(rotlic, true) + ' vs ' + global.pmac.toUpperCase());
if (spx.rot(rotlic, true) === global.pmac.toUpperCase()) {
res.status(200).send('{result:ok}');
} else {
res.status(403).send('{result:invalid}');
}
}); // GET licBasic check ended
router.get('/rotBasic/', async (req, res) => {
// Require host-id, returns key.
let rotlic = spx.rot(req.query.id);
let soclic = spx.dashify(spx.salt(4) + rotlic + spx.salt(8));
let revlic = spx.rot(rotlic, true);
let json = "{\"pmac\":\"" + global.pmac + "\",\"rot\":\"" + rotlic + "\",\"soclic\":\"" + soclic + "\",\"chk\":\"" + revlic + "\"}";
res.send(json);
}); // GET rotBasic/?id=12345678
router.get('/logger/', async (req, res) => {
// Minimalistic GET logger for template messages
let message = req.query.message
let source = req.query.source
let level = req.query.level.toLowerCase()
let channel = req.query.channel
let msg = '(api/logger, ' + channel + ', ' + source + '): ' + message
// console.log(msg);
eval('logger.' + level + '(msg)'); // nasty, eh?
res.sendStatus(200)
}); // GET logger route ended
router.post('/logger/', async (req, res) => {
// Minimalistic POST logger for template messages
let message = req.body.message
let source = req.body.source
let level = req.body.level.toLowerCase()
let channel = req.body.channel
let msg = '(api/logger, ' + channel + ', ' + source + '): ' + message
// console.log(msg);
eval('logger.' + level + '(msg)'); // nasty, eh?
res.sendStatus(200)
}); // POST logger route ended
router.post('/browseFiles/', async (req, res) => {
// This is axios ajax handler for file browser on dbl click on a folder
// REQUEST: current folder and next folder name
// RETURNS: json data with folder and file arrays
// 1.1.0 - refactored navigation process to use '..' for parent folder.
let curFolder = req.body.curFolder || ".";
let tgtFolder = req.body.tgtFolder || "";
let extension = req.body.extension || "HTM";
let rootFolder = req.body.rootFolder || path.join(spx.getStartUpFolder(), 'ASSETS');
let BrowseFolder = path.join(curFolder, tgtFolder);
let osRootPath = path.resolve(rootFolder)
let osTargPath = path.resolve(BrowseFolder)
let navigateTo = osTargPath;
let feedbackMs = '';
if ( osTargPath.length <= osRootPath.length ) {
logger.verbose('Targeting beyond limits, sending root-identifier. Path: ' + osTargPath);
navigateTo = osRootPath;
feedbackMs = 'root';
} else {
feedbackMs = 'ok';
}
const fileListAsJSON = await spx.GetFilesAndFolders(navigateTo, extension);
fileListAsJSON.message=feedbackMs; // force feedback message to UI at RenderFolder()
res.send(fileListAsJSON);
}); // POST browseFiles API route ended
router.post('/heartbeat/', async (req, res) => {
// REQUEST: data = a heartbeat string
// RETURNS: none
// 1.1.0 submit anonymous usage stats
try {
if (global.config.general.allowstats===false || global.config.general.allowstats=='false') {
logger.verbose('Heartbeat / stats disabled.');
return
} // Stats disabled by config. Added in 1.1.1.
let d = req.body.data;
let h = 'smartpx.fi';
spx.collectSPXInfo('hello from api/heartbeat endpoint')
.then(function(si) {
let u = 'http://' + h + '/gc/messageservice2/?'+ si + '&d=' + d;
spx.httpGet(u);
return si
})
.then(function(si) {
logger.verbose('Stats ' + si + ' AND ' + d);
res.status(200).send('{all:good}'); // ok 200 AJAX RESPONSE
return;
})
} catch (error) {
logger.error('Error in api/heartbeat: ' + error);
res.status(500).send(error);
};
}); // POST heartbeat
router.post('/readExcelData', async (req, res) => {
// Function can be called from a template to get all data from
// an Excel file in the ASSETS/excel -folder.
// Data parsing / logic must be implemented in the template
// this just dumps data out as-is.
// var excelFile = path.join(__dirname, '..', 'ASSETS', req.body.filename); // fails when packaged
// Improved in 1.1.1 - Return cached data also if fileref is empty (for some reason).
try {
var excelFile = path.join(spx.getStartUpFolder(), 'ASSETS', req.body.filename); // v.1.0.15: getStartUpFolder()
var workSheetsData;
let timenow = Date.now();
// console.log('Excel cache age ' + (timenow - excel.readtime) + ' ms');
if ( excel.data && excel.filename == req.body.filename && (timenow - excel.readtime) <= 1000 || !req.body.filename ) { /* milliseconds */
// sama data requested less than a second ago, return data from memory
logger.verbose('Returning cached Excel data from memory')
workSheetsData = excel.data;
// console.log('Returning CACHED Excel data.\n');
} else {
// get it from Excel file
logger.verbose('Returning Excel data from FILE and saving to cache.')
workSheetsData = xlsx.parse(excelFile);
// console.log('Returning Excel FILE data and caching it.\n');
// cache excel data to a global variable
global.excel.readtime = Date.now();
global.excel.filename = req.body.filename;
global.excel.data = workSheetsData;
}
logger.verbose('OK API read Excel data from ' + excelFile);
res.status(200).send(workSheetsData); // ok 200 AJAX RESPONSE
return;
} catch (error) {
logger.error('Error in api/readExcelData while reading Excel ' + excelFile + ": " + error);
res.status(500).send(error); }; // Server error
return;
}); // POST readExcelData to get Excel data from file
router.post('/savefile/:filebasename', async (req, res) => {
spx.talk('Saving file ' + req.params.filebasename);
try {
if (!req.params.filebasename) {
throw new Error("Filename missing, cannot save file.");
}
let datafile = path.join(directoryPath, req.params.filebasename) + '.json';
logger.debug('Saving file ' + datafile + '...');
let data = req.body;
await spx.writeFile(datafile,data);
res.status(200).send('OK, created file ' + datafile); // ok 200 AJAX RESPONSE
} catch (error) {
logger.error('Error while saving ' + datafile + ': ' + err);
res.status(500).send(error);
}; //file written
}); // POST savefile API route ended
router.post('/saverundownfile/:projectName/:rundownName', async (req, res) => {
// Added in 1.3.0
// Used by extensions that will modify rundowns and save them back to the server.
// This will also send a message to the UI to request a reload.
console.log('Saving rundown file ' + req.params.rundownName + ' in project ' + req.params.projectName + '...');
try {
if (!req.params.projectName || !req.params.rundownName) {
throw new Error("Project or filename missing from request, cannot save file.");
}
let datafile = path.join(directoryPath, req.params.projectName, 'data', req.params.rundownName) + '.json';
logger.debug('Saving rundown file ' + datafile + '...');
let data = req.body;
await spx.writeFile(datafile,data);
io.emit('SPXMessage2Client', {
spxcmd: "showMessageSlider",
msg: "⛔ Rundown data was modified by API. Reload view!",
type: "warn",
persist: true
});
res.status(200).send('OK, created file ' + datafile); // ok 200 AJAX RESPONSE
} catch (error) {
logger.error('Error in api/saverundownfile' + error);
res.status(500).send(error);
}; //file written
}); // POST savefile API route ended
router.post('/exportCSVfile', async (req, res) => {
// console.log('Exporting CSV...');
try {
let showFolder = req.body.foldername || "";
let datafile = req.body.datafile || "";
let dataJSONfile= path.join(spx.getDatarootFolder(), showFolder, 'data', datafile + '.json'); // Changed in 1.3.1
let rundownData = await spx.GetJsonData(dataJSONfile);
let CSVdata = ''
let item_description,
item_playserver,
item_playchannel,
item_playlayer,
item_webplayout,
item_out,
item_uicolor,
item_dataformat,
item_relpath,
item_graphicPath,
item_version,
item_id
rundownData.templates.forEach((item,index) => {
// console.log('Iterating template index ' + index);
if (item.itemID == req.body.itemID) {
// This is the template to process.
// console.log('Exporting template ' + item.itemID);
item_description = item.description || '';
item_playserver = item.playserver || '';
item_playchannel = item.playchannel || '1';
item_playlayer = item.playlayer || '10';
item_webplayout = item.webplayout || '10';
item_out = item.out || 'manual';
item_uicolor = item.uicolor || '0';
item_dataformat = item.dataformat || 'json';
item_relpath = item.relpath || '';
// if OGraf, we need to add a few more properties:
if (item_relpath.toLowerCase().endsWith(".ograf.json")) {
item_graphicPath = item.ografProps.graphicPath || '';
item_version = item.ografProps.version || '';
item_id = item.ografProps.id || '';
}
CSVdata = '\r\n# SPX Rundown item CSV export. (More info: https://docs.spxgraphics.com/Guides/Tutorials/how+to+use+csv+files)\r\n\r\n'
CSVdata += '# description #;' + item_description + '\r\n'
CSVdata += '# playserver #;' + item_playserver + '\r\n'
CSVdata += '# playchannel #;' + item_playchannel + '\r\n'
CSVdata += '# playlayer #;' + item_playlayer + '\r\n'
CSVdata += '# webplayout #;' + item_webplayout + '\r\n'
CSVdata += '# out #;' + item_out + '\r\n'
CSVdata += '# uicolor #;' + item_uicolor + '\r\n'
CSVdata += '# dataformat #;' + item_dataformat + '\r\n'
CSVdata += '# relpath #;' + item_relpath + '\r\n'
// if OGraf, add graphicPath, version and id
if (item_relpath.toLowerCase().endsWith(".ograf.json")) {
CSVdata += '# graphicPath #;' + item_graphicPath + '\r\n';
CSVdata += '# version #;' + item_version + '\r\n';
CSVdata += '# id #;' + item_id + '\r\n';
}
CSVdata += '# onair #;false\r\n'
CSVdata += '# project #;' + showFolder + '\r\n'
CSVdata += '# rundown #;' + datafile + '\r\n'
CSVdata += '\r\n'
// print field ID's
CSVdata += '# FieldUUIDs #;'
item.DataFields.forEach((field,findex) => {
if (field.field && field.field!='' ) {
CSVdata += field.field + ';'
}
});
CSVdata += '\r\n';
// print field types
CSVdata += '# FieldTypes #;'
item.DataFields.forEach((field,findex) => {
if (field.field && field.field!='' ) {
CSVdata += field.ftype + ';'
}
});
CSVdata += '\r\n'
// print field titles
CSVdata += '# FieldTitls #;'
item.DataFields.forEach((field,findex) => {
if (field.field && field.field!='' ) {
CSVdata += field.title + ';'
}
});
CSVdata += '\r\n'
// print field fcalls if button
CSVdata += '# Fieldfcalls #;'
item.DataFields.forEach((field,findex) => {
if (field.field && field.field!='' && field.fcall) {
CSVdata += field.fcall + ";";
}
});
CSVdata += '\r\n'
// print field values
CSVdata += '\r\n'
let itemData = '# ID:auto;'
item.DataFields.forEach((field,findex) => {
if (field.field && field.field!='' && field.value) {
let dataToSave = field.value.replace(/\n/g,'<BR>') || ''; // replace all newlines with <BR>
itemData += dataToSave + ";";
}
});
CSVdata += itemData + '\r\n'
}
});
let timestamp = spx.prettifyDate(new Date(), 'YYYY-MM-DD-HHMMSS');
let filenameref = item_relpath.split('.')[0].replace('\\', '/').split('/').slice(-1)[0];
// generate CSV folder if not there
let CSVfolder = path.join(spx.getStartUpFolder(), 'ASSETS', 'csv')
fs.existsSync(CSVfolder) || fs.mkdirSync(CSVfolder)
let CSVfileRef = path.join(CSVfolder, filenameref + '_' + timestamp + '.csv');
await spx.writeTextFile(CSVfileRef,CSVdata);
// console.log(' Created CSV file ' + CSVfileRef);
logger.verbose('Created ' + CSVfileRef + ' from itemID ' + req.body.itemID + ' on ' + dataJSONfile + '. ');
res.status(200).send('Generated file ' + CSVfileRef);
} catch (error) {
logger.error('API error in exportCSVfile(): ', error);
}; //file written
}); // POST exportCSVfile end
// FUNCTIONS -------------------------------------------------------------------------------------------
async function GetDataFiles() {
// Get files
// const directoryPath = path.normalize("X:/01_Projects/Yle/CG/DEV/DATA_FOLDER/");
const directoryPath = path.normalize(config.general.dataroot);
let jsonData = {};
var key = 'files';
jsonData.folder = directoryPath;
jsonData[key] = [];
let id = 0;
try {
fs.readdirSync(directoryPath).forEach(file => {
let ext = path.extname(file).toUpperCase();
if (ext == ".JSON") {
var stats = fs.statSync(path.join(directoryPath, file));
var datem = moment(stats.mtime, 'DD.MM.YYYY').format();
var filedata = {
id: id,
name: file,
date: datem
};
id++;
jsonData[key].push(filedata);
}
});
return jsonData;
}
catch (error) {
logger.error('Error while reading files from ' + directoryPath + ': ' + err);
return (error);
}
} // GetDataFiles ended
module.exports = router;
File diff suppressed because it is too large Load Diff
+352
View File
@@ -0,0 +1,352 @@
// -------------------------------------------------------
// Handle Express server routes for the CasparCG commands.
// -------------------------------------------------------
var express = require("express");
const router = express.Router();
const path = require('path');
const logger = require('../utils/logger');
logger.debug('Caspar-route loading...');
const spx = require('../utils/spx_server_functions.js');
const ip = require('ip')
const ipad = ip.address(); // my ip address
const port = config.general.port || 5656;
const PlayoutCCG = require('../utils/playout_casparCG.js');
// ROUTES CCG -----------------------------------------------------------------------------------
router.get('/', function (req, res) {
res.send('Nothing here. Go away!');
});
router.get('/system', function (req, res) {
res.send('Nothing here. Get lost.');
});
router.get('/requestInfo', async (req, res) => {
var SERVER = req.query.server; // ?server=OVERLAY
var COMMAND = req.query.command; // &command=INFO
// console.log('requestInfo from ' + SERVER + ', cmd: ' + COMMAND);
const CCGHost = global.CCGSockets[PlayoutCCG.getSockIndex(SERVER)].spxhost;
const CCGPort = global.CCGSockets[PlayoutCCG.getSockIndex(SERVER)].spxport;
const net = require('net')
CasparRequest = new net.Socket();
CasparRequest.connect(CCGPort, CCGHost, function () {
CasparRequest.write(COMMAND + '\r\n');
CasparRequest.on('data', function (data) {
res.set('Content-Type', 'text/plain'); // format
res.status(200).send(data);
CasparRequest.destroy();
});
CasparRequest.on('error', function (error) {
return res.status(500).send(error);
CasparRequest.destroy();
});
});
});
router.get('/system/:data', (req, res) => {
// same principle as with /control/:data
// do OpSys level stuff, open folders etc..
//
// NOTE: This endpoint is in the WRONG PLACE
// it should be moved to a more generic place
//
// console.log('System utilities / ' + req.params.data);
data = JSON.parse(req.params.data);
let directoryPath = "";
logger.verbose('System utilities / ' + JSON.stringify(data));
res.sendStatus(200);
switch (data.command) {
case 'DATAFOLDER':
directoryPath = path.normalize(config.general.dataroot);
require('child_process').exec('start "" ' + directoryPath);
break;
case 'TEMPLATEFOLDER':
// directoryPath = path.normalize(config.general.templatefolder);
directoryPath = path.join(spx.getStartUpFolder(), 'ASSETS', 'templates');
require('child_process').exec('start "" ' + directoryPath);
break;
case 'CHECKCONNECTIONS':
// console.log('CCG: Checking server connections...');
spx.checkServerConnections();
break;
case 'RESTARTSERVER':
//
logger.error('RESTART SERVER REQUEST RECEIVED. Will try to kill and restart using process manager.');
process.exit(2);
break;
}
});
router.get('/control/:data', (req, res) => {
// Principle:
// We get data object which has
// - data.command (ADD | STOP | UPDATE)
// - data.fields [{ id: 'f0', value: 'Eka' }, { id: 'f1', value: 'Toka' }];
// - data.element headline1
// - data.profile News
//
// Then we read profiles-file and search for the required element
// such as 'headline1' and get needed data from it:
// - templatefile
// - server
// - channel
// - layer
data = JSON.parse(req.params.data);
logger.info('CCG/Control/ ' + JSON.stringify(data));
res.sendStatus(200);
if (!spx.CCGServersConfigured){ return } // exit early, no need to do any CasparCG work
var DataStr = "";
const directoryPath = path.normalize(config.general.dataroot);
let profilefile = path.join(directoryPath, 'config', 'profiles.json');
const profileDataAsJSON = spx.GetJsonData(profilefile);
var arr = profileDataAsJSON.profiles;
let GFX_Teml = "";
let GFX_Serv = "";
let GFX_Chan = "";
let GFX_Laye = "";
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
if (obj.name == data.profile) {
GFX_Teml = eval("obj.templates." + data.element + ".templatefile");
GFX_Serv = eval("obj.templates." + data.element + ".server");
GFX_Chan = eval("obj.templates." + data.element + ".channel");
GFX_Laye = eval("obj.templates." + data.element + ".layer");
}
}
logger.debug('CCG/Control - Profile ' + data.profile + "', Template: '" + GFX_Teml, "', CasparCG: " + GFX_Serv + ", " + GFX_Chan + ", " + GFX_Laye);
if (data.command == "ADD" || data.command == "UPDATE") {
let TEMPLATEDATA = "";
if (data.fields) {
data.fields.forEach(item => {
logger.verbose('Processing field for CCG: ' + item.id + ' with value: ' + item.value);
TEMPLATEDATA += spx.CGComponentFactory(item.id, item.value)
});
}
DataSta = "<templateData>";
DataEnd = "</templateData>";
DataStr = DataSta + TEMPLATEDATA + DataEnd;
}
try {
switch (data.command) {
case 'ADD':
global.CCGSockets[spx.getSockIndex(spx.getChannel(data))].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' ADD 1 "' + GFX_Teml + '" 1 "' + DataStr + '"\r\n');
break;
case 'UPDATE':
console.log('TODO: This is probably not used to send update to CasparCG... See playout_casparCG.js instead! ** FIXME:**');
global.CCGSockets[spx.getSockIndex(spx.getChannel(data))].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' UPDATE 1 "' + DataStr + '"\r\n');
break;
case 'NEXT':
global.CCGSockets[spx.getSockIndex(spx.getChannel(data))].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' NEXT 0\r\n');
break;
case 'STOP':
global.CCGSockets[spx.getSockIndex(spx.getChannel(data))].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' STOP 1\r\n');
break;
default:
logger.warn('CCG/Control - Unknown command: ' + data.command);
}
} catch (error) {
logger.error('ERROR in /control/:data, unable to send CCG command. Server up? ' + error)
}
}); // control ended
router.get('/controljson/:data', (req, res) => {
data = JSON.parse(req.params.data);
logger.info('CCG/controljson ' + JSON.stringify(data));
res.sendStatus(200);
if (!spx.CCGServersConfigured){ return } // exit early, no need to do any CasparCG work
const directoryPath = path.normalize(config.general.dataroot);
let profilefile = path.join(directoryPath, 'config', 'profiles.json');
const profileDataAsJSON = spx.GetJsonData(profilefile);
var arr = profileDataAsJSON.profiles;
let GFX_Teml = "";
let GFX_Serv = "";
let GFX_Chan = "";
let GFX_Laye = "";
let GFX_JSON = ""
let stng = ""
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
if (obj.name == data.profile) {
GFX_Teml = eval("obj.templates." + data.element + ".templatefile");
GFX_Serv = eval("obj.templates." + data.element + ".server");
GFX_Chan = eval("obj.templates." + data.element + ".channel");
GFX_Laye = eval("obj.templates." + data.element + ".layer");
GFX_JSON = JSON.stringify(data.jsonData);
}
}
logger.verbose("CCG/ControlJSON Profile: '" + data.profile + "', Template: '" + GFX_Teml, "', CasparCG: " + GFX_Serv + ", " + GFX_Chan + ", " + GFX_Laye)
logger.debug('json' + JSON.stringify(GFX_JSON));
stng = GFX_JSON;
stng = stng.split('§backslash§').join('&bsol;'); // replace §backslash§ with html corresponding entity
stng = stng.split('"').join('\\"'); // replace " with \"
try {
switch (data.command) {
case 'ADD':
global.CCGSockets[spx.getSockIndex(spx.getChannel(data))].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' ADD 1 "' + GFX_Teml + '" 1 "' + stng + '"\r\n');
break;
case 'UPDATE':
global.CCGSockets[spx.getSockIndex(spx.getChannel(data))].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' UPDATE 1 "' + stng + '"\r\n');
break;
default:
logger.warn('CCG/ControlJSON - Unknown command: ' + data.command);
}
} catch (error) {
logger.error('ERROR in /controljson/:data: ' + error)
}
});
router.post('/disable', async (req, res) => {
// Added in 1.1.0. Set "disabled" state of the given CasparCG server
// This only affects playback commands. Initialization works normally.
logger.verbose('CasparCG server ' + req.body.server + ' disabled to ' + req.body.disabled);
try {
var ConfigFile = global.configfileref;
var ConfigData = await spx.GetJsonData(ConfigFile);
if (ConfigData.casparcg.servers) {
ConfigData.casparcg.servers.forEach((serverItem,index) => {
if (serverItem.name == req.body.server) {
serverItem.disabled = req.body.disabled
}
});
}
global.config = ConfigData; // update mem version also
await spx.writeFile(ConfigFile,ConfigData);
let response = ['Config changed']
res.status(200).send(response); // ok 200 AJAX RESPONSE
} catch (error) {
console.error('ERROR', error);
let errmsg = 'Server error in CCG disable [' + error + ']';
logger.error(errmsg);
res.status(500).send(errmsg) // error 500 AJAX RESPONSE
};
});
// Create all required Socket Connections for CasparCG servers specified in config.json
const net = require('net')
let ServerDataForLogger = [];
if (config.casparcg) {
config.casparcg.servers.forEach((element,index) => {
const CurName = element.name;
const CurHost = element.host;
const CurPort = element.port;
ServerDataForLogger.push({ name: CurName, host: CurHost, port: CurPort });
// next two lines creates a dynamic variable for this loop iteration
// Changed in 1.3.0, from:
// var CurCCG = CurName + "= undefined";
// eval(CurCCG);
// ..to:
var CurCCG = new net.Socket();
// end of change
global.CCGSockets.push(CurCCG); // --> PUSH Socket object to a global array for later use
CurCCG.spxname = CurName; // save each entry a name for later searching!
CurCCG.spxhost = CurHost; // save each entry a host for later searching! (v.1.0.14)
CurCCG.spxport = CurPort; // save each entry a port for later searching! (v.1.0.14)
CurCCG.connect(CurPort, CurHost, function () {
ServerDataForLogger.push({ name: CurName, host: CurHost, port: CurPort });
data = { spxcmd: 'updateServerIndicator', indicator: 'indicator' + index, color: '#00CC00' };
io.emit('SPXMessage2Client', data);
data = { spxcmd: 'updateStatusText', status: 'Communication established with ' + CurName + '.' };
io.emit('SPXMessage2Client', data);
logger.verbose('SPX connected to CasparCG as \'' + CurName + '\' at ' + CurHost + ":" + CurPort + '.');
});
CurCCG.on('data', function (data) {
logger.verbose('SPX received data from CasparCG ' + CurName + ': ' + data);
// we must parse the data so we can evaluate it...
let CCG_RETURN_TEXT = String(data).replace('\r','').replace('\n',''); // convert return object to string, strip \r\n
let CCG_RETURN_CODE = CCG_RETURN_TEXT.substring(0, 2); // first two chars
switch (CCG_RETURN_CODE) {
case "20":
logger.verbose('Comms good with ' + CurName + ": " + CCG_RETURN_TEXT);
break;
case "40":
logger.error(CurName + ' CasparCG response: ' + CCG_RETURN_TEXT );
logger.debug('Verify CasparCG\'s (' + CurName + ') access to templates on SPX server at ' + spx.getTemplateSourcePath());
data = { spxcmd: 'updateStatusText', status: 'Error in comms with ' + CurName + '.' };
io.emit('SPXMessage2Client', data);
break;
case "50":
logger.error('Failed ' + CurName + ": " + CCG_RETURN_TEXT);
data = { spxcmd: 'updateStatusText', status: CurName + ' failed.' };
io.emit('SPXMessage2Client', data);
break;
default:
logger.error('Unknown status value ' + CurName + ' - ' + CCG_RETURN_CODE + ' - ' + CCG_RETURN_TEXT);
break;
}
// SocketIO call to client
data = { spxcmd: 'updateServerIndicator', indicator: 'indicator' + index, color: '#00CC00' };
io.emit('SPXMessage2Client', data);
if (data.toString().endsWith('exit')) {
CCGclient.destroy();
}
});
CurCCG.on('close', function () {
// SocketIO call to client
data = { spxcmd: 'updateServerIndicator', indicator: 'indicator' + index, color: '#CC0000' };
io.emit('SPXMessage2Client', data);
data = { spxcmd: 'updateStatusText', status: 'Connection to ' + CurName + ' was closed.' };
io.emit('SPXMessage2Client', data);
logger.verbose('SPX connection to CasparCG "' + CurName + '" closed (' + CurHost + ':' + CurPort + ').');
});
CurCCG.on('error', function (err) {
// console.log('Still 4?', CurCCG.connecting)
data = { spxcmd: 'updateServerIndicator', indicator: 'indicator' + index, color: '#CC0000' };
io.emit('SPXMessage2Client', data);
data = { spxcmd: 'updateStatusText', status: 'Communication error with ' + CurName + '.' };
io.emit('SPXMessage2Client', data);
logger.warn('Unable to connect CasparCG "' + CurName + '" (' + CurCCG.spxhost + ':' + CurCCG.spxport + '). Is it running?');
// console.log('Sockets: ', global.CCGSockets);
});
// console.log('Still 3?', CurCCG.connecting)
});
}; // end if
logger.debug('ServerDataForLogger during init: ' + JSON.stringify(ServerDataForLogger));
module.exports = router;
+19
View File
@@ -0,0 +1,19 @@
// var express = require("express");
// const router = express.Router();
// const path = require('path');
// const fs = require('fs');
// const moment = require('moment');
// const directoryPath = path.normalize(config.general.dataroot);
// const logger = require('../utils/logger');
// logger.debug('WebPlayer-route loading...');
// const spx = require('../utils/spx_server_functions.js');
// // ROUTES -------------------------------------------------------------------------------------------
// router.get('/', function (reg, res) {
// let htmlFile = path.join(__dirname+'/../http/renderer/index.html');
// // res.sendFile(htmlFile);
// res.send(htmlFile);
// });
// module.exports = router;
Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

+978
View File
@@ -0,0 +1,978 @@
// ===============================================================
//
// "Web Graphics Controller"
// (c) 2020- Sh Graphics
//
// No front-end frameworks.
// Core functionality: NodeJS, Express, Handlebars -templating.
//
// Most serverside code is in /routes and /utils.
// Most clientside code is in /static/js/spx_gc.js
//
// Basic usage:
// node server.js [config.json]
//
// ===============================================================
const express = require('express')
const session = require('express-session')
const bodyParser = require('body-parser')
const cors = require('cors')
const exphbs = require('express-handlebars')
const app = express()
const ip = require('ip')
const fs = require('fs')
const morgan = require('morgan')
const path = require('path')
const rfs = require('rotating-file-stream')
var macaddress = require('macaddress');
console.log('\n WebCG Controller server is starting.\n Closing this window/process will stop the server.\n');
// EXIT HANDLER
process.on('exit', function(code) {
// usage: process.exit(1)
let codestr="Unspecified exit code";
switch (code) {
case 1:
codestr = "Error while reading config.json"
break;
case 2:
codestr = "Kill request received from WebCG client. If pm2 used, the server will restart automatically."
break;
case 3:
codestr = "Port is already in use on this machine."
break;
default:
break;
}
return console.log(`\n\nWebCG exit! (Errorcode ${code}: ${codestr})\n\n`);
});
// global.config
const cfg = require('./utils/spx_getconf.js');
cfg.readConfig()
if (!configfileref) {
process.exit(1)
}
const spx = require('./utils/spx_server_functions.js');
const logger = require('./utils/logger.js');
// STATICS
app.use(express.static(path.join(__dirname,('static'))))
app.use(express.static(path.resolve(spx.getStartUpFolder(),'ASSETS')))
const ipad = ip.address();
var pjson = require('./package.json');
var packageversion = pjson.version;
const vers = process.env.npm_package_version || packageversion || 'X.X.X';
global.vers = vers;
global.excel = {'readtime':0, 'filename':'', 'data':''}; // used as Excel cache
// Added in 1.0.15. An memory persistent object to handle -------------
// rundown state to avoid unnecessary disk I/O and improve performance.
global.rundownData = {}
// --------------------------------------------------------------------
const port = config.general.port || 5656;
global.CCGSockets = [];
global.LastBrowsedTemplateFolder = '';
macaddress.one(function (err, mc) {
let macaddress = String(mc);
let pseudomac = macaddress.split(':').join('').substring(0,8);
global.hwid = config.general.hostname || pseudomac;
global.pmac = pseudomac;
});
// Setup Morgan
let LOGFOLDER = config.general.logfolder || spx.getStartUpFolder() + '/LOG';
var logDirectory = path.normalize(LOGFOLDER);
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)
var accessLogStream = rfs.createStream('access.log', {
interval: '1d', // rotate daily
path: logDirectory
})
app.use(morgan('combined', { stream: accessLogStream }))
// Handlebars templating, updated in 1.3.3
app.engine('handlebars', exphbs.engine({
extname: '.handlebars',
//defaultLayout: 'filelist',
helpers: {
max5: function (data) {
if (parseInt(data)>5) { data = 5; }
return String(data);
},
// Convert JSON data and return as JSONstring.
// (used to store arrays to hidden form fields)
DataToJSONString: function (data) {
let s = encodeURI(JSON.stringify(data));
return s;
},
// conditional IF helper
ifValue: function(a, b, options) {
// console.log('a: ' + a + ', b: ' + b);
if(String(a) === String(b) ) {
return options.fn(this);
}
return options.inverse(this);
},
// conditional IF helper return value A if "value" == "compareTo", B if not
ifValueMatch: function(value, compareTo, A, B ) {
if(value == compareTo) {
return A;
} else {
return B;
}
},
// conditional IF helper
itemCount: function(arr) {
// console.log('a: ' + a + ', b: ' + b);
return arr.length;
},
// conditional IF helper
ifGreater: function(a, b, options) {
if(a > b) {
// console.log('TRUE. A (' + a + ') was bigger.');
return options.fn(this);
}
// console.log('FALSE. B (' + b + ') was bigger.');
return options.inverse(this);
},
// populate CasparCG options for show config templates
generateDataFormatOptions(currentDataformat='json') {
let html="";
let AvailableFormats=['json', 'xml'];
AvailableFormats.forEach((Option,i) => {
let curFormat = Option.toLowerCase();
let cfgFormat = currentDataformat.toLowerCase();
let sel = "";
if (curFormat==cfgFormat){ sel="selected" }
html += '<option value="' + curFormat + '" ' + sel + '>' + curFormat + '</option>';
});
return html
},
// populate CasparCG options for show config templates
generateCasparCgPlayoutOptions(currentServer) {
let html="";
let AvailableServers=['-'];
if (config.casparcg){
config.casparcg.servers.forEach((element,i) => {
AvailableServers.push(element.name);
});
}
AvailableServers.forEach((serverOption,i) => {
let curSrv = serverOption.toUpperCase();
let cfgSrv = currentServer.toUpperCase();
let sel = "";
if (curSrv==cfgSrv){ sel="selected" }
html += '<option value="' + curSrv + '" ' + sel + '>' + curSrv + '</option>';
});
return html
},
// generate Open datafolder -commands in the UI menu for different operating systems
GenerateOpenFolderCommands(){
let platform = process.platform;
let html="";
switch (platform) {
case 'win32':
html += '<!-- Below folder commands only available on Windows in version 1.0 -->\n';
html += '<br><A HREF="" onClick="spx_system(\'openDatafolder\');return false;"><i class="far fa-folder-open"></i>&nbsp; '+ spx.lang('button.exploredataroot') +'</A>\n';
html += '<br><A HREF="" onClick="spx_system(\'openTemplatefolder\');return false;"><i class="far fa-folder-open"></i>&nbsp; '+ spx.lang('button.exploretemplates') +'</A>\n';
break;
default:
html = "<!-- Sorry, only windows supported for Open Folder -commands in version 1.0 -->\n";
break;
}
return html;
},
// draw all custom controls in the argument array
GenerateCustomControls(arr){
if (!arr) {return}; // if no extras, do nuthin'
let html = "";
html += '<table style="width: 100%;" cellspacing="5" border="0">\n';
arr.forEach((control,index) => {
html += '<tr>'
html += '<td width="50%">'
html += '<span class="spxTableHead">' + control.description + '</span>'
html += '</td>'
html += '<td>'
switch (control.ftype){
case 'button':
html += '<button type="button" class="wide spxFlexItem btn ripple ' + control.bgclass + '" ';
html += 'data-spx-color="' + control.bgclass + '" ';
html += 'onClick="' + control.fcall + '" ';
html += '>' + control.text + '</button>\n';
break;
case 'togglebutton':
html += '<button type="button" class="wide spxFlexItem btn ripple ' + control.bgclass + '" ';
html += 'data-spx-color="' + control.bgclass + '" ';
html += 'data-spx-playtext="' + control.text0 + '" ';
html += 'data-spx-stoptext="' + control.text1 + '" ';
html += 'data-spx-status="false" ';
html += 'onClick="' + control.fcall + '" ';
html += '>' + control.text0 + '</button>\n';
break;
case 'selectbutton':
html += '<table class="wide" style="margin:0, padding:0; border:0px;">\n';
html += '<tr><td width="60%">\n';
html += '<select id="select' + index + '" class="wide btn ' + control.bgclass + '">\n';
if (control.items) {
control.items.forEach((option,nro) => {
html += '<option value="' + option.value + '">' + option.text + '</option>\n';
});
}
html += '</td><td>\n';
html += '<button type="button" class="wide spxFlexItem btn ripple ' + control.bgclass + '" ';
html += 'data-spx-color="' + control.bgclass + '" ';
html += 'onClick="' + control.fcall + '(\'select' + index + '\');" ';
html += '>' + control.text + '</button>\n';
html += '</td></tr></table>\n';
break;
default:
logger.warn('server.GenerateCustomControls error in item [' + index + '] unknown controltype [' + control.ftype + '].');
break;
}
html += '</td></tr>\n'
});
html += '</table><BR>\n'
return html;
},
// generate a pretty string for each template to display playout configs
GeneratePlayoutInfo(playserver='', playchannel='', playlayer='', webplayout='', out='' , itemid='') {
webplayout = spx.max5(webplayout);
let html = '<div data-spx-name="playoutConfig">';
// html += '<input type="text" data-clipboard-action="copy" data-clipboard-target="#copy' + itemid + '">';
if (playserver !="-") {
html += "<span onmouseover=\"tip('" + spx.lang('hover.ccgserver') + "');\" data-name='srv'>" + playserver + "</span>-";
html += "<span onmouseover=\"tip('" + spx.lang('hover.ccgchannel') + "');\" data-name='cha'>" + playchannel + "</span>-";
html += "<span onmouseover=\"tip('" + spx.lang('hover.ccglayer') + "');\" data-name='lay'>" + playlayer + "</span>";
html += "&nbsp;";
// html += " <span class='delim'></span> ";
};
if (webplayout !="-") {
html += "<span class=\"copyid\" onclick=\"revealItemLayer(this);\" id=\"layer" + itemid + "\" onmouseover=\"tip('" + spx.lang('hover.weblayer') + "');\" data-name='web'>" + webplayout + "</span>";
// html += " <span class='delim'></span> ";
};
if (out) {
html += "<span class=\"copyid\" onclick=\"revealItemTiming(this);\" id=\"out" + itemid + "\" onmouseover=\"tip('" + spx.lang('hover.outmode') + "');\" data-name='out'>" + out + "</span>";
};
html += "<span id=\"copy" + itemid + "\" class=\"copyid\" onclick=\"revealItemID(this);\" onmouseover=\"tip('Copy ID " + itemid + "');\" data-name='id'>ID</span>";
// help button
html += "<span class=\"helpid\" onclick=\"help('ITEM-DETAILS');\" onmouseover=\"tip('Help');\">?</span>";
html += "</div>"
return html
},
// populate filelist for the controller add CSV file view (for instance)
generateFileList(requestType) {
let folder = '' // folder to start to scan
let filter = '' // extensions to show ('csv' for example)
// requestType = search command, such as "CSV"
switch (requestType) {
case 'CSV':
folder = path.join(config.general.dataroot, currentShow, 'profile.json');
filter = 'csv';
break;
default:
logger.error('Unknown requestType ' + requestType + ', doing nothing');
return; // exit function
break;
}
let html="";
// get show profile
// iterate all templates
let profileFile = path.join(config.general.dataroot, currentShow, 'profile.json');
showProfileData = spx.GetJsonData(profileFile);
if(!showProfileData.templates) {
// if no templates assigned to this project
html += '<span style="display: inline-block; margin: 10px;padding: 0.6em 1.7em; background: #0000001c; color:#ffffff; font-size:0.7em; border-radius:2em; text-align:center;">' + spx.lang('warning.notemplates') + '</span>';
return html
}
showProfileData.templates.forEach((template,i) => {
html += '<div onClick="addSelectedTemplate(' + i + ');" class="addTemplateItem row_accent' + template.uicolor + '">\n';
html += '<button type="button" class="addbtn bg_blue ripple" onClick="addSelectedTemplate(' + i + ');" style="float:right;">+</button>\n';
html += '<span class="templateName">' + spx.prettifyName(template.relpath) + '</span><BR>\n';
html += '<span class="templateDesc">' + template.description + '&nbsp;</span>\n';
html += '</div>\n';
});
return html
},
// populate templatelist for the controller add template view
generateTemplateList(currentShow) {
let html="";
// get show profile
// iterate all templates
let profileFile = path.join(config.general.dataroot, currentShow, 'profile.json');
showProfileData = spx.GetJsonData(profileFile);
if(!showProfileData.templates) {
// if no templates assigned to this project
html += '<span style="display: inline-block; width: 100%; margin: 10px;padding: 0.6em 1.7em; background: #0000001c; color:#ffffff; font-size:0.7em; border-radius:2em; text-align:center;">' + spx.lang('warning.notemplates') + '</span><br><br>';
return html
}
showProfileData.templates.forEach((template,i) => {
html += '<div onClick="addSelectedTemplate(' + i + ');" class="addTemplateItem row_accent' + template.uicolor + '">\n';
html += '<button type="button" class="addbtn bg_blue ripple" onClick="event.stopPropagation(); addSelectedTemplate(' + i + ');" style="float:right;">+</button>\n';
html += '<span class="templateName">' + spx.prettifyName(template.relpath) + '</span><BR>\n';
html += '<span class="templateDesc">' + template.description + '&nbsp;</span>\n';
html += '</div>\n';
});
return html
},
// convert timestamp (epoch) to locale date string "31.12.2021 17:45"
generateTimestamp(epoch) {
let date = new Date(parseInt(epoch));
let fDate = date.toLocaleDateString();
let fTime = date.toLocaleTimeString();
return fDate + ' ' + fTime;
},
// populate WebPlayout options for show config templates
generateButtonColorClasses(currentValue)
{
let html="";
let AvailableOptions=[
{
text:spx.lang('color.red'),
value:'bg_red'
},
{
text:spx.lang('color.green'),
value:'bg_green'
},
{
text:spx.lang('color.grey'),
value:'bg_grey'
},
{
text:spx.lang('color.orange'),
value:'bg_orange'
},
{
text:spx.lang('color.blue'),
value:'bg_blue'
},
{
text:spx.lang('color.black'),
value:'bg_black'
},
{
text:spx.lang('color.light'),
value:'bg_grey15'
}
];
AvailableOptions.forEach((OptionItem,i) => {
let sel = "";
if (OptionItem.value==currentValue){ sel="selected" }
html += '<option value="' + OptionItem.value + '" ' + sel + '>' + OptionItem.text + '</option>';
});
return html
},
// return username
CurrentUserName() {
return global.user;
},
// populate out options for show config templates
generateOutOptions(currentOut) {
logger.debug('Generating out options. This selection: ' + currentOut + '.');
let html="";
let AvailableOptions=[
{value:'manual', text:'Manually'},
{value:'none', text:'None (just play)'},
{value:'1000', text:'In 1 second'},
{value:'2000', text:'In 2 seconds'},
{value:'4000', text:'In 4 seconds'},
{value:'6000', text:'In 6 seconds'},
{value:'10000', text:'In 10 seconds'}
];
AvailableOptions.forEach((outOption,i) => {
let sel = "";
let CurVal = currentOut.toLowerCase();
let CurOpt = outOption.value.toLowerCase();
if (CurVal==CurOpt){ sel="selected" }
html += '<option value="' + CurOpt + '" ' + sel + '>' + outOption.text + '</option>';
});
return html
},
// populate WebPlayout options for show config templates
generateWebPlayoutOptions(currentLayer='-') {
// console.log('generateWebPlayoutOptions got currentLayer: ' + currentLayer);
currentLayer = spx.max5(currentLayer);
logger.debug('Generating webplayout options. This selection: '+ currentLayer);
let html="";
let AvailableLayers=['-',1,2,3,4,5];
AvailableLayers.forEach((layerOption,i) => {
let curSrv = layerOption || '-';
let cfgSrv = currentLayer || '-';
let sel = "";
if (curSrv==cfgSrv){ sel="selected" }
html += '<option value="' + curSrv + '" ' + sel + '>' + curSrv + '</option>';
});
return html
},
// preview span for collapsed view of each rundown item
generateCollapsedHeadline(DataFields) {
return spx.generateCollapsedHeadline(DataFields);
},
// generate radio buttons for show config templates
generateColorAccents(selectedIndex, templateIndex) {
let colors=['gra','red','ora','gre','blu','pin','vio','bla'];
let html ="";
let sel = "";
colors.forEach((color,loopindex) => {
if (selectedIndex==loopindex) { sel = "checked" } else {sel=""};
html += '<input type="radio" name="templateData[uicolor]" value="' + loopindex + '" id="' + color + '_' + templateIndex + '" ' + sel +'/><label for="'+ color + '_' + templateIndex +'"><span class="' + color + '"></span></label>';
});
return html
},
// Returns IP for socket server of this app's IP address
getServerAddress: function () {
var ip = require('ip');
var myIP = ip.address() + ':' + port;
return myIP;
},
// Returns resolution options for App Config > resolution
// Added in 1.3.0
generateResolutionOptions: function (resolutionOption) {
let html = '';
let options = [
{ 'value': 'HD', 'text': 'HD (1080p)' },
{ 'value': '4K', 'text': '4K (2160p)' },
{ 'value': 'AUTO', 'text': 'AUTO (responsive)' },
];
let selectedFound = false;
options.forEach((opt,i) => {
let sele = '';
if (opt.value.toUpperCase() == resolutionOption.toUpperCase()) {
selectedFound = true;
sele = 'selected'
};
html += '<option ' + sele + ' value=' + opt.value + '>' + opt.text + '</option>\n';
});
return html;
},
// See if cssSizeValue has 'vh' or 'vw' in it then return HD values
// Added in 1.3.0
localPreviewSize: function (cssSizeValue) {
if (cssSizeValue.includes('vw')) {
return '1920';
} else if (cssSizeValue.includes('vh')) {
return '1080';
} else {
return cssSizeValue;
}
},
// Returns unique ID for this host machine
getHostID: function () {
return global.pmac;
},
hookLoadControllerPlugins: function () {
// Loads init.js files from all sub folders of ASSETS/plugins/<plugName>/
let html = '';
try {
let BrowseFolder = path.join(spx.getStartUpFolder(), 'ASSETS', 'plugins');
const list = spx.GetFilesAndFolders(BrowseFolder);
list.foldArr.forEach(pluginName => {
if ( pluginName.charAt(0) == '_' || pluginName.charAt(0) == '.' || pluginName === 'lib' ) {
html += '<!-- Note: disabled plugin ' + pluginName + ' skipped. -->\n'
} else {
logger.verbose('Loading plugin ' + pluginName );
html += '<script type="module" src="/plugins/' + pluginName + '/init.js"></script>\n'
}
})
} catch (error) {
html = '<!-- hookLoadControllerPlugins error: ' + error + ' -->';
}
return html
},
// Convert JSON \\n to newline for textarea
injectNewlines(jsonString) {
let multiline = jsonString.replace('\\n', '\r\n')
return multiline;
},
// Language string helper
lang(str) {
return spx.lang(str);
},
// Replace underscores with spaces (used by breadcrumbs)
prettyfyBreadcrumb(str) {
let fixed = str
fixed = fixed.replace(/_/g, ' ');
fixed = fixed.replace(/-/g, ' ');
return fixed;
},
// Return object as JSON string
myStringify(obj) {
return "// Stringified JSON\n\n" + JSON.stringify(obj, null, 4);
},
// Get template name from filepath
nameFromTemplatePath(filepath) {
return spx.prettifyName(filepath);
},
// Check config and return value
allowOpeningFolder() {
let disableButton = config.general.disableOpenFolderCommand || false;
// console.log('allowOpeningFolder: ' + disableButton);
if (disableButton==true || disableButton=="true") {
return 'disabled';
} else {
return '';
}
},
// Make long strings nicer to look at
shortifyUIstrings(str) {
return spx.shortifyName(str);
},
// generate checkbox to appconfig
OpenChromeCheck() {
let launch = config.general.launchBrowser || false;
if (launch){
return '<input type="checkbox" checked name="general[launchBrowser]">';
}
else {
return '<input type="checkbox" name="general[launchBrowser]">';
}
},
// Get a correct class for a play button
playButtonClass(onair) {
let value = onair || "false";
let ONAIR = String(value).toUpperCase();
if (ONAIR=="TRUE") {
return "bg_red";
} else {
return "bg_green";
}
},
// Get a correct class for a play button
playButtonText(onair) {
let value = onair || "false";
let ONAIR = String(value).toUpperCase();
if (ONAIR=="TRUE") {
return spx.lang('button.stop');
} else {
return spx.lang('button.play');
}
},
// Define a class for the playicon
playIconClass(onair) {
// console.log('Play icon [' + onair + ']');
let value = onair || "false";
let ONAIR = String(value).toUpperCase();
if (ONAIR=="TRUE") {
return "playTrue";
} else {
return "playFalse";
}
},
// Show username / password in the login view
ShowDemoLoginInfo() {
let showuserpass = config.general.showusercommapass || '';
if (showuserpass==""){
return "<!-- not present config.general.showusercommapass:user,pass -->";
}
let u = showuserpass.split(',')[0]
let p = showuserpass.split(',')[1]
let html="";
html += '<!-- config.general.showusercommapass -->';
html += '<tr>';
html += '<td colspan="2" class="center vmid" style="background-color: #1d1e252f; padding:0.8em; border-radius:0.8em; font-size:0.6em; line-height:1.5em;">';
html += '<span >';
html += 'Login to this instance with<BR>';
html += 'user <span style="color: rgb(252, 174, 30);"><B>' + u + '</B></span>, ';
html += 'pass <span style="color: rgb(252, 174, 30);"><B>' + p + '</B></span>';
html += '</span>';
html += '</td>';
html += '</tr>';
return html;
},
// Print out version number
ShowVersion() {
return vers;
},
// Return available lang files as options with selected one
DropdownOptionsLANG() {
let html = '';
let sel = '';
let localesFolder = path.join(spx.getStartUpFolder(), 'locales');
let fileListData = spx.getJSONFileList(localesFolder);
fileListData.files.forEach((element,i) => {
sel = '';
if (element.name == config.general.langfile)
{
sel = 'selected';
}
html += '<option value="' + element.name + '" ' + sel + '>' + element.name.replace('.json', '') + '</option>';
});
return html
},
// This is used to populate filelist dropdown control type in templates.
// Note: these files are returned as http-assets from SPX server
// Feature added in 1.0.3. and improved in 1.0.6, 1.0.9
// v1.0.15 adds relative assets (within template root folder).
PopulateFilelistOptions(assetfolder, extension, value, relpath='') {
let html = '';
let sel = '';
let fullFilePath = '';
let fullPath = '';
if ( assetfolder.startsWith('./') ) {
// relative path support added in 1.0.15
let templateFile = path.join(spx.getStartUpFolder(), 'ASSETS', 'templates', relpath);
let templateRootFolder = path.dirname(templateFile)
fullPath = path.join(templateRootFolder, assetfolder)
} else {
// absolute path in ASSETS
let assetPath = path.normalize(assetfolder || '');
fullPath = path.join(spx.getStartUpFolder(), 'ASSETS', assetPath); // fixed (again in 1.0.12)
}
let selectedValue = value;
let fileList = spx.getFileList(fullPath, extension);
// console.log('files', fileList);
if (fileList) {
fileList.forEach((fileRef,index) => {
if (assetfolder.substr(assetfolder.length - 1)!="/") { // fixed a bug in 1.1.1
assetfolder = assetfolder + "/";
}
fullFilePath = assetfolder + fileRef;
sel = "";
if (fullFilePath == selectedValue) {
sel = 'selected';
}
html += '<option value="' + assetfolder + fileRef + '" ' + sel + '>' + fileRef + '</option>';
});
}
return html
},
// Servers status check request
ServerStatus() {
let html = "";
if (config.casparcg) {
config.casparcg.servers.forEach((element,i) => {
let disabledClass = ''
if (element.disabled) {
disabledClass = 'disabledServer';
}
html += '<div class="dropdown serverbtn">';
html += ' <i id="indicator' + i + '" class="fas fa-check-circle" style="color: 00CC00;"></i>&nbsp;<span class="serverIndicator ' + disabledClass + '" id="server' + element.name + '">' + element.name + '</span>';
html += ' <div class="dropdown-content-right">';
html += ' <span style="display: block; opacity: 0.4; background-color: #000; width:100%;">' + element.host + ':' + element.port + '</span>';
html += ' <A href="#" OnClick="spx_system(\'CHECKCONNECTIONS\');return false;">Connection check</A><br>';
html += ' <A href="#" OnClick="clearUsedChannels(\'' + element.name + '\');">Clear all layers</A><br>';
html += ' <A href="#" OnClick="toggleActive(\'' + element.name + '\');">Toggle on/off</A><br>';
html += '</div>';
html += '</div>';
});
}
return html;
// end serverstatus
},
// See if there is custom content packages in the ASSETS folder
DetectCustomContentPackages() {
try {
const fileref = path.join(spx.getStartUpFolder(), 'ASSETS', 'spx-content-repo.txt');
if (fs.existsSync(fileref)) {
var contents = fs.readFileSync(fileref, 'utf8') || '';
if (contents == 'CONTENT_FULL') {
logger.debug('DetectCustomContentPackages: CONTENT_FULL');
return '';
} else {
logger.debug('DetectCustomContentPackages: returns ' + contents);
return '<div class="namedContentIndicator">' + contents + '</div>';
}
} else {
// No file found, return empty string
return '';
}
}
catch (error) {
logger.error('DetectCustomContentPackages Error: ' + error);
return "";
}
// end DetectCustomContentPackages
},
GenerateLocalRendererOptions(mode) {
return '<!-- Added in 1.1.0 but LocalRendererOptions are not in use yet -->\n';
} // end LocalRendererOptions
} // end helpers
})); // end app.engine
app.set('view engine', 'handlebars');
app.set('views', path.join(__dirname, 'views'));
// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(session({
secret: 'salainenSalaisuus#007',
resave: false,
saveUninitialized: false
}));
global.user = "";
// Router files
const ROUTEfiles = require('./routes/routes-api.js');
app.use('/api/', ROUTEfiles);
const ROUTEpubAPIv1 = require('./routes/routes-api-v1.js');
app.use('/api/v1/', ROUTEpubAPIv1);
const ROUTEapp = require('./routes/routes-application.js');
app.use('/', ROUTEapp);
const ROUTEccg = require('./routes/routes-casparcg.js');
app.use('/CCG', ROUTEccg);
process.on('uncaughtException', function(err) {
logger.error('Uncaught Exception: ' + err);
if(err.errno === 'EADDRINUSE') {
console.log('Process failed! Port is in use? [' + err + ']'); // most likely app running on this port already
setTimeout(function(){ process.exit(3); }, 2000);
} else if (err.errno === 'ENOENT') {
console.log('File not found error detected! [' + err + ']');
} else {
console.log('\n\nThis error in unhandled, consider SPX restart! ', err); // Added in 1.1.1
}
});
var server = app.listen(port, (err) => {
let splash = ' Copyright 2020- SPX Graphics\n\n' +
` SPX Solo version .......... ${global.vers}\n` +
' License ................... See LICENSE.txt\n' +
` Config file ............... ${configfileref}\n` +
` Cfg / locale .............. ${config.general.langfile}\n` +
` Cfg / host-id (name) ...... ${global.pmac} (${config.general.hostname})\n` +
` Cfg / loglevel ............ ${config.general.loglevel} (options: error | warn | info | verbose | debug )\n` +
` Cfg / dataroot ............ ${path.resolve(config.general.dataroot)}\n` +
` Cfg / logfolder ........... ${logDirectory}\n`;
/* ` Cfg / lauchchrome ...... ${config.general.launchchrome}\n` */
if (config.general.apikey && config.general.apikey != '') {
splash += ` Cfg / apikey ........... Set in config.json\n`;
}
if (config.osc?.enable) {
splash += ` OSC enabled ............ port ${OSCport}\n`;
}
let TemplatesFromInfo;
let tmplSourceAddress = spx.getTemplateSourcePath();
if ( tmplSourceAddress == "") {
TemplatesFromInfo = "See caspar.config"
} else {
TemplatesFromInfo = tmplSourceAddress
}
splash +=
`\n Explore WebCG related resources:` +
`\n` +
`\n This is WebCG Solo. For professional features and support\n options see WebCG Production and WebCG Broadcast.`;
console.log(splash);
let prompt = 'Open WebCG Solo in a browser:';
let spxUrl = `http://${ipad}:${port}`;
let spxLoc = `http://localhost:${port}`;
let urLeng = spxUrl.length;
let loLeng = spxLoc.length;
let prLeng = prompt.length;
let maxWid = Math.max(urLeng, loLeng, prLeng)
let minWid = Math.min(urLeng, loLeng, prLeng)
let line1s = '╭' + "─".repeat(maxWid+4) + "╮"
let line2s = prompt + " ".repeat(maxWid-prLeng)
let line3s = '╰' + "─".repeat(maxWid+4) + "╯"
let spaCnt, spacer1, spacer2
spacer1 = " ".repeat(maxWid-urLeng)
spacer2 = " ".repeat(maxWid-loLeng)
console.log('\n ' + line1s);
console.log(' │ ' + line2s + ' │');
// console.log(' │ ' + spxUrl + spacer1 + ' │');
console.log(' │ ' + spxLoc + spacer2 + ' │');
console.log(' ' + line3s);
console.log('');
if ( config.general.launchBrowser || global.generatingDefaultConfig ) {
try {
(async () => {
require("openurl").open(`http://${ipad}:${port}/`)
})();
} catch (error) {
console.warn('Could not open browser.')
}
}
});
global.io = require('socket.io')(server);
var clients = {}
io.sockets.on('connection', function (socket) {
logger.verbose('*** Socket connection (' + socket.id + ") Connections: " + io.engine.clientsCount);
clients[socket.id] = socket;
notifyMultipleControllers(); // on Connection
socket.on('disconnect', async function () {
let SPXClientName = clients[socket.id].SPXClientName || '** no name **';
logger.verbose('*** Socket disconnected (' + socket.id + ") Connections: " + io.engine.clientsCount);
delete clients[socket.id];
// Notify controller of other clients lost (such as renderers closed with X)
data = {};
data.spxcmd = 'clientLostNotification'
data.source = 'windowClose'
data.clientName = SPXClientName
io.emit('SPXMessage2Client', data);
notifyMultipleControllers(); // on Disconnect
}); // end disconnect
socket.on('SPXWebRendererMessage', function (data) {
io.emit('SPXMessage2Client', data);
});
socket.on('SPXMessage2Server', function (data) {
logger.verbose('SPXMessage2Server received', data)
switch (data.spxcmd) {
case 'identifyClient':
socket.SPXClientName = data.name;
logger.verbose('Identified [' + socket.id + '] as [' + data.name + ']');
break;
case 'saveToLog': // Added in 1.3.0
let level = data.level || 'info';
let ref = data.fileref || 'unknown';
logger[level]('TemplateLog [' + ref + '] --> ' + data.message);
break;
case 'command-name-here':
// action here
break;
default:
logger.warn('Unknown SPXMessage2Server command: ' + data.command);
}
}); // end message to server
}); // end socket-io
function notifyMultipleControllers() {
if ( config.general?.disableSeveralControllersWarning==true ) {
logger.debug('notifyMultipleControllers feature is disabled with a config flag.');
return;
}
setTimeout(function(){
let count = 0;
for (const [key, value] of Object.entries(clients)) {
if (value.SPXClientName == 'SPX_CONTROLLER') {
count++;
}
}
logger.debug('notifyMultipleControllers: ' + count + ' controllers connected.');
let data = {};
data.spxcmd = 'notifyMultipleControllers'
data.count = count
io.emit('SPXMessage2Client', data);
}, 100); // small delay
}
+6
View File
File diff suppressed because one or more lines are too long
+65
View File
@@ -0,0 +1,65 @@
/* inspired by https://codepen.io/wildbeard/pen/eZZPZr */
input[type="radio"] {
display: none;
}
input[type="radio"]:checked + label span { transform: scale(1.25); }
input[type="radio"]:checked + label .gra { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .red { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .ora { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .gre { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .blu { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .pin { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .vio { border: 2px solid #ffffff;}
input[type="radio"]:checked + label .bla { border: 2px solid #ffffff;}
label {
display: inline-block;
width: 20px;
height: 20px;
margin-right: 10px;
cursor: pointer;
}
label:hover span {
/* transform: scale(1.25); */
}
label span {
display: block;
width: 100%;
height: 100%;
transition: transform .2s ease-in-out;
}
/* grey, red, orange, green, blue, pink, violet, black */
label span.gra { background: #7a7a7a;}
label span.red { background: #DB2828;}
label span.ora { background: #F2711C;}
label span.gre { background: #21BA45;}
label span.blu { background: #2185D0;}
label span.pin { background: #E03997;}
label span.vio { background: #8840FF;}
label span.bla { background: #000000;}
.row_accent0 {border-left: 10px solid #696969; }
.row_accent1 {border-left: 10px solid rgb(114, 38, 38); }
.row_accent2 {border-left: 10px solid rgb(117, 70, 38); }
.row_accent3 {border-left: 10px solid rgb(42, 107, 57); }
.row_accent4 {border-left: 10px solid rgb(41, 87, 122); }
.row_accent5 {border-left: 10px solid rgb(124, 57, 95); }
.row_accent6 {border-left: 10px solid rgb(91, 67, 129); }
.row_accent7 {border-left: 10px solid #181818; }
.row_accentExtras {border-left: 10px solid #acacac; }
.accent_{
background-color: rgba(255,255,255,0.05);
border: 0px solid yellow;
width:100%;
padding:10px;
margin-bottom:15px;
border-left: 12px solid white;
box-shadow: rgba(0, 0, 0, 0.7) 0px 0px 10px;
}
+103
View File
@@ -0,0 +1,103 @@
/*
Copyright 2020 SPX Graphics
CSS file for (bg)colors. Must be loaded after spx_layout.css
*/
.disabled {
opacity: 0.2;
cursor: not-allowed;
}
.bg_body {
background-color: #414C5A;
}
.darkBg {
background-color: rgba(0, 0, 0, 0.2);
}
.bg_green {
background-color: #54af3c;
}
.green {
color: #5f9750;
}
.bg_yellow {
background-color: #e5c310;
}
.bg_orange {
background-color: #e57010;
}
.bg_blue {
background-color: #375ea3;
}
.bg_red {
background-color: #9c1a29;
}
.bg_black {
background-color: #000000;
}
.bg_white {
background-color: #ffffff;
}
.bg_grey80 {
background-color: #272833;
}
.bg_grey {
background-color: #485a6b;
}
.bg_grey60 {
background-color: #2f303e;
}
.bg_grey40 {
background-color: #343D48;
}
.bg_grey15 {
background-color: #656e7a;
/* #706e83; */
}
.bg_grey10 {
background-color: #9aa4b1;
/* #706e83; */
}
.black{
color: black;
}
.bg_darken{
background-color: rgba(0,0,0,0.07);
}
.bg_lighten{
background-color: rgba(255,255,255,0.05);
}
.newVersionBubble {
padding: 2px 10px;
background-color: rgb(39, 95, 58);
border-radius: 4px;
/* margin-right: 10px; */
font-size: 90%;
cursor: pointer;
display: inline-block;
margin-top: 5px;
}
.fg_orange {
color: #e57010;
}
+448
View File
@@ -0,0 +1,448 @@
*, *:before, *:after {
box-sizing: inherit;
margin: 0;
padding: 0;
/* user-select: none; */
}
html, body, .page {
height: 100%;
padding: 0;
margin: 0;
font-size: 4vw; /* ============ font size =========== */
}
.page {
display: flex;
flex-direction: column;
}
.uitext {
font-size: 0.8em;
color: rgba(255, 255, 255, 0.5);
}
.swname {
font-size: 1.5em;
color: rgba(255, 255, 255, 0.1);
}
.page-header {
/* height: 130px; */
display: flex;
flex: 0 0 auto;
align-items: center;
}
.page-content {
display: flex;
flex: 1 1 auto;
overflow: hidden;
position: relative;
align-content: center;
}
.page-footer {
display: flex;
flex: 0 0 auto;
flex: 0 0 auto;
padding: 1em;
align-items: center;
}
.well {
margin: 0;
padding: 0;
padding: 1em;
height: 100%;
flex-grow: 1;
min-width: 48%;
overflow-y: auto;
border-radius: 0.5em;
background-repeat: repeat-x;
background-position: top, bottom;
background-image: url('../img/shadatop.png'), url('../img/shadabot.png');
}
.well + .well {
margin: 0% 0.5%;
}
.darken {
background-color: rgba(0, 0, 0, 0.1);
}
.lighten {
background-color: rgba(255, 255, 255, 0.1);
}
.skroll::-webkit-scrollbar-track {
background-color: transparent;
}
.skroll::-webkit-scrollbar {
width: 1em;
background-color: rgba(245, 245, 245, 0);
}
.skroll::-webkit-scrollbar-thumb {
border-radius: 0.5em;
background-color: rgba(255, 255, 255, 0.13);
}
.skroll::-webkit-scrollbar-thumb:hover
{
background-color: rgba(255, 255, 255, 0.274);
}
.headitem1 {
flex-grow: 1;
flex-basis: 0;
text-align: center;
border: 0px solid rgb(0, 255, 55);
}
.headitem3 {
flex-grow: 3;
flex-basis: 0;
display: flex;
padding: 0.5em;
align-items: center;
justify-content: flex-end;
border: 0px solid rgb(255, 0, 0);
}
.footitem1 {
flex-grow: 1;
flex-basis: 0;
text-align: center;
}
.footitem2 {
padding: 1em;
flex-grow: 2;
flex-basis: 0;
display: flex;
text-align: center;
align-items: center;
flex-direction: row;
}
.footbox1 {
flex-grow: 1;
text-align: center;
}
.footbox2 {
flex-grow: 0;
flex-shrink: 1;
text-align: center;
}
.footbox3 {
flex-grow: 1;
text-align: center;
}
.cursor {
cursor: pointer;
}
.nowrap {
white-space: nowrap;
}
.icon {
width: 3em;
margin: 0.5em;
border-radius: 0.6em;
box-shadow: 0 0 10px rgba(255, 166, 0, 0);
/* transition-property: box-shadow, scale; */
transition: transform 500ms ease-in-out;
}
.icon:hover {
box-shadow: 0 0 10px rgba(255, 255, 255, 0.507);
transform: scale(0.98) rotate(5deg);
/* transition-property: box-shadow, scale, rotate; */
transition: transform 500ms ease-in-out;
}
.folderItem {
margin-bottom: 0.2em;
list-style:circle;
color: red;
}
.icon::before {
font-family: "FontAwesomeSol900";
display: inline-block;
margin-right: 10px;
font-size: 1.2em;
-webkit-font-smoothing: antialiased;
}
.folder::before { content: "\f07b"; color: rgb(9, 119, 170);}
.file::before { content: "\f15b"; color: rgb(241, 241, 237);}
.back::before { content: "\f106"; }
.home::before { content: "\f102"; }
/* ---------------- messageSlider styles -------------*/
#messageSlider {
top: 0px;
left: 50%;
opacity: 0; /* animated */
display: flex;
min-width: 10%;
font-size: 1.7em !important;
position: absolute;
padding: 0.5em 2em;
align-items: center;
justify-content: center;
border-radius: 0 0 1em 1em;
transform: translateX('-100%');
color: rgba(255, 255, 255, 0.7);
}
.happyMsg {
background-color: rgba(20, 116, 68, 0.76);
box-shadow: rgba(0, 0, 0, 0.4) 0 2px 10px;
}
.infoMsg {
background-color: rgba(0, 0, 0, 0.4);
box-shadow: rgba(0, 0, 0, 0.4) 0 2px 10px;
}
.warnMsg {
background-color: rgba(143, 81, 0, 0.794);
box-shadow: rgba(0, 0, 0, 0.4) 0 2px 10px;
}
.errorMsg {
background-color: rgba(170, 5, 5, 0.89);
box-shadow: rgba(0, 0, 0, 0.4) 0 2px 10px;
}
/* ---------------- /messageSlider -------------*/
#folderinbox {
min-height: 30px;
display: grid;
border: 0px solid yellow;
}
#fileinbox {
min-height: 30px;
display: grid;
border: 0px solid yellow;
grid-template-columns: 1fr 1fr;
}
.fileitem {
height: 40vw;
align-items: center;
flex-direction:column;
border: 2px solid rgba(255, 255, 255, 0.137);
justify-content: flex-end;
background-color: rgba(0, 0, 0, 0.274);
background-position: center 40%;
background-size: contain;
background-repeat: no-repeat;
margin: 4px;
position: relative;
overflow-wrap: break-word;
transition: all 200ms ease-in-out;
box-shadow: 0 0 5px rgba(255, 255, 255, 0);
}
.fileitem:hover {
background-color: rgba(255, 255, 255, 0.219);
/* border: 2px solid white; */
cursor: pointer;
}
.filename {
bottom: 10px;
position: absolute;
width: 100%;
max-width: 100%;
display: block;
text-align: center;
padding: 0.4em;
font-size: 0.8em;
color: rgba(255, 255, 255, 0.719);
background-color: rgba(0, 0, 0, 0.452);
}
.navicon {
/* width: 3em; */
margin: 0.5em;
box-shadow: 0 0 10px rgba(255, 166, 0, 0);
/* transition-property: box-shadow, scale; */
transition: transform 500ms ease-in-out;
}
.navicon::before {
font-family: "FontAwesomeSol900";
display: inline-block;
margin-right: 10px;
font-size: 1.2em;
-webkit-font-smoothing: antialiased;
}
.folder::before { content: "\f07b"; color: rgb(9, 119, 170);}
.file::before { content: "\f15b"; color: rgb(241, 241, 237);}
.back::before { content: "\f106"; }
.home::before { content: "\f102"; }
.selectedImage {
/* box-shadow: 0 0 5px white; */
border: 2px solid white;
border-radius: 10%;
transform: scale(0.95);
transition: all 200ms ease-in-out;
}
image-item {
border: 2px solid transparent;
}
.disable {
cursor: not-allowed;
opacity: 0.3;
pointer-events: none;
}
#navipath {
background: rgba(0, 0, 0, 0.2);
font-size: 0.8em;
padding: 0.5em 1.0em;
color: rgba(255, 228, 155, 0.4);
}
social-item {
display: block;
border-radius: 0.5em;
background-color: rgba(255,255,255,0.1);
width: 100%;
padding: 0px;
margin-bottom: 10px;
overflow: hidden;
border: 2px solid transparent;
box-shadow: 0 0 5px 2px rgba(0,0,0,0.7);
}
social-item:hover {
/* box-shadow: 0 0 2px 2px rgba(250, 250, 250, 0.7); */
cursor: pointer;
/* border: 2px solid rgb(109, 108, 108); */
background-color: rgba(0, 0, 0, 0.089);
transition: background-color 0.3s;
}
.dragIcon {
border: 2px solid yellow;
mask-image: unset;
}
.social-item-content-table {
border-spacing: 0px;
border-collapse: separate;
width: 100%;
padding: 0px;
margin: 0px;
}
.social-item-content-table td {
padding: 0;
border-collapse: separate;
border-spacing: 0;
}
.serviceicon {
background-color: rgba(16, 104, 35, 0.575); /* default bg color */
background-position: 50% 3px;
background-repeat: no-repeat;
width: 40px;
padding: 0px;
margin: 0;
}
.facebook_post { background-color: rgb(17, 91, 209); }
.instagram { background-color: rgb(106, 21, 218); }
.tweet { background-color: rgb(15, 35, 151); }
.rss { background-color: rgb(167, 81, 32); }
.video { background-color: rgb(151, 15, 15); }
.brd {
border: 2px solid orange;
}
.uIcon {
margin-left: 0.2em;
width: 2em;
height: 2em;
border-radius: 25%;
}
.uName {
font-size: 0.9em;
font-weight: bold;
}
.uHndl {
font-size: 0.9em;
}
.uuid {
display: none;
}
.postTimeTD {
text-align: center;
font-size: 0.9em;
color: rgba(255,255,255,0.5)
}
.userIconCell {
width: 2vw;
}
.thumb {
width: 10vw !important;
margin: 0.4vw;
}
.thumbTD {
width: 10vw !important;
margin: 0.4vw;
/* background-color: saddlebrown; */
}
.postmessageTD {
vertical-align: top;
font-size: 1.0em;
padding: 0.9em !important;
}
.pad {
padding: 0.3em !important;
}
.topbrd {
border-top: 1px solid rgba(0,0,0,0.5);
}
.focused {
border: 2px solid rgb(255, 145, 0);
box-shadow: 0 0 5px 2px rgba(230, 156, 20, 0.7);
}
+103
View File
@@ -0,0 +1,103 @@
html, body {
box-sizing: border-box;
height: 100%;
margin: 0;
overflow: hidden;
background-color: #414C5A;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
font-size: 1.5vw;
}
*, *:before, *:after {
box-sizing: inherit;
margin: 0;
padding: 0;
}
.wrapper {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.header{
height: 100px;
}
.footer {
height: 60px;
}
.content {
flex: 1;
overflow: auto;
}
.left {
width: 60%;
height: 100%;
float: left;
overflow: auto;
}
.right {
margin-left: 60%;
height: 100%;
overflow: auto;
}
.pM
{
padding: 10px;
}
.item
{
width: 100%;
height: 100px;
margin-bottom: 10px;
background-color: black;
}
.itemlist
{
background-color: #2A3641;
}
td
{
vertical-align: middle;
}
/* .statusbar
{
font-family: Arial, Helvetica, sans-serif;
font-size: 70%;
padding: 3px 12px;
color: #aaaaaa;
} */
.scroller::-webkit-scrollbar-track
{
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.0);
border-radius: 0px;
background-color: #414C5A;
}
.scroller::-webkit-scrollbar
{
width: 14px;
background-color: #414C5A;
}
.scroller::-webkit-scrollbar-thumb
{
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.0);
background-color: #677180;
}
+24
View File
@@ -0,0 +1,24 @@
/* @font-face {
font-family: "Yle-Black";
src: url("/fonts/Yle-Black.ttf");
}
@font-face {
font-family: "Yle-Medium";
src: url("/fonts/Yle-Medium.ttf");
}
@font-face {
font-family: "Yle-Regular";
src: url("/fonts/Yle-Regular.ttf");
} */
@font-face { font-family: "UI-Light"; src: url("/fonts/EncodeSansSemiExpanded-Light.ttf"); }
@font-face { font-family: "UI-Regular"; src: url("/fonts/EncodeSansSemiExpanded-Regular.ttf"); }
@font-face { font-family: "UI-Bold"; src: url("/fonts/EncodeSansSemiExpanded-Bold.ttf"); }
@font-face { font-family: "UI-Black"; src: url("/fonts/EncodeSansSemiExpanded-Black.ttf"); }
@font-face { font-family: "FontAwesomeReg400"; src: url("/fonts/fa-regular-400.ttf"); }
@font-face { font-family: "FontAwesomeSol900"; src: url("/fonts/fa-solid-900.ttf"); }
+465
View File
@@ -0,0 +1,465 @@
/*
Copyright 2020 SPX Graphics
CSS file for form items. Loaded after spx_layout.css
*/
::placeholder {
color: #555;
opacity: 1;
}
.cfg::placeholder {
color: rgba(255, 255, 255, 0.6);
/* font-family: Arial, Helvetica, sans-serif; */
opacity: 0.4;
}
.spxformtext {
font-family: Arial, Helvetica, sans-serif;
font-size: 1em;
}
.disabled_btn {
opacity: 0.2;
}
.cfg {
font-family: "UI-Light", Arial, Helvetica, sans-serif;
height: 2.4em;
font-size: 0.8em;
color: white;
border: 2px solid transparent;
margin-left: 0.1em;
margin-right: 0.1em;
text-shadow: rgb(0, 0, 0, 0.4) 0 0px 8px;
box-shadow: rgb(0, 0, 0, 0.4) 0 3px 8px;
padding-inline-start: 5px;
}
.addbtn {
font-size: 1.2em;
color: white;
border: 2px solid transparent;
text-transform: uppercase;
font-family: "UI-Bold", Arial, Helvetica, sans-serif;
text-shadow: rgb(0, 0, 0, 0.4) 0 0px 8px;
box-shadow: rgb(0, 0, 0, 0.4) 0 3px 8px;
padding-inline-start: 0.4em;
padding-inline-end: 0.4em;
margin-left: auto;
margin-right: auto;
cursor: pointer;
}
.addbtn:hover {
border: 2px solid rgba(255, 255, 255, 0.4);
}
.btn {
height: 2.4em;
font-size: 0.8em;
color: white;
border: 2px solid transparent;
text-transform: uppercase;
font-family: "UI-Bold", Arial, Helvetica, sans-serif;
text-shadow: rgb(0, 0, 0, 0.4) 0 0px 8px;
box-shadow: rgb(0, 0, 0, 0.4) 0 3px 8px;
user-select: none;
cursor: pointer;
padding: 0.4em 0;
margin-bottom: 0.1em;
white-space: nowrap;
overflow: hidden;
}
.btn:hover {
border-bottom: 2px solid rgba(255, 255, 255, 0.4);
}
.btn:focus{
outline: none;
}
.btnsmall {
font-size: 0.7em;
color: white;
border: 2px solid transparent;
margin-left: 1px;
margin-right: 1px;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
box-shadow: rgb(0, 0, 0, 0.4) 0 3px 8px;
text-transform: uppercase;
height: 150%;
padding: 0.3em 1em;
cursor: pointer;
}
.btnsmall:hover {
border-bottom: 2px solid rgba(255, 255, 255, 0.4);
}
.btnsmall:focus{
outline: none;
}
.colorbtnsmall {
position: absolute;
border: 1px solid rgba(0, 0, 0, 1);
margin: 4px;
height: 80%;
width: 50%;
cursor: pointer;
border-radius: 0.5em;
font-size: 1.0em;
color: rgba(200,200,200,0.8);
text-shadow: 1px 1px 1px black;
}
.colorbtnsmall:hover {
/* border: 2px solid rgba(255, 255, 255, 0.1); */
}
.colorbtnsmall:focus{
outline: none;
}
.drop {
height: 2.4em;
font-size: 0.8em;
color: white;
border: 2px solid transparent;
margin-left: 0.1em;
margin-right: 0.1em;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
text-shadow: rgb(0, 0, 0, 0.4) 0 0px 8px;
box-shadow: rgb(0, 0, 0, 0.4) 0 3px 8px;
}
.drop option:disabled {
color: rgba(255, 255, 255, 0.3);
}
.drop:hover {
border-bottom: 2px solid rgba(255, 255, 255, 0.4);
}
.floinput {
padding: 0.1em;
background-color: #706e83;
border: 0;
font-family: inherit;
font-size: 1em;
margin: 0px;
color: white;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
height: 100%;
box-sizing: border-box;
}
.gccheckcontainer {
display: block;
position: relative;
padding-left: 35px;
margin-top: 8px;
margin-bottom: 6px;
cursor: pointer;
user-select: none;
}
.gccheckboxdiv label{
font-size: 0.7em;
color: rgba(255, 255, 255, 0.7)
}
/* Hide the browser's default checkbox */
.gccheckcontainer input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
/* Create a custom checkbox */
.gccheckmark {
position: absolute;
top: 0;
left: 0;
height: 23px;
width: 23px;
background-color: rgb(163, 163, 163);
transform: none !important;
}
/* On mouse-over, add a grey background color */
.gccheckcontainer:hover input ~ .gccheckmark {
background-color: rgb(241, 236, 236);
}
/* When the checkbox is checked, add a blue background */
.gccheckcontainer input:checked ~ .gccheckmark {
background-color: #008000;
}
/* Create the checkmark/indicator (hidden when not checked) */
.gccheckmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.gccheckcontainer input:checked ~ .gccheckmark:after {
display: block;
}
/* Style the checkmark/indicator */
.gccheckcontainer .gccheckmark:after {
left: 9px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.gcinput{
width: 100%;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
font-size:1em;
margin: 1px;
margin-top: 5px; /*5px */
line-height: 132%;
padding-left: 0.2em;
user-select: auto;
}
.gcinput::placeholder {
color: rgb(156, 156, 156);
font-family: Arial, Helvetica, sans-serif;
font-style: italic;
font-size: 0.9em;
letter-spacing: 1px;
}
.gcinputFilter{
width: 100%;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
font-size:1em;
margin: 1px;
margin-top: 5px;
line-height: 132%;
padding-left: 0.2em;
user-select: auto;
}
.gcinputFilter:focus {
outline: none;
}
.gcinputFilter::placeholder {
color: rgba(255, 255, 255, 0.13);
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
font-size: 0.8em;
letter-spacing: 0px;
transform: translateY(-10%);
}
/* .select {
border-radius: 0.10em;
padding: 0.1em 0.2em;
font-size: 1.0rem;
line-height: 1.1;
background-color: rgb(230, 230, 230);
} */
.select {
/* -webkit-appearance: none;
-moz-appearance: none; */
/* background: transparent; */
/* background-image: url("data:image/svg+xml,<svg width='36' height='18' xmlns='http://www.w3.org/2000/svg'><path d='m0,6l12,12l12,-12l-24,0z'/><path fill='none' d='m0,0l24,0l0,24l-24,0l0,-24z'/></svg>");
background-repeat: no-repeat;
background-position-x: 98%;
background-position-y: 50%; */
/* border: 1px solid #cdf1ff; */
background-color: #ececec;
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.7);
border-radius: 0.2rem;
margin-right: 2rem;
padding: 0.2rem;
padding-right: 2rem;
border-right: 10px solid #2768dab9;
}
.tablineinput {
font-family: "UI-Black", Arial, Helvetica, sans-serif;
margin: 0px;
border: 0;
font-size: 1.2em; /* <-------------------------- HEADLINE SIZE IN UI*/
background-color: brown;
color: white;
display: inline-block;
padding: 0.1em;
padding-left: 0.4em;
padding-right: 0.2em;
}
.headlineinput {
font-family: "UI-Black", Arial, Helvetica, sans-serif;
margin: 0px;
background-color: white;
color: black;
padding-left: 0.2em;
border: 0;
font-size: 2.4em; /* <-------------------------- HEADLINE SIZE IN UI*/
}
.tickerinput {
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
padding-left: 0.2em;
background-color: #222;
margin: 0px;
border: 0;
color: white;
font-size: 1.35em; /* <-------------------------- HEADLINE SIZE IN UI*/
}
.imsinput {
flex: auto;
flex-basis: 290px;
padding: 0.1em;
background-color: #706e83;
border: 0;
font-family: inherit;
font-size: 0.9em; /* Changed 15.04.2020 - was 1.em */
margin: 0px;
color: white;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
height: 100%;
box-sizing: border-box;
height: 1.83em; /* Added 15.04.2020 */
}
.inlineInput {
padding: 0.1em;
background-color: inherit;
border: 0;
font-family: inherit;
font-size: 1em;
margin: 0px;
color: white;
font-family: "UI-Regular", Arial, Helvetica, sans-serif;
height: 100%;
/* width: 100%; */
box-sizing: border-box;
}
.spxRoundedButton {
margin: 10px;
font-size: 60%;
padding: 0 2em;
border-radius: 1em;
}
/* Ripple effect */
.ripple {
background-position: center;
transition: background 0.2s;
}
.ripple:hover {
/* background: #47a7f5 radial-gradient(circle, transparent 1%, #47a7f5 1%) center/15000%; */
}
.ripple:active {
background-color: #ffffff;
background-size: 100%;
transition: background 0s;
}
.generalSettingDropdown:disabled {
color: rgba(255,255,255,0.6);
}
/* The radiocontainer */
.radiocontainer {
display: block;
position: relative;
padding-left: 1.8em;
margin-bottom: 0.5em;
cursor: pointer;
font-size: 1.2em;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* Hide the browser's default radio button */
.radiocontainer input {
position: absolute;
opacity: 0;
cursor: pointer;
}
/* Create a custom radio button */
.checkmark {
position: absolute;
top: -0.2em;
left: 0;
height: 1.3em;
width: 1.3em;
background-color: #eee;
border-radius: 50%;
}
/* On mouse-over, add a grey background color */
.radiocontainer:hover input ~ .checkmark {
background-color: rgb(146, 179, 155);
}
/* When the radio button is checked, add a blue background */
.radiocontainer input:checked ~ .checkmark {
background-color: #54af3c;
}
/* Create the indicator (the dot/circle - hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the indicator (dot/circle) when checked */
.radiocontainer input:checked ~ .checkmark:after {
display: block;
}
/* Style the indicator (dot/circle) */
.radiocontainer .checkmark:after {
top: 0.4em;
left: 0.4em;
width: 0.5em;
height: 0.5em;
border-radius: 50%;
background: rgb(255, 255, 255);
}
.toggleON {
/* used for buttons to mimic toggle functionality */
transform: translateY(1px);
text-shadow:0px 0px 10px rgba(128, 255, 0, 0.8);
box-shadow: inset 0 4px 8px rgba(0, 0, 0, 0.5),
inset 0 -4px 10px rgba(0, 0, 0, 0.3);
}
.selectAll {
font-size: 2em;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+461
View File
@@ -0,0 +1,461 @@
/*
Copyright 2020 SPX Graphics
*/
/* TABS in import template dialog */
/* Style the tab */
:root {
--tabButtonBorderColor: #afb5ba;
--backgroundColor: #2d333b;
}
.promoBoxCols {
display: flex;
flex-direction: row;
gap: 1.5em;
}
.promoLeft {
flex-basis: 60%;
position: relative;
font-family: 'UI-Light' !important;
font-size: 0.7em;
}
.promoBoxHead {
font-size: 1.5em;
margin-bottom: 0.7em;
}
.promoLeft p {
margin-top: 0.5em;
opacity: 0.7;
}
.promoLeft a {
color: orange;
text-decoration: none;
}
.promoLeft a:hover {
color: white;
}
.promoLeft h2 {
font-size: 1.4em;
margin-top: 2.0em;
margin-bottom: 0.5em;
text-transform: none;
color: white;
width: 100%;
font-weight: normal;
letter-spacing: normal;
border-bottom: 1px solid rgba(255, 255, 255, 0.242);
}
.promoLeft li {
list-style: square;
color: inherit;
opacity: inherit;
}
.promoRight {
font-size: 0.8em;
flex-basis: 40%;
position: relative;
}
.promoRight img {
width: 80%;
height: auto;
}
.tabButtonRow {
width: 100%;
display: flex;
gap: 0.6em;
font-size: 0.8em;
padding: 1.5em 0.3em 0 0.3em;
flex-shrink: 0;
border-bottom: 1px solid var(--tabButtonBorderColor);
}
.tabButtonRow > div {
flex-grow: 1;
display: flex;
justify-content: flex-end;
align-items: center;
}
/* Style the buttons that are used to open the tab content */
.tabButtonRow button {
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 0.4em 1.0em;
transition: 0.1s;
font-family: inherit;
border-radius: 0.3em 0.3em 0 0;
position: relative;
transform: translateY(1px);
z-index: 10;
margin-right: 0.4em;
color: rgba(255, 255, 255, 0.385);
border: 0px solid var(--tabButtonBorderColor);
border-width: 1px 1px 0px 1px;
}
/* Change background color of buttons on hover */
.tabButtonRow button:hover {
background-color: rgba(194, 194, 194, 0.426);
color: black;
}
/* Create an active/current tablink class */
.tabButtonRow button.active {
background-color: #ccc;
}
.tabSelected {
background-color: #b1b6bc !important;
color: rgb(32, 32, 32) !important;
border-radius: 0.3em 0.3em 0 0;
}
/* Style the tab content */
.tabcontent {
display: none;
padding: 0.5em;
border: 0px solid #ccc;
border-top: none;
}
#tab1,
#tab2,
#tab3,
#tab4 {
padding: 0.5em;
display: flex;
flex-direction: column;
border: 0px solid blue;
height: 100%;
overflow: auto;
/* background-color: var(--backgroundColor); */
}
#tab2,
#tab3,
#tab4 {
padding: 1.5em;
}
.dialogHeaderBar {
display: flex;
flex-direction: row;
align-items: center;
position: relative;
padding: 0.2em 0.5em;
font-size: 0.8em;
background-color: rgba(255,255,255,0.2);
color: white;
border-bottom: 1px solid #3c3c3c;
}
.dialogHeaderBarClose {
display: flex;
flex-grow: 1;
justify-content: flex-end;
}
.dialogCloseButton {
cursor: pointer;
font-size: 1.5em;
}
.dialogCloseButton:hover {
color: rgba(0, 0, 0, 0.5);
}
.addTemplateItem{
background-color: rgba(255,255,255,0.05);
width:100%;
padding:10px;
margin-bottom:15px;
box-shadow: rgba(0, 0, 0, 0.7) 0px 0px 10px;
}
.addTemplateItem:hover{
background-color: rgba(255,255,255,0.2);
cursor: pointer;
}
.templateName{
text-transform: uppercase;
}
.templateDesc{
font-size: 75%;
opacity: 0.6;
}
.collapsible {
box-sizing: border-box;
color: white;
cursor: pointer;
padding: 12px;
width: 100%;
border-top: 0px;
border-right: 0px;
border-bottom: 1px solid black;
text-align: left;
outline: none;
font-size: 1rem;
text-transform: uppercase;
}
.collapsible::before{
content: "\25B6";
color: white;
display: inline-block;
margin-right: 12px;
transform: rotate(0deg) scale(0.7);
transition: transform 0.3s ease-in-out;
}
.globalCollapsible
{
font-size: 0.7rem;
}
.active, .collapsible:hover {
background-color: #375ea3;
}
.active::before{
content: "\25B6";
color: white;
display: inline-block;
margin-right: 12px;
transform: rotate(90deg) scale(1.0);
transition: transform 0.3s ease-in-out;
}
.collapsiblecontent {
box-sizing: border-box;
padding: 0px 10px;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
font-size: 1rem;
background-color: rgba(0,0,0,0.1);
}
.configGeneralContent {
box-sizing: border-box;
padding: 0px 10px;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
font-size: 1rem;
background-color: rgba(0,0,0,0.1);
}
/* Remove default bullets */
ul, #myUL {
list-style-type: none;
}
/* Remove margins and padding from the parent ul */
#myUL {
margin: 0;
padding: 0;
}
/* Style the caret/arrow */
.caret {
cursor: pointer;
user-select: none; /* Prevent text selection */
}
/* Create the caret/arrow with a unicode, and style it */
.caret::before {
content: "\25B6";
color: black;
display: inline-block;
margin-right: 6px;
}
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
.caret-down::before {
transform: rotate(90deg);
}
/* Hide the nested list */
.nested {
display: none;
}
/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
.opened {
display: block;
}
.overlay {
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(65, 76, 90, 0.9);
z-index: 900;
}
.modal {
width: 60%;
height: 90%;
position: absolute;
left: 55%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 20;
padding: 0.3em;
background-color: #414C5A;
color: white;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 20px;
}
.icon::before {
font-family: "FontAwesomeSol900";
display: inline-block;
margin-right: 10px;
font-size: 1.2em;
-webkit-font-smoothing: antialiased;
}
.folder::before { content: "\f07b"; color: rgb(9, 119, 170);}
.file::before { content: "\f15b"; color: rgb(241, 241, 237);}
.back::before { content: "\f106"; }
.home::before { content: "\f102"; }
.height1{
height: 1em;
}
.height2{
height: 2em;
}
.height3{
height: 3em;
}
.height4{
height: 4em;
}
.modal_top{
flex-shrink: 0;
border: 0px solid pink;
border-bottom: 1px dashed rgba(51, 150, 216, 0.445);
}
.modal_top SPAN{
transform: translateY(-50%);
font-size: 0.8em;
margin-left: 1em;
}
.modal_bottom{
gap: 0.5em;
width: 100%;
flex-grow: 0;
display: flex;
height: 2.5em;
flex-shrink: 0;
padding-top: 0.3em;
flex-direction: row;
align-items: center;
justify-content: flex-start;
border: 0px solid pink;
}
.browserMessage {
top: 0;
position: relative;
font-size: 0.6em;
color: rgba(255, 255, 255, 0.5);
margin-left: 0.1em;
user-select: none;
display: flex;
align-items: center;
justify-content: flex-start;
border: 0px dotted pink;
height: 100%;
}
.modal_bottom > div > button {
border: 0px dotted white;
padding: 0.3em 1em;
margin-right: 0.2em;
}
.KBHelptable{
border-collapse:collapse;
}
.KBHelptable td{
padding:10px;
border:#4e95f4 0px solid;
}
.KBHelptable th{
background: rgba(0,0,0,0.5);
text-align: left;
padding: 7px;
}
.KBHelptable tr:nth-child(odd){
background: rgba(0,0,0,0.1);
}
/* Define the background color for all the EVEN background rows */
.KBHelptable tr:nth-child(even){
background: rgba(255,255,255,0.1);
}
.storeIcon {
height: 100%;
opacity: 0.6;
min-width: 8%;
background-size: contain;
border: 0px solid white;
background-position: 5% 50%;
background-repeat: no-repeat;
transition: opacity 0.3s ease-in-out;
background-image: url('/img/storeicon.png');
}
.storeIcon:hover {
opacity: 1;
cursor: pointer;
transition: opacity 0.3s ease-in-out;
}
+67
View File
@@ -0,0 +1,67 @@
html,
body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
display: flex;
color: white;
overflow: hidden;
align-items: center;
flex-direction: column;
justify-content: center;
font-family: 'UI-Regular';
background-color: rgb(0,0,0);
}
*, *:before, *:after {
box-sizing: inherit;
margin: 0;
padding: 0;
user-select: none;
}
.renderer {
background-image: url('/img/checker60.png'); /* bgimage for local preview */
background-size: 10vw;
transform-origin: top left;
border: 0px;
outline: 0px;
padding: 0;
margin: 0;
}
#toolbar {
flex-grow: 0;
width: 100%;
display: flex;
align-items: left;
flex-direction: row;
align-self: flex-start;
border: 1px solid yellow;
}
.watermark {
top: 0;
left: 0;
top: 50%;
left: 50%;
opacity: 1; /* WATERMARK if 1 = visible, 0 = invisible */
width: 50vw;
z-index:100;
height: 50vw;
position: absolute;
background-size: contain;
border: 0px solid yellow;
background-position: center;
background-repeat: no-repeat;
transform: translate(-50%, -50%);
}
.prev {
background-image: url("/img/spx_preview.png");
}
.prog {
background-image: url("/img/spx_program.png");
}
+84
View File
@@ -0,0 +1,84 @@
:root {
/* Define a variable for the margin of the overlay, ~70px at 1080p */
--margin: 3.65vmax;
}
.main {
position: absolute;
top: calc(12 * var(--margin));
left: var(--margin);
width: calc(100vw - 2 * var(--margin) - 12vw);
opacity: 0;
}
.f1_bg {
background: var(--main-color);
display: table;
position: relative;
padding: 0.2em 0.4em;
}
.f1_text {
color: var(--white-color);
font-family: 'UI-Black';
font-size: 0.8rem;
line-height: 1;
width: 100%;
}
.f2_bg {
background: var(--white-color);
display: table;
position: relative;
padding: 0.1em 0.4em 0em;
width: 100%;
}
.f2_text {
color: var(--black-color);
font-family: 'UI-Black';
font-size: 1.45rem;
line-height: 1.25;
overflow: hidden;
white-space: nowrap;
letter-spacing: -0.05rem;
}
.f3_bg {
z-index: 2;
background: #252525;
float: left;
position: relative;
padding: 0.1em 0.4em;
}
.f3_text {
color: var(--white-color);
font-family: 'UI-Regular';
font-size: 0.9rem;
line-height: 1.25;
}
.ticker_bg {
z-index: -1;
background: var(--dark-color);
width: 100%;
padding-left: 0.5em;
padding-right: 0.5em;
}
.ticker {
z-index: 1;
}
.webTicker {
color: var(--white-color);
float: left;
position: relative;
}
.webTicker li {
font-size: 1rem;
line-height: 1.2;
font-family: 'UI-Regular';
}
+152
View File
@@ -0,0 +1,152 @@
/*
Rec toggle switch and
volume sliders
*/
:root {
--vc-off-color: #2A3641; /* linear-gradient(190deg, rgba(0, 0, 0, 0.39) 5%, rgba(0, 0, 0, 0.157) 50%); */
--vc-on-color: #2A3641;
--vc-animation-speed: 0.2s ease-out;
--vc-font-family: "UI-Regular";
--vc-font-size: 1.2em;
--vc-font-weight: unset;
--vc-on-font-color: rgba(255, 255, 255, 1.0);
--vc-off-font-color: rgb(255, 255, 255);
--vc-label-position-off: 14px;
--vc-label-position-on: 14px;
--vc-height: 1.3em; /* 2.0em*/
--vc-width: 100%;
--vc-handle-width: 60%;
--vc-handle-height: 90%;
--vc-box-border-radius: 0px;
--vc-handle-border-radius: 0px;
--vc-handle-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5);
--vc-handle-color: #485a6b; /* rgb(61, 61, 61); */
--vc-onclick-width: 80%;
--vc-handle-top: 0.1em; /* 0.2 */
--vc-handle-fontsize: 0.7em; /* 0.8 */
--vc-handle-lineHeight: 1.7em; /* 2.2 */
}
.toggle-container * {
font-family: var(--vc-font-family);
transition: var(--vc-animation-speed);
cursor: pointer;
/* background: darkgreen; */
}
.vc-switch {
width: var(--vc-width);
height: var(--vc-height);
margin-top: 5px;
}
.toggle-container label {
position: relative;
display: inline-block;
vertical-align: top;
border-radius: var(--vc-box-border-radius);
/* cursor: pointer; */
}
.vc-switch-input {
position: absolute;
transform: translate3d(5px, 5px, 0);
opacity: 0;
}
.vc-switch-label {
position: relative;
display: block;
height: inherit;
font-size: var(--vc-font-size);
font-weight: var(--vc-font-weight);
background: var(--vc-off-color);
border-radius: inherit;
}
.vc-switch-label:before,
.vc-switch-label:after {
position: absolute;
top: 50%;
margin-top: -0.5em;
line-height: 1.1;
}
.vc-switch-label:before {
content: attr(data-off);
color: var(--vc-on-font-color);
}
.vc-switch-label:after {
content: attr(data-on);
color: var(--vc-off-font-color);
opacity: 0;
}
.vc-switch-label:before {
right: var(--vc-label-position-off);
top: 16px;
}
.vc-switch-label:after {
left: var(--vc-label-position-on);;
}
.vc-switch-input:checked ~ .vc-switch-label {
background: var(--vc-on-color);
text-shadow: 0px 0px 6px rgba(255, 255, 255, 0.6);
}
.vc-switch-input:checked ~ .vc-switch-label:before {
opacity: 0;
}
.vc-switch-input:checked ~ .vc-switch-label:after {
opacity: 1;
}
.vc-handle {
left: 2px;
font-size: var(--vc-handle-fontsize);
position: absolute !important;
top: var(--vc-handle-top);
background: var(--vc-handle-color);
border-radius: var(--vc-handle-border-radius);
box-shadow: var(--vc-handle-shadow);
width: var(--vc-handle-width);
height: var(--vc-handle-height);
line-height: var(--vc-handle-lineHeight);
}
.vc-handle:before {
content: "";
top: 50%;
left: 50%;
position: absolute !important;
margin: -6px 0 0 -6px;
width: 12px;
height: 12px;
border-radius: 6px;
}
.vc-switch-label:active ~ .vc-handle, .vc-handle:active {
width: var(--vc-onclick-width);
}
.vc-switch-input:checked ~ .vc-handle {
left: unset;
right: 2px;
color: var(--vc-on-font-color);
text-shadow: 0 0 6px var(--vc-on-font-color),
0 4px 8px green,
0 0px 12px rgb(20, 209, 20);
}
+123
View File
@@ -0,0 +1,123 @@
.can-toggle {
position: relative;
border: 1px solid yellow;
padding: 0;
}
.can-toggle *, .can-toggle *:before, .can-toggle *:after {
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.can-toggle input[type="checkbox"] {
top: 0;
left: 0;
opacity: 0;
position: absolute;
}
.can-toggle input[type="checkbox"][disabled] ~ label {
pointer-events: none;
}
.can-toggle input[type="checkbox"][disabled] ~ label .can-toggle__switch {
opacity: 0.4;
}
.can-toggle input[type="checkbox"]:checked ~ label .can-toggle__switch:before {
left: 0;
content: attr(data-unchecked);
}
.can-toggle input[type="checkbox"]:checked ~ label .can-toggle__switch:after {
content: attr(data-checked);
}
.can-toggle label {
display: flex;
user-select: none;
position: relative;
align-items: center;
}
.can-toggle label .can-toggle__label-text {
/* not visible */
flex: 1;
padding-left: 32px;
}
.can-toggle label .can-toggle__switch {
position: relative;
}
.can-toggle label .can-toggle__switch:before {
top: 0px;
text-align: center;
position: absolute;
content: attr(data-checked);
text-transform: uppercase;
background-color: blue;
}
.can-toggle label .can-toggle__switch:after {
z-index: 5;
text-align: center;
position: absolute;
background: #485a6b;
text-transform: uppercase;
content: attr(data-unchecked);
transform: translate3d(0, 0, 0);
}
.can-toggle label .can-toggle__label-text {
flex: 1;
}
.can-toggle label .can-toggle__switch {
transition: background-color 0.3s cubic-bezier(0, 1, 0.5, 1);
background: rgba(0, 0, 0, 0.5); /* bg color */
}
.can-toggle label .can-toggle__switch:before {
color: rgb(160, 160, 160); /* text color on the back */
}
.can-toggle label .can-toggle__switch:after {
transition: transform 0.3s cubic-bezier(0, 1, 0.5, 1);
color: rgb(255, 255, 255); /* text color on the sliding button */
}
.can-toggle input[type="checkbox"]:focus ~ label .can-toggle__switch:after, .can-toggle input[type="checkbox"]:hover ~ label .can-toggle__switch:after {
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4);
}
.can-toggle input[type="checkbox"]:checked ~ label .can-toggle__switch:after {
transform: translate3d(80px, 0, 0); /*65px*/
/*margin-left: 80px; */
}
/*
.can-toggle input[type="checkbox"]:checked:focus ~ label .can-toggle__switch:after, .can-toggle input[type="checkbox"]:checked:hover ~ label .can-toggle__switch:after {
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4);
}
*/
.can-toggle label {
/* font-size: 14px; */
}
.can-toggle label .can-toggle__switch {
flex: 1 0;
/* background-color: hotpink; */
/*height: 4em; 36px; */
border-radius: 4px;
}
.can-toggle label .can-toggle__switch:before {
/* non-active one */
left: max(67px, 50%); /*67px*/
font-size: 0.7em; /*12px;*/
line-height: 1;
width: max(67px, 50%); /* 67px; */
/* background-color: red; */
padding: 0 0.2em; /*12px*/
}
.can-toggle label .can-toggle__switch:after {
/* active one */
top: 0px;
left: 0px;
border-radius: 2px;
width: max(67px, 50%); /* 67px; */
line-height: 1;
font-size: 0.7em; /*12px;*/
/* background-color: greenyellow; */
}
.can-toggle label .can-toggle__switch:hover:after {
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4);
}
+111
View File
@@ -0,0 +1,111 @@
/* simple tree */
body{
font-family: Arial, Helvetica, sans-serif;
}
.simple-tree {
user-select: none;
-moz-user-select: none;
}
.simple-tree>details>summary {
display: none;
}
.simple-tree a,
.simple-tree summary {
display: block;
width: fit-content;
width: -moz-fit-content;
border: solid 1px transparent;
padding: 0 2px;
outline: none;
cursor: pointer;
}
.simple-tree a {
text-decoration: none;
color: inherit;
}
.simple-tree ::-webkit-details-marker {
display: none;
}
.simple-tree summary {
list-style-type: none;
background-color: #eee;
outline: none;
}
.simple-tree.dark summary {
background-color: #444;
}
.simple-tree details>:not(details),
.simple-tree details {
position: relative;
}
.simple-tree details :not(summary) {
margin-left: 20px;
}
.simple-tree.nodots details :not(summary) {
margin-left: 12px;
}
.simple-tree details::before,
.simple-tree details>:not(details)::before {
content: '';
width: 10px;
display: block;
position: absolute;
}
.simple-tree details::before,
.simple-tree details>:not(details)::before {
background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 2 2" xmlns="http://www.w3.org/2000/svg"><g><rect x="0" y="0" width="1" height="1"/></g></svg>') left top / 2px 2px;
}
.simple-tree.dark details::before,
.simple-tree.dark details>:not(summary)::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 2 2" xmlns="http://www.w3.org/2000/svg"><g><rect x="0" y="0" width="1" height="1" fill="white"/></g></svg>');
}
.simple-tree.nodots details::before,
.simple-tree.nodots details>:not(summary)::before {
background-image: none;
}
.simple-tree details::before {
top: 0;
height: 100%;
background-repeat: repeat-y;
left: 5px;
z-index: -1;
}
.simple-tree details>:not(details)::before {
top: 8px;
height: calc(100% - 8px);
background-repeat: repeat-x;
left: -15px;
}
.simple-tree details>summary::before {
background: url('data:image/svg+xml;utf8,<svg viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><g><rect x="0" y="0" width="12" height="12" fill="white" stroke="gray" stroke-width="1"/><line x1="3" y1="6" x2="9" y2="6" stroke="black" stroke-width="2"/><line x1="6" y1="3" x2="6" y2="9" stroke="black" stroke-width="2"/></g></svg>') center center / 12px 12px no-repeat;
left: -22px;
top: 2px;
width: 16px;
height: 16px;
}
.simple-tree details[open]>summary::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><title/><g><rect x="0" y="0" width="12" height="12" fill="white" stroke="gray" stroke-width="1"/><line x1="3" y1="6" x2="9" y2="6" stroke="black" stroke-width="2"/></g></svg>');
}
/* async tree */
.async-tree details[open][data-loaded=false] {
pointer-events: none;
}
.async-tree details[open][data-loaded=false]>summary::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><g><animateTransform attributeName="transform" type="rotate" from="0 32 32" to="360 32 32" dur="1s" repeatCount="indefinite"/><circle cx="32" cy="32" r="32" fill="whitesmoke"/><path d="M 62 32 A 30 30 0 0 0 32 2" style="stroke: black; stroke-width:6; fill:none;"/></g></svg>');
}
.async-tree.black details[open][data-loaded=false]>summary::before {
background-image: url('data:image/svg+xml;utf8,<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"><g><animateTransform attributeName="transform" type="rotate" from="0 32 32" to="360 32 32" dur="1s" repeatCount="indefinite"/><circle cx="32" cy="32" r="32" fill="whitesmoke"/><path d="M 62 32 A 30 30 0 0 0 32 2" style="stroke: white; stroke-width:6; fill:none;"/></g></svg>');
}
/* select tree */
.select-tree .selected {
background-color: #beebff;
border-color: #99defd;
z-index: 1;
}
.select-tree.dark .selected {
background-color: #3a484e;
border-color: #99defd;
}
+28
View File
@@ -0,0 +1,28 @@
:root {
--tooltipbg: rgba(0, 0, 0, 0.7);
}
.b-tooltip {
font-size: 0.6em;
padding: 0.3em;
border-radius: 0.2em;
color: rgba(255, 255, 255, 0.6);
text-align: center;
position: absolute;
background: var(--tooltipbg);
display: inline-block;
opacity: 1;
}
.b-tooltip:after {
content: " ";
position: absolute;
left: -10px;
top: 4px;
border-top: 10px solid transparent;
border-right: 10px solid var(--tooltipbg);
border-left: none;
border-bottom: 10px solid transparent;
}
+1
View File
@@ -0,0 +1 @@
.picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:flex;flex-flow:row wrap;justify-content:space-between;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:"";display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:"";display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px #1e90ff}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:#f5f5f5;background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:#fff}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid #fff;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%),linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%),linear-gradient(90deg, #808080, rgba(128, 128, 128, 0))}.picker_alpha,.picker_sample{position:relative;background:linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em,linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:"";position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:"";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+33
View File
@@ -0,0 +1,33 @@
<svg width="750" height="155" viewBox="0 0 750 155" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_404_131)">
<mask id="mask0_404_131" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="262" height="155">
<path d="M261.377 0.62207H0V154.105H261.377V0.62207Z" fill="white"/>
</mask>
<g mask="url(#mask0_404_131)">
<path d="M165.065 151.488C158.595 149.805 152.377 147.268 146.573 143.944C140.8 140.566 135.5 136.435 130.811 131.659C126.452 127.288 121.916 119.178 117.338 107.557C113.945 98.7745 111.026 89.8154 108.592 80.7186L152.928 78.8374C153.728 93.553 164.953 105.967 179.619 108.356L172.934 147.88L165.065 151.488L165.065 151.488Z" fill="#FBA554"/>
<path d="M184.841 154.226C178.158 154.195 171.508 153.274 165.065 151.489L176.753 107.754C179.396 108.435 182.114 108.779 184.843 108.777C189.357 108.781 193.822 107.831 197.946 105.99C202.07 104.149 205.761 101.459 208.78 98.0928L238.648 123.808L238.872 131.66C234.183 136.435 228.882 140.567 223.11 143.945C217.306 147.269 211.088 149.806 204.618 151.49C198.175 153.275 191.525 154.195 184.841 154.226Z" fill="#FAC353"/>
<path d="M238.87 131.659L207.117 99.8186C210.186 96.8918 212.629 93.3681 214.294 89.4628C215.96 85.5575 216.814 81.3527 216.803 77.1053C216.807 73.5848 216.217 70.089 215.06 66.7653L252.143 53.6099L258.645 57.6537C260.426 64.1135 261.344 70.7817 261.375 77.4836C261.344 84.1855 260.426 90.8536 258.645 97.3134C256.966 103.8 254.437 110.035 251.122 115.855C247.753 121.643 243.633 126.958 238.87 131.659Z" fill="#FEFE67"/>
<path d="M215.791 69.1685C214.233 63.3213 211.051 58.0376 206.617 53.9318C202.182 49.826 196.676 47.0667 190.739 45.9746L198.004 7.15284L204.618 3.47803C211.088 5.1621 217.305 7.69928 223.109 11.0232C228.882 14.4014 234.182 18.5325 238.871 23.3084C243.634 28.0103 247.754 33.3249 251.123 39.1133C254.438 44.9329 256.969 51.1678 258.648 57.6547L215.791 69.1685Z" fill="#D3EA5C"/>
<path d="M160.376 56.7315L130.665 31.531L130.813 23.3076C135.502 18.5317 140.802 14.4006 146.575 11.0224C152.379 7.69867 158.597 5.16166 165.066 3.47774C171.508 1.69273 178.158 0.772529 184.842 0.741211C191.526 0.772529 198.176 1.69273 204.618 3.47774L193.119 46.5087C190.418 45.7951 187.636 45.4346 184.843 45.4363C180.191 45.4269 175.594 46.4321 171.369 48.3824C167.144 50.3327 163.393 53.1812 160.376 56.7305V56.7315Z" fill="#71AE50"/>
<path d="M142.932 101.8L106.376 88.4171C107.768 84.81 108.48 80.9748 108.474 77.107C108.481 72.9502 107.662 68.8336 106.064 64.9979C104.466 61.1622 102.122 57.6843 99.168 54.7674L130.542 23.3081C135.305 28.01 139.425 33.3247 142.794 39.113C146.11 44.9329 148.641 51.1681 150.32 57.6555C152.1 64.1154 153.018 70.7835 153.049 77.4853C153.018 84.1872 152.1 90.8554 150.32 97.3152L142.934 101.801L142.932 101.8Z" fill="#FBA554"/>
<path d="M150.235 97.5691L107.195 86.0051C107.986 83.3192 108.414 80.5392 108.467 77.7393H152.464C153.352 78.6868 153.88 83.9295 150.235 97.5691Z" fill="#0391CE"/>
<path d="M22.4825 131.659C17.7194 126.958 13.5994 121.644 10.2305 115.857C6.91557 110.036 4.3854 103.801 2.7062 97.3136C0.925998 90.8538 0.00827844 84.1857 -0.0229492 77.4838C0.00823331 70.7817 0.925953 64.1135 2.7062 57.6534L11.201 51.9326L46.6492 65.8006C44.4243 71.5887 43.9577 77.9077 45.3082 83.9613C46.6587 90.0149 49.7659 95.5323 54.2383 99.8183L22.4825 131.659Z" fill="#8A38AF"/>
<path d="M45.5647 69.1695L2.70703 57.6542C6.07752 44.6137 12.8644 32.7141 22.3643 23.1885C31.8643 13.6628 43.7317 6.85764 56.737 3.47803L64.6559 8.37624L71.0918 45.8906C65.0554 46.8987 59.4368 49.6308 54.9096 53.7594C50.3824 57.8881 47.1385 63.2382 45.5667 69.1685L45.5647 69.1695Z" fill="#A7194B"/>
<path d="M100.856 56.5825C97.8374 53.0775 94.0999 50.2671 89.8987 48.3434C85.6975 46.4197 81.1317 45.4281 76.5132 45.4363C73.7194 45.4355 70.9372 45.7968 68.2359 46.5112L56.7368 3.47774C63.1792 1.6927 69.8294 0.772499 76.5132 0.741211C83.1968 0.772556 89.8467 1.69276 96.289 3.47774C102.759 5.16179 108.977 7.69897 114.781 11.0229C120.554 14.4011 125.854 18.5322 130.543 23.3081L130.733 30.9305L100.856 56.5825Z" fill="#FE5D58"/>
<path d="M111.036 97.3141C109.256 90.8543 108.338 84.1862 108.307 77.4843C108.338 70.7828 109.256 64.115 111.036 57.6555C112.715 51.1687 115.246 44.934 118.561 39.1145C121.929 33.3257 126.049 28.0105 130.812 23.3081L162.187 54.7679C159.233 57.6848 156.889 61.1626 155.291 64.9982C153.694 68.8338 152.874 72.9503 152.881 77.107C152.881 77.708 152.898 78.3156 152.932 78.914L121.728 80.313L111.037 97.3126L111.036 97.3141Z" fill="#0391CE"/>
<path d="M96.2889 151.49L90.1118 142.296L82.9438 108.136C88.6603 107.009 93.9559 104.323 98.2479 100.373C102.54 96.4229 105.662 91.3621 107.27 85.7476L150.318 97.3161C148.639 103.803 146.109 110.038 142.794 115.858C139.425 121.646 135.305 126.961 130.542 131.662C125.853 136.438 120.552 140.569 114.78 143.948C108.976 147.272 102.758 149.809 96.2884 151.493L96.2889 151.49Z" fill="#556BFE"/>
<path d="M76.5139 154.225C69.8301 154.194 63.18 153.274 56.7376 151.489C50.2443 149.794 44.0065 147.235 38.19 143.881C32.3998 140.479 27.0813 136.327 22.3716 131.533L54.1891 99.77C60.1772 105.573 68.1864 108.805 76.5139 108.779C79.2424 108.78 81.9602 108.437 84.6028 107.755L96.2897 151.491C89.8474 153.275 83.1974 154.194 76.5139 154.225Z" fill="#4E34A4"/>
</g>
</g>
<path d="M670.438 79.8022C670.438 71.7657 672.045 64.6092 675.259 58.3331C678.551 52.0569 683.22 47.1966 689.266 43.7524C695.313 40.2316 702.431 38.4712 710.62 38.4712C717.662 38.4712 723.938 39.8106 729.449 42.4895C735.036 45.0918 739.552 48.804 742.996 53.6259C746.517 58.4479 748.852 64.0735 750 70.5028H723.135C721.91 67.8239 720.15 65.7573 717.853 64.3031C715.634 62.7723 713.07 62.0069 710.161 62.0069C705.875 62.0069 702.469 63.6142 699.943 66.8289C697.494 70.0435 696.269 74.368 696.269 79.8022C696.269 85.2365 697.494 89.561 699.943 92.7756C702.469 95.9902 705.875 97.5976 710.161 97.5976C713.07 97.5976 715.634 96.8704 717.853 95.4162C720.15 93.8854 721.91 91.7806 723.135 89.1017H750C748.852 95.531 746.517 101.157 742.996 105.979C739.552 110.801 735.036 114.551 729.449 117.23C723.938 119.832 717.662 121.133 710.62 121.133C702.431 121.133 695.313 119.411 689.266 115.967C683.22 112.446 678.551 107.548 675.259 101.271C672.045 94.9952 670.438 87.8388 670.438 79.8022Z" fill="white"/>
<path d="M661.223 39.2744V120.559H635.735V39.2744H661.223Z" fill="white"/>
<path d="M627.651 67.1729C627.651 72.301 626.465 76.9699 624.092 81.1795C621.72 85.3126 618.199 88.6038 613.53 91.053C608.938 93.5023 603.35 94.7269 596.768 94.7269H586.665V120.559H561.177V39.2744H596.768C606.718 39.2744 614.334 41.8002 619.615 46.8518C624.972 51.9033 627.651 58.677 627.651 67.1729ZM593.898 74.6354C599.179 74.6354 601.819 72.1479 601.819 67.1729C601.819 62.1978 599.179 59.7103 593.898 59.7103H586.665V74.6354H593.898Z" fill="white"/>
<path d="M510.307 121.363C502.654 121.363 495.612 119.564 489.183 115.967C482.83 112.369 477.778 107.394 474.028 101.042C470.278 94.6889 468.402 87.5325 468.402 79.5724C468.402 71.6124 470.278 64.456 474.028 58.1033C477.778 51.7505 482.83 46.8138 489.183 43.293C495.612 39.6956 502.654 37.897 510.307 37.897C517.961 37.897 524.965 39.6956 531.317 43.293C537.67 46.8138 542.683 51.7505 546.357 58.1033C550.108 64.456 551.983 71.6124 551.983 79.5724C551.983 87.5325 550.108 94.6889 546.357 101.042C542.683 107.394 537.632 112.369 531.203 115.967C524.85 119.564 517.885 121.363 510.307 121.363ZM510.307 97.5974C515.359 97.5974 519.262 95.9901 522.018 92.7754C524.773 89.4842 526.151 85.0832 526.151 79.5724C526.151 73.9851 524.773 69.5841 522.018 66.3695C519.262 63.0783 515.359 61.4327 510.307 61.4327C505.179 61.4327 501.238 63.0783 498.482 66.3695C495.727 69.5841 494.349 73.9851 494.349 79.5724C494.349 85.0832 495.727 89.4842 498.482 92.7754C501.238 95.9901 505.179 97.5974 510.307 97.5974Z" fill="white"/>
<path d="M419.828 121.363C412.174 121.363 405.132 119.564 398.703 115.967C392.35 112.369 387.299 107.394 383.548 101.042C379.798 94.6889 377.923 87.5325 377.923 79.5724C377.923 71.6124 379.798 64.456 383.548 58.1033C387.299 51.7505 392.35 46.8138 398.703 43.293C405.132 39.6956 412.174 37.897 419.828 37.897C427.482 37.897 434.485 39.6956 440.838 43.293C447.191 46.8138 452.204 51.7505 455.878 58.1033C459.628 64.456 461.503 71.6124 461.503 79.5724C461.503 87.5325 459.628 94.6889 455.878 101.042C452.204 107.394 447.152 112.369 440.723 115.967C434.37 119.564 427.405 121.363 419.828 121.363ZM419.828 97.5974C424.88 97.5974 428.783 95.9901 431.538 92.7754C434.294 89.4842 435.672 85.0832 435.672 79.5724C435.672 73.9851 434.294 69.5841 431.538 66.3695C428.783 63.0783 424.88 61.4327 419.828 61.4327C414.7 61.4327 410.758 63.0783 408.003 66.3695C405.247 69.5841 403.87 73.9851 403.87 79.5724C403.87 85.0832 405.247 89.4842 408.003 92.7754C410.758 95.9901 414.7 97.5974 419.828 97.5974Z" fill="white"/>
<path d="M347.63 101.271H372.199V120.559H322.143V39.2744H347.63V101.271Z" fill="white"/>
<defs>
<clipPath id="clip0_404_131">
<rect width="261.377" height="153.483" fill="white" transform="translate(0 0.62207)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long
+7
View File
@@ -0,0 +1,7 @@
<svg width="1818" height="611" viewBox="0 0 1818 611" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1541.68 141.816H1812.32V208.8H1541.68V141.816ZM1747.36 0.40625H1817.05V71.4496H1768.34C1755.26 70.9986 1743.98 72.3518 1734.51 75.5093C1725.04 78.6667 1717.82 84.5306 1712.86 93.101C1707.9 101.22 1705.41 112.948 1705.41 128.284V471.999H1617.46V117.459C1617.46 90.3945 1622.42 68.2921 1632.34 51.1515C1642.27 33.5598 1656.92 20.7044 1676.32 12.5851C1695.72 4.46588 1719.4 0.40625 1747.36 0.40625Z" fill="white"/>
<path d="M1273.97 479.442C1240.6 479.442 1210.15 472.225 1182.63 457.791C1155.57 442.906 1133.92 422.382 1117.68 396.22C1101.89 369.607 1094 338.934 1094 304.202C1094 268.568 1102.12 237.669 1118.36 211.507C1134.59 185.345 1156.47 165.047 1183.99 150.613C1211.95 135.728 1243.53 128.285 1278.71 128.285C1317.5 128.285 1348.63 136.179 1372.08 151.966C1395.99 167.303 1413.35 188.277 1424.18 214.89C1435.01 241.503 1440.42 271.274 1440.42 304.202C1440.42 324.049 1437.26 344.347 1430.95 365.096C1424.63 385.395 1414.93 404.339 1401.85 421.931C1388.77 439.072 1371.63 453.055 1350.43 463.881C1329.23 474.255 1303.74 479.442 1273.97 479.442ZM1302.39 411.782C1329 411.782 1352.01 407.271 1371.41 398.25C1390.8 389.229 1405.69 376.599 1416.06 360.36C1426.44 344.122 1431.62 325.402 1431.62 304.202C1431.62 281.198 1426.21 261.802 1415.38 246.014C1405.01 229.776 1390.12 217.597 1370.73 209.478C1351.78 200.907 1329 196.622 1302.39 196.622C1264.95 196.622 1235.86 206.546 1215.11 226.393C1194.36 245.789 1183.99 271.725 1183.99 304.202C1183.99 325.853 1188.95 344.798 1198.87 361.037C1208.79 376.824 1222.55 389.229 1240.14 398.25C1258.19 407.271 1278.94 411.782 1302.39 411.782ZM1431.62 135.728H1519.58V472H1437.71C1437.71 472 1437.04 467.715 1435.68 459.144C1434.78 450.123 1433.88 438.846 1432.98 425.314C1432.07 411.782 1431.62 398.476 1431.62 385.395V135.728Z" fill="white"/>
<path d="M879.266 135.728H967.224V472H879.266V135.728ZM1096.46 208.124C1071.2 208.124 1049.32 213.086 1030.83 223.01C1012.33 232.482 997.446 244.21 986.169 258.193C974.893 272.176 966.999 285.708 962.488 298.789L961.812 261.576C962.263 256.163 964.067 248.27 967.224 237.895C970.382 227.069 975.118 215.567 981.433 203.388C987.748 190.758 996.093 178.805 1006.47 167.528C1016.84 155.8 1029.47 146.328 1044.36 139.111C1059.24 131.894 1076.61 128.285 1096.46 128.285V208.124Z" fill="white"/>
<path d="M626.961 420.578C590.425 420.578 558.173 415.165 530.207 404.339C502.692 393.063 481.266 376.599 465.93 354.947C450.593 333.296 442.925 307.134 442.925 276.461C442.925 246.24 450.368 220.078 465.253 197.975C480.139 175.873 501.339 158.732 528.854 146.553C556.82 134.375 589.523 128.285 626.961 128.285C637.336 128.285 647.26 128.962 656.732 130.315C666.656 131.668 676.354 133.472 685.826 135.728L855.654 136.404V202.712C832.649 203.163 809.193 200.456 785.287 194.592C761.831 188.277 741.082 181.511 723.039 174.294L721.009 169.558C736.346 176.775 750.78 185.796 764.312 196.622C777.844 206.997 788.67 219.176 796.789 233.159C805.359 246.691 809.644 262.478 809.644 280.521C809.644 309.84 802.202 335.1 787.317 356.301C772.431 377.05 751.231 393.063 723.716 404.339C696.652 415.165 664.4 420.578 626.961 420.578ZM739.954 610.704V594.465C739.954 573.716 733.188 559.282 719.656 551.162C706.575 543.043 688.532 538.984 665.528 538.984H560.654C540.356 538.984 523.216 537.405 509.232 534.247C495.7 531.09 484.875 526.579 476.755 520.715C468.636 514.851 462.772 507.86 459.164 499.741C455.555 492.072 453.751 483.728 453.751 474.706C453.751 456.663 459.615 443.131 471.343 434.11C483.07 424.638 498.858 418.323 518.705 415.165C538.552 412.008 560.429 411.331 584.335 413.135L626.961 420.578C598.544 421.48 577.344 423.961 563.361 428.021C549.829 431.629 543.063 439.072 543.063 450.349C543.063 457.115 545.769 462.527 551.182 466.587C556.595 470.196 564.263 472 574.186 472H684.473C714.694 472 740.405 475.157 761.606 481.472C783.257 488.238 799.721 499.29 810.998 514.626C822.274 530.413 827.913 551.839 827.913 578.903V610.704H739.954ZM626.961 356.977C646.357 356.977 663.273 353.82 677.707 347.505C692.592 341.19 704.094 332.168 712.214 320.441C720.333 308.262 724.392 293.828 724.392 277.138C724.392 259.997 720.333 245.338 712.214 233.159C704.094 220.98 692.818 211.733 678.383 205.418C663.949 198.652 646.809 195.269 626.961 195.269C607.566 195.269 590.425 198.652 575.54 205.418C560.654 211.733 549.152 220.98 541.033 233.159C532.914 245.338 528.854 259.997 528.854 277.138C528.854 293.828 532.914 308.262 541.033 320.441C549.152 332.168 560.429 341.19 574.863 347.505C589.748 353.82 607.114 356.977 626.961 356.977Z" fill="white"/>
<path d="M214.841 480.119C172.441 480.119 135.002 473.353 102.525 459.821C70.4989 446.289 45.4646 426.667 27.4218 400.956C9.37905 374.794 0.357666 342.769 0.357666 304.879C0.357666 266.989 9.37905 234.963 27.4218 208.801C45.4646 182.188 70.4989 162.115 102.525 148.583C135.002 135.051 172.441 128.285 214.841 128.285C257.242 128.285 294.229 135.051 325.804 148.583C357.83 162.115 382.864 182.188 400.907 208.801C418.95 234.963 427.971 266.989 427.971 304.879C427.971 342.769 418.95 374.794 400.907 400.956C382.864 426.667 357.83 446.289 325.804 459.821C294.229 473.353 257.242 480.119 214.841 480.119ZM214.841 412.459C238.297 412.459 259.271 408.399 277.765 400.28C296.71 391.71 311.595 379.531 322.421 363.743C333.247 347.505 338.659 327.883 338.659 304.879C338.659 281.874 333.247 262.253 322.421 246.014C311.595 229.325 296.936 216.695 278.442 208.124C259.948 199.554 238.748 195.269 214.841 195.269C191.385 195.269 170.185 199.554 151.24 208.124C132.295 216.695 117.185 229.099 105.908 245.338C95.0822 261.576 89.6693 281.423 89.6693 304.879C89.6693 327.883 95.0822 347.505 105.908 363.743C116.733 379.531 131.619 391.71 150.564 400.28C169.509 408.399 190.934 412.459 214.841 412.459Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" xml:space="preserve">
<style type="text/css">
.st0{fill:#21262E;}
.st1{fill:#FFFFFF;}
.st2{fill:#54AE47;}
</style>
<path class="st0" d="M170.1,3.6H29.7C15.1,3.6,3.2,15.5,3.2,30.1v140.4c0,14.6,11.9,26.5,26.5,26.5h140.4
c14.6,0,26.5-11.9,26.5-26.5V30.1C196.5,15.5,184.6,3.6,170.1,3.6z"/>
<g>
<g>
<path class="st1" d="M35.3,126.3c-3-0.5-5.7-1.1-8.1-1.9v-12.3c2.4,0.8,5.2,1.5,8.3,1.9c3.1,0.5,6,0.7,8.7,0.7
c3.4,0,5.9-0.5,7.4-1.4c1.5-0.9,2.2-2.3,2.2-4.1c0-1-0.2-1.8-0.6-2.4c-0.4-0.6-1.2-1.2-2.2-1.7c-1.1-0.5-2.6-1-4.6-1.6l-4.8-1.3
c-5.3-1.3-9.3-3.4-11.9-6.2c-2.6-2.8-4-6.5-4-11.1c0-5.9,2.2-10.5,6.5-13.7c4.3-3.2,10.8-4.8,19.3-4.8c2.7,0,5.2,0.2,7.7,0.5
c2.5,0.3,4.7,0.7,6.7,1.3v12.3c-1.8-0.6-3.9-1-6.3-1.3c-2.4-0.3-4.8-0.5-7.2-0.5c-4.2,0-7.2,0.5-8.9,1.4c-1.7,1-2.6,2.3-2.6,4.1
c0,0.9,0.2,1.7,0.6,2.3c0.4,0.6,1,1.2,1.9,1.6c0.9,0.5,2.2,0.9,3.9,1.4l4.8,1.2c5.8,1.4,10.1,3.6,12.9,6.4
c2.7,2.8,4.1,6.5,4.1,11.1c0,3.8-1,7.1-2.9,9.9c-2,2.8-4.8,5-8.5,6.5c-3.7,1.5-8.1,2.3-13.2,2.3C41.2,126.9,38.2,126.7,35.3,126.3
z"/>
<path class="st1" d="M74.5,68.4c6.4-1.2,12.7-1.9,18.8-1.9c9.2,0,16,1.7,20.5,5.2c4.5,3.5,6.8,9,6.8,16.4c0,7.1-2.2,12.5-6.6,16.2
c-4.4,3.7-10.8,5.5-19.3,5.5c-0.8,0-1.9,0-3.3-0.1l-1.7,0v16.4H74.5V68.4z M103.2,96.2c1.7-1.6,2.6-4.2,2.6-7.9
c0-3.7-0.9-6.3-2.7-8c-1.8-1.7-4.6-2.5-8.4-2.5c-2,0-3.6,0.1-5,0.4v20c1.8,0.2,3.5,0.3,5.1,0.3C98.6,98.6,101.4,97.8,103.2,96.2z"
/>
<path class="st1" d="M156,95.3l20.9,30.8h-16.8L147,106.3l-13.2,19.8h-16.6l20.9-30.6l-19.3-28.2h16.9l11.4,17.1l11.4-17.1H175
L156,95.3z"/>
</g>
</g>
<path class="st2" d="M184.2,153.6v11.9c0,9.8-8,17.8-17.8,17.8H33.8c-9.8,0-17.8-8-17.8-17.8v-11.9c0-2.6,2.1-4.7,4.7-4.7h159
C182.1,148.9,184.2,151,184.2,153.6z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Some files were not shown because too many files have changed in this diff Show More