├── backend ├── cli │ ├── __init__.py │ └── __main__.py ├── nx │ ├── __init__.py │ └── utils │ │ ├── xml.py │ │ ├── hashing.py │ │ └── __init__.py ├── server │ ├── __init__.py │ ├── context.py │ ├── models │ │ ├── plugin_models.py │ │ ├── __init__.py │ │ └── login.py │ ├── request.py │ ├── utils.py │ └── background.py ├── setup │ ├── __init__.py │ ├── defaults │ │ ├── __init__.py │ │ ├── actions.py │ │ ├── services.py │ │ └── channels.py │ ├── dump.py │ ├── metatypes.py │ └── __main__.py ├── tests │ ├── __init__.py │ └── test_supernova.py ├── .gitignore ├── nebula │ ├── helpers │ │ └── __init__.py │ ├── metadata │ │ ├── __init__.py │ │ └── format.py │ ├── objects │ │ ├── __init__.py │ │ ├── event.py │ │ ├── utils.py │ │ └── bin.py │ ├── version.py │ ├── plugins │ │ ├── cli.py │ │ ├── __init__.py │ │ └── common.py │ ├── settings │ │ └── common.py │ ├── messaging.py │ ├── exceptions.py │ ├── __init__.py │ ├── common.py │ └── config.py ├── api │ ├── init │ │ └── __init__.py │ ├── order │ │ ├── __init__.py │ │ ├── models.py │ │ └── order_request.py │ ├── playout │ │ ├── __init__.py │ │ ├── models.py │ │ └── playout_request.py │ ├── rundown │ │ ├── __init__.py │ │ ├── rundown_request.py │ │ └── models.py │ ├── scheduler │ │ ├── __init__.py │ │ ├── models.py │ │ └── scheduler_request.py │ ├── stats │ │ └── __init__.py │ ├── scheduling_templates │ │ ├── __init__.py │ │ ├── models.py │ │ └── utils.py │ ├── users │ │ ├── __init__.py │ │ ├── list_users_request.py │ │ └── save_user_request.py │ ├── jobs │ │ └── __init__.py │ ├── sessions │ │ ├── __init__.py │ │ ├── list_sessions_request.py │ │ └── invalidate_session_request.py │ ├── auth │ │ ├── __init__.py │ │ ├── logout_request.py │ │ ├── token_exchange.py │ │ └── set_password_request.py │ ├── proxy.py │ └── solve.py ├── nxtools │ └── __init__.py └── manage ├── frontend ├── src │ ├── vite-env.d.ts │ ├── components │ │ ├── table │ │ │ ├── index.jsx │ │ │ ├── BodyCell.jsx │ │ │ └── HeaderCell.jsx │ │ ├── ErrorBanner.tsx │ │ ├── Icon.tsx │ │ ├── TextArea.tsx │ │ ├── InputInteger.tsx │ │ ├── InputNumber.tsx │ │ ├── lib │ │ │ └── datetime.ts │ │ ├── InputPassword.tsx │ │ ├── ScrollBox.tsx │ │ ├── InputSwitch.tsx │ │ ├── InputText.tsx │ │ ├── RangeSlider.tsx │ │ ├── theme.ts │ │ ├── Timestamp.tsx │ │ ├── Loader.tsx │ │ ├── Dropdown.styled.tsx │ │ ├── Form.tsx │ │ ├── RadioButton.tsx │ │ ├── index.tsx │ │ ├── Dialog.styled.tsx │ │ ├── Progress.tsx │ │ ├── InputColor.tsx │ │ ├── Button.tsx │ │ ├── Canvas.tsx │ │ ├── Input.styled.tsx │ │ ├── Button.styled.tsx │ │ ├── InputSwitch.styled.tsx │ │ ├── Dialog.tsx │ │ └── DatePickerDialog.tsx │ ├── containers │ │ ├── MediaUpload │ │ │ ├── index.tsx │ │ │ ├── FileSelect.styled.tsx │ │ │ ├── MediaUploadMonitor.styled.tsx │ │ │ ├── MediaUpload.tsx │ │ │ ├── MediaUploadDialog.tsx │ │ │ └── MediaUploadMonitor.tsx │ │ ├── Browser │ │ │ ├── index.jsx │ │ │ └── BrowserNav.jsx │ │ ├── Calendar │ │ │ ├── index.jsx │ │ │ ├── ZoomControl.jsx │ │ │ ├── CalendarWrapper.jsx │ │ │ ├── drawUtils.js │ │ │ ├── drawMarks.js │ │ │ └── drawEvents.js │ │ ├── MainNavbar │ │ │ ├── index.jsx │ │ │ ├── PageTitle.jsx │ │ │ ├── ChannelSwitcher.jsx │ │ │ └── Logo.jsx │ │ ├── VideoPlayer │ │ │ ├── index.jsx │ │ │ ├── VideoPlayer.jsx │ │ │ ├── ToggleButtonContainer.jsx │ │ │ ├── ChannelSelect.jsx │ │ │ ├── VideoOverlay.jsx │ │ │ └── AudioContext.jsx │ │ ├── Pagination.jsx │ │ ├── DraggableIcon.jsx │ │ ├── Dialogs │ │ │ ├── ConfirmDialog.jsx │ │ │ ├── MetadataDialog.jsx │ │ │ ├── SubclipsDialog.jsx │ │ │ └── SendToDialog.jsx │ │ └── DateNav.jsx │ ├── pages │ │ ├── Rundown │ │ │ ├── index.jsx │ │ │ ├── RundownTableWrapper.jsx │ │ │ ├── RundownEditTools.jsx │ │ │ ├── RundownNav.jsx │ │ │ └── utils.js │ │ ├── System │ │ │ ├── Users │ │ │ │ ├── index.tsx │ │ │ │ └── UserList.jsx │ │ │ ├── Services │ │ │ │ └── index.tsx │ │ │ ├── Storages │ │ │ │ ├── index.tsx │ │ │ │ ├── common.ts │ │ │ │ ├── Storages.styled.tsx │ │ │ │ └── StorageVisualization.tsx │ │ │ ├── index.tsx │ │ │ └── SystemPage.tsx │ │ ├── JobsPage │ │ │ ├── index.jsx │ │ │ └── JobsNav.jsx │ │ ├── Profile │ │ │ └── index.jsx │ │ ├── Scheduler │ │ │ ├── index.jsx │ │ │ ├── utils.js │ │ │ └── SchedulerNav.jsx │ │ ├── AssetEditor │ │ │ ├── index.jsx │ │ │ ├── MetadataDetail.jsx │ │ │ ├── AssigneesButton.jsx │ │ │ └── EditorNav.jsx │ │ ├── LoadingPage.tsx │ │ ├── ToolPage.jsx │ │ └── LoginPage.styled.tsx │ ├── client │ │ └── index.ts │ ├── hooks │ │ ├── index.ts │ │ ├── useKeyDown.tsx │ │ ├── useLocalStorage.tsx │ │ └── useDialog.jsx │ ├── tableFormat │ │ ├── formatAuthorship.tsx │ │ ├── formatRundownRunMode.jsx │ │ ├── formatObjectIdFolder.jsx │ │ ├── formatMetaTimecode.jsx │ │ ├── formatMetaDatetime.jsx │ │ ├── formatObjectQcState.tsx │ │ ├── formatRundownTime.jsx │ │ ├── formatObjectStatus.jsx │ │ ├── formatRundownDifference.jsx │ │ ├── formatObjectTitle.jsx │ │ ├── formatObjectDuration.jsx │ │ ├── formatRundownSymbol.jsx │ │ └── cellStyles.scss │ ├── types │ │ └── upload.ts │ ├── index.tsx │ ├── utils.js │ ├── websocket.jsx │ └── actions.js ├── vite.config.d.ts ├── .prettierrc.json ├── .gitignore ├── index.html ├── openapi-ts.config.ts ├── tsconfig.node.json ├── tsconfig.json ├── vite.config.js ├── vite.config.ts └── package.json ├── .dockerignore ├── .gitignore ├── .github ├── release.yml └── workflows │ ├── dev.yml │ └── release.yml ├── Dockerfile └── Makefile /backend/cli/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/nx/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/setup/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /backend/nebula/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/nebula/metadata/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/nebula/objects/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/setup/defaults/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/nebula/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "6.1.2" 2 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | node_modules 3 | 4 | storage/ 5 | plugins/ 6 | worker/ 7 | -------------------------------------------------------------------------------- /frontend/src/components/table/index.jsx: -------------------------------------------------------------------------------- 1 | import Table from './Table'; 2 | export default Table; 3 | -------------------------------------------------------------------------------- /frontend/src/containers/MediaUpload/index.tsx: -------------------------------------------------------------------------------- 1 | export { UploadButton } from './MediaUpload'; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/Rundown/index.jsx: -------------------------------------------------------------------------------- 1 | import Rundown from './Rundown'; 2 | export default Rundown; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/System/Users/index.tsx: -------------------------------------------------------------------------------- 1 | import Users from './Users'; 2 | export default Users; 3 | -------------------------------------------------------------------------------- /backend/api/init/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["InitRequest"] 2 | 3 | from .init_request import InitRequest 4 | -------------------------------------------------------------------------------- /frontend/src/containers/Browser/index.jsx: -------------------------------------------------------------------------------- 1 | import Browser from './Browser'; 2 | export default Browser; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/JobsPage/index.jsx: -------------------------------------------------------------------------------- 1 | import JobsPage from './JobsPage'; 2 | export default JobsPage; 3 | -------------------------------------------------------------------------------- /backend/api/order/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["OrderRequest"] 2 | 3 | from .order_request import OrderRequest 4 | -------------------------------------------------------------------------------- /frontend/src/containers/Calendar/index.jsx: -------------------------------------------------------------------------------- 1 | import Calendar from './Calendar'; 2 | export default Calendar; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/Profile/index.jsx: -------------------------------------------------------------------------------- 1 | import ProfilePage from './ProfilePage'; 2 | export default ProfilePage; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/Scheduler/index.jsx: -------------------------------------------------------------------------------- 1 | import Scheduler from './Scheduler'; 2 | export default Scheduler; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/System/Services/index.tsx: -------------------------------------------------------------------------------- 1 | import Services from './Services'; 2 | export default Services; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/System/Storages/index.tsx: -------------------------------------------------------------------------------- 1 | import Storages from './Storages'; 2 | export default Storages; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/System/index.tsx: -------------------------------------------------------------------------------- 1 | import SystemPage from './SystemPage'; 2 | export default SystemPage; 3 | -------------------------------------------------------------------------------- /backend/api/playout/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["PlayoutRequest"] 2 | 3 | from .playout_request import PlayoutRequest 4 | -------------------------------------------------------------------------------- /backend/api/rundown/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["RundownRequest"] 2 | 3 | from .rundown_request import RundownRequest 4 | -------------------------------------------------------------------------------- /frontend/src/containers/MainNavbar/index.jsx: -------------------------------------------------------------------------------- 1 | import MainNavbar from './MainNavbar'; 2 | export default MainNavbar; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/AssetEditor/index.jsx: -------------------------------------------------------------------------------- 1 | import AssetEditor from './AssetEditor'; 2 | export default AssetEditor; 3 | -------------------------------------------------------------------------------- /frontend/src/client/index.ts: -------------------------------------------------------------------------------- 1 | // This file is auto-generated by @hey-api/openapi-ts 2 | export * from './types.gen'; 3 | -------------------------------------------------------------------------------- /frontend/src/containers/VideoPlayer/index.jsx: -------------------------------------------------------------------------------- 1 | import VideoPlayer from './VideoPlayer'; 2 | export default VideoPlayer; 3 | -------------------------------------------------------------------------------- /backend/api/scheduler/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["SchedulerRequest"] 2 | 3 | from .scheduler_request import SchedulerRequest 4 | -------------------------------------------------------------------------------- /backend/setup/defaults/actions.py: -------------------------------------------------------------------------------- 1 | from nebula.settings.models import ActionSettings 2 | 3 | ACTIONS: list[ActionSettings] = [] 4 | -------------------------------------------------------------------------------- /backend/api/stats/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "NebulaStoragesRequest", 3 | ] 4 | 5 | from .storages import NebulaStoragesRequest 6 | -------------------------------------------------------------------------------- /backend/setup/defaults/services.py: -------------------------------------------------------------------------------- 1 | from nebula.settings.models import ServiceSettings 2 | 3 | SERVICES: list[ServiceSettings] = [] 4 | -------------------------------------------------------------------------------- /backend/setup/defaults/channels.py: -------------------------------------------------------------------------------- 1 | from nebula.settings.models import PlayoutChannelSettings 2 | 3 | CHANNELS: list[PlayoutChannelSettings] = [] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .mypy_cache 3 | .pytest_cache 4 | .env 5 | frontend/node_modules 6 | 7 | storage/ 8 | settings/*.py 9 | /plugins/ 10 | -------------------------------------------------------------------------------- /backend/nebula/plugins/cli.py: -------------------------------------------------------------------------------- 1 | class CLIPlugin: 2 | name: str = "cli_plugin" 3 | 4 | def __repr__(self) -> str: 5 | return f"" 6 | -------------------------------------------------------------------------------- /frontend/vite.config.d.ts: -------------------------------------------------------------------------------- 1 | interface ConfigEnv { 2 | mode: string; 3 | } 4 | declare const _default: ({ mode }: ConfigEnv) => import('vite').UserConfig; 5 | export default _default; 6 | -------------------------------------------------------------------------------- /backend/api/scheduling_templates/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["ApplyTemplateRequest", "ListTemplatesRequest"] 2 | 3 | from .template_request import ApplyTemplateRequest, ListTemplatesRequest 4 | -------------------------------------------------------------------------------- /backend/api/users/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["ListUsersRequest", "SaveUserRequest"] 2 | 3 | from .list_users_request import ListUsersRequest 4 | from .save_user_request import SaveUserRequest 5 | -------------------------------------------------------------------------------- /backend/nebula/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["SolverPlugin", "CLIPlugin"] 2 | 3 | from .cli import CLIPlugin 4 | from .common import modules_root 5 | from .solver import SolverPlugin 6 | 7 | assert modules_root 8 | -------------------------------------------------------------------------------- /backend/nebula/settings/common.py: -------------------------------------------------------------------------------- 1 | from typing import Literal 2 | 3 | from pydantic import BaseModel 4 | 5 | 6 | class SettingsModel(BaseModel): 7 | pass 8 | 9 | 10 | LanguageCode = Literal["en", "cs"] 11 | -------------------------------------------------------------------------------- /frontend/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { default as useLocalStorage } from './useLocalStorage'; 2 | export { default as useKeyDown } from './useKeyDown'; 3 | 4 | export { DialogProvider, useDialog } from './useDialog'; 5 | -------------------------------------------------------------------------------- /backend/api/jobs/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["JobsRequest", "ActionsRequest", "SendRequest"] 2 | 3 | from .actions_request import ActionsRequest 4 | from .jobs_request import JobsRequest 5 | from .send_request import SendRequest 6 | -------------------------------------------------------------------------------- /backend/api/sessions/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["ListSessionsRequest", "InvalidateSessionRequest"] 2 | 3 | from .invalidate_session_request import InvalidateSessionRequest 4 | from .list_sessions_request import ListSessionsRequest 5 | -------------------------------------------------------------------------------- /frontend/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "es5", 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "printWidth": 88, 7 | "arrowParens": "always", 8 | "bracketSpacing": true, 9 | "singleQuote": true 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/components/table/BodyCell.jsx: -------------------------------------------------------------------------------- 1 | const BodyCell = ({ rowData, column, cellFormatter }) => { 2 | if (cellFormatter) return cellFormatter(rowData, column.name); 3 | return {rowData[column.name]}; 4 | }; 5 | export default BodyCell; 6 | -------------------------------------------------------------------------------- /frontend/src/pages/LoadingPage.tsx: -------------------------------------------------------------------------------- 1 | import { Loader } from '@components'; 2 | 3 | const LoadingPage = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default LoadingPage; 12 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: "🎉 New Features" 4 | labels: 5 | - "feature" 6 | 7 | - title: "❤️ Improvements" 8 | labels: 9 | - "enhancement" 10 | 11 | - title: "🛠️ Bugfixes" 12 | labels: 13 | - "bug" 14 | -------------------------------------------------------------------------------- /frontend/src/tableFormat/formatAuthorship.tsx: -------------------------------------------------------------------------------- 1 | import type { JSX } from 'react/jsx-runtime'; 2 | import nebula from '@/nebula'; 3 | 4 | const formatAuthorship = (rowData: Record, key: string): JSX.Element => { 5 | return {nebula.getUserName(rowData[key])}; 6 | }; 7 | 8 | export default formatAuthorship; 9 | -------------------------------------------------------------------------------- /backend/tests/test_supernova.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import nebula 4 | 5 | 6 | @pytest.mark.asyncio 7 | async def test_supernova(): 8 | res = await nebula.db.fetch("SELECT meta FROM users") 9 | assert res, "No users found" 10 | 11 | user = nebula.User.from_meta(res[0]["meta"]) 12 | assert user.name 13 | -------------------------------------------------------------------------------- /frontend/src/tableFormat/formatRundownRunMode.jsx: -------------------------------------------------------------------------------- 1 | const RUN_MODES = ['Auto', 'Manual', 'Soft', 'Hard', 'Skip']; 2 | 3 | const formatRundownRunMode = (rowData, key) => { 4 | const runMode = RUN_MODES[rowData[key] || 0]; 5 | return {runMode}; 6 | }; 7 | 8 | export default formatRundownRunMode; 9 | -------------------------------------------------------------------------------- /frontend/src/tableFormat/formatObjectIdFolder.jsx: -------------------------------------------------------------------------------- 1 | import nebula from '/src/nebula'; 2 | 3 | const formatObjectIdFolder = (rowData, key) => { 4 | const folder = nebula.settings.folders.find((f) => f.id === rowData[key]); 5 | return {folder?.name}; 6 | }; 7 | 8 | export default formatObjectIdFolder; 9 | -------------------------------------------------------------------------------- /backend/server/context.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, Field 2 | 3 | 4 | class ScopedEndpoint(BaseModel): 5 | endpoint: str 6 | title: str 7 | scopes: list[str] 8 | 9 | 10 | class ServerContext(BaseModel): 11 | scoped_endpoints: list[ScopedEndpoint] = Field(default_factory=list) 12 | 13 | 14 | server_context = ServerContext() 15 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /backend/nx/utils/xml.py: -------------------------------------------------------------------------------- 1 | __all__ = ["xml"] 2 | 3 | from xml.etree import ElementTree 4 | 5 | 6 | def xml(data: str) -> ElementTree.Element | None: 7 | """Parse an XML string using ElementTree 8 | 9 | Args: 10 | data (str): The XML document to parse 11 | 12 | Returns: 13 | ElementTree.Element: The root element of the parsed XML string 14 | """ 15 | return ElementTree.XML(data) 16 | -------------------------------------------------------------------------------- /backend/server/models/plugin_models.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Literal 2 | 3 | from pydantic import BaseModel, Field 4 | 5 | 6 | class ContextPluginResponseModel(BaseModel): 7 | type: Literal["table", "markdown", "cards"] = Field(...) 8 | header: str | None = Field(None) 9 | footer: str | None = Field(None) 10 | dialog_style: dict[str, Any] | None = Field(None) 11 | payload: Any | None = Field(None) 12 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Nebula 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/src/components/ErrorBanner.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | 3 | const ErrorBanner = styled.div` 4 | padding: 20px; 5 | background-color: var(--color-surface-04); 6 | color: var(--color-red); 7 | margin: 10px; 8 | border-radius: 4px; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | font-size: 14px; 13 | font-weight: 700; 14 | `; 15 | 16 | export default ErrorBanner; 17 | -------------------------------------------------------------------------------- /frontend/src/containers/VideoPlayer/VideoPlayer.jsx: -------------------------------------------------------------------------------- 1 | import { AudioContextProvider } from './AudioContext'; 2 | import { VideoPlayerBody } from './VideoPlayerBody'; 3 | 4 | const VideoPlayer = (props) => { 5 | const audioChannels = 2; 6 | 7 | return ( 8 | 9 | 10 | 11 | ); 12 | }; 13 | 14 | export default VideoPlayer; 15 | -------------------------------------------------------------------------------- /backend/setup/dump.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel 2 | 3 | from nebula.settings import get_server_settings 4 | 5 | 6 | def dump_data(filename: str, data: BaseModel) -> None: 7 | with open(filename, "w") as f: 8 | f.write(data.model_dump_json(exclude_unset=True, exclude_none=True)) 9 | 10 | 11 | async def dump_settings() -> None: 12 | settings = await get_server_settings() 13 | dump_data("/settings/server.json", settings) 14 | -------------------------------------------------------------------------------- /frontend/src/tableFormat/formatMetaTimecode.jsx: -------------------------------------------------------------------------------- 1 | import { Timecode } from '@wfoxall/timeframe'; 2 | 3 | const formatMetaTimecode = (rowData, key) => { 4 | let duration = rowData[key] || 0; 5 | if (!duration) return ; 6 | 7 | const fps = rowData['video/fps_f'] || 25; 8 | const timecode = new Timecode(duration * fps, fps); 9 | return {timecode.toString().substring(0, 11)}; 10 | }; 11 | 12 | export default formatMetaTimecode; 13 | -------------------------------------------------------------------------------- /backend/api/auth/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "LoginRequest", 3 | "LogoutRequest", 4 | "SetPasswordRequest", 5 | "SSOLoginRequest", 6 | "SSOLoginCallback", 7 | "TokenExchangeRequest", 8 | ] 9 | 10 | from .login_request import LoginRequest 11 | from .logout_request import LogoutRequest 12 | from .set_password_request import SetPasswordRequest 13 | from .sso import SSOLoginCallback, SSOLoginRequest 14 | from .token_exchange import TokenExchangeRequest 15 | -------------------------------------------------------------------------------- /backend/nebula/plugins/common.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from nebula.config import config 5 | 6 | modules_root = os.path.join(config.plugin_dir, "common") 7 | if os.path.isdir(modules_root): 8 | for pydirname in os.listdir(modules_root): 9 | pydir = os.path.join(modules_root, pydirname) 10 | if not os.path.isdir(pydir): 11 | continue 12 | if pydir in sys.path: 13 | continue 14 | sys.path.append(pydir) 15 | -------------------------------------------------------------------------------- /frontend/src/tableFormat/formatMetaDatetime.jsx: -------------------------------------------------------------------------------- 1 | import { Timestamp } from '/src/components'; 2 | 3 | const formatMetaDatetime = (rowData, key, mode = 'datetime') => { 4 | const timestamp = rowData[key]; 5 | if (!timestamp) 6 | return ( 7 | 8 |
9 | 10 | ); 11 | return ( 12 | 13 | 14 | 15 | ); 16 | }; 17 | 18 | export default formatMetaDatetime; 19 | -------------------------------------------------------------------------------- /backend/server/models/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "UserModel", 3 | "UserPermissionsModel", 4 | "RequestModel", 5 | "ResponseModel", 6 | "ContextPluginResponseModel", 7 | ] 8 | 9 | from pydantic import BaseModel 10 | 11 | from .plugin_models import ContextPluginResponseModel 12 | from .user_models import UserModel, UserPermissionsModel 13 | 14 | 15 | class RequestModel(BaseModel): 16 | pass 17 | 18 | 19 | class ResponseModel(BaseModel): 20 | pass 21 | -------------------------------------------------------------------------------- /frontend/src/pages/ToolPage.jsx: -------------------------------------------------------------------------------- 1 | import { useParams } from 'react-router-dom'; 2 | import styled from 'styled-components'; 3 | 4 | const Iframe = styled.iframe` 5 | flex-grow: 1; 6 | backgound: transparent; 7 | padding: 0; 8 | border: none; 9 | `; 10 | 11 | const ToolPage = () => { 12 | const { tool } = useParams(); 13 | const toolURL = `${window.location.origin}/plugins/${tool}/index.html`; 14 | return