├── .github
├── semantic.yml
└── workflows
│ └── flutter-analyze.yml
├── linux
├── .gitignore
├── main.cc
├── flutter
│ ├── generated_plugin_registrant.h
│ ├── generated_plugin_registrant.cc
│ └── generated_plugins.cmake
└── my_application.h
├── fastlane
└── metadata
│ └── android
│ └── en-US
│ ├── title.txt
│ ├── short_description.txt
│ ├── images
│ ├── icon.png
│ ├── featuredGraphics.png
│ └── phoneScreenshots
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ └── 6.png
│ └── changelogs
│ └── 1.txt
├── android
├── Gemfile
├── app
│ ├── .settings
│ │ └── org.eclipse.buildship.core.prefs
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── drawable
│ │ │ │ │ ├── background.png
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-hdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── android12splash.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-mdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── android12splash.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xhdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── android12splash.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-v21
│ │ │ │ │ ├── background.png
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-xxhdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── android12splash.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── drawable-xxxhdpi
│ │ │ │ │ ├── splash.png
│ │ │ │ │ ├── android12splash.png
│ │ │ │ │ └── ic_launcher_foreground.png
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-night
│ │ │ │ │ ├── background.png
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── drawable-night-v21
│ │ │ │ │ ├── background.png
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── drawable-night-hdpi
│ │ │ │ │ └── android12splash.png
│ │ │ │ ├── drawable-night-mdpi
│ │ │ │ │ └── android12splash.png
│ │ │ │ ├── drawable-night-xhdpi
│ │ │ │ │ └── android12splash.png
│ │ │ │ ├── drawable-night-xxhdpi
│ │ │ │ │ └── android12splash.png
│ │ │ │ ├── drawable-night-xxxhdpi
│ │ │ │ │ └── android12splash.png
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ └── ic_launcher.xml
│ │ │ │ ├── values-night
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values-v31
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night-v31
│ │ │ │ │ └── styles.xml
│ │ │ └── kotlin
│ │ │ │ └── com
│ │ │ │ ├── example
│ │ │ │ └── obs_blade
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── kounex
│ │ │ │ └── obs_blade
│ │ │ │ └── MainActivity.kt
│ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── .classpath
│ └── .project
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── .gitignore
├── build.gradle
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── settings.gradle
└── .project
├── ios
├── Runner
│ ├── Runner-Bridging-Header.h
│ ├── Assets.xcassets
│ │ ├── LaunchImage.imageset
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ ├── README.md
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-50x50@1x.png
│ │ │ ├── Icon-App-50x50@2x.png
│ │ │ ├── Icon-App-57x57@1x.png
│ │ │ ├── Icon-App-57x57@2x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-72x72@1x.png
│ │ │ ├── Icon-App-72x72@2x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchBackground.imageset
│ │ │ ├── background.png
│ │ │ ├── darkbackground.png
│ │ │ └── Contents.json
│ └── AppDelegate.swift
├── Flutter
│ ├── Debug.xcconfig
│ ├── Release.xcconfig
│ └── AppFrameworkInfo.plist
├── Runner.xcodeproj
│ └── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
├── Runner.xcworkspace
│ ├── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
│ └── contents.xcworkspacedata
├── .gitignore
└── Podfile
├── web
├── favicon.png
├── icons
│ ├── Icon-192.png
│ ├── Icon-512.png
│ ├── Icon-maskable-192.png
│ └── Icon-maskable-512.png
├── splash
│ ├── img
│ │ ├── dark-1x.png
│ │ ├── dark-2x.png
│ │ ├── dark-3x.png
│ │ ├── dark-4x.png
│ │ ├── light-1x.png
│ │ ├── light-2x.png
│ │ ├── light-3x.png
│ │ └── light-4x.png
│ ├── splash.js
│ └── style.css
└── manifest.json
├── .vscode
└── settings.json
├── assets
├── fonts
│ ├── JamIcons.ttf
│ └── CustomFlutterIcons.ttf
├── icons
│ └── app
│ │ ├── splash.png
│ │ ├── app_logo.png
│ │ ├── app_logo_old.png
│ │ ├── splash_old.png
│ │ └── app_logo_adaptive.png
└── images
│ ├── base_logo.png
│ ├── base_logo_dark.png
│ ├── base_logo_old.png
│ ├── flutter_logo_render.png
│ ├── intro
│ ├── intro_obs_websocket_page.png
│ ├── intro_obs_websocket_download.png
│ └── intro_obs_websocket_settings.png
│ └── kounex_logo_ai_no_background.png
├── .gitmodules
├── macos
├── .gitignore
├── Runner
│ ├── Configs
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ ├── Warnings.xcconfig
│ │ └── AppInfo.xcconfig
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── app_icon_16.png
│ │ │ ├── app_icon_32.png
│ │ │ ├── app_icon_64.png
│ │ │ ├── app_icon_1024.png
│ │ │ ├── app_icon_128.png
│ │ │ ├── app_icon_256.png
│ │ │ └── app_icon_512.png
│ ├── AppDelegate.swift
│ ├── Release.entitlements
│ ├── DebugProfile.entitlements
│ ├── MainFlutterWindow.swift
│ └── Info.plist
├── Flutter
│ ├── Flutter-Debug.xcconfig
│ ├── Flutter-Release.xcconfig
│ └── GeneratedPluginRegistrant.swift
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── Runner.xcodeproj
│ └── project.xcworkspace
│ │ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── Podfile
├── windows
├── runner
│ ├── resources
│ │ └── app_icon.ico
│ ├── resource.h
│ ├── CMakeLists.txt
│ ├── utils.h
│ ├── runner.exe.manifest
│ ├── flutter_window.h
│ └── main.cpp
├── .gitignore
└── flutter
│ ├── generated_plugin_registrant.h
│ ├── generated_plugins.cmake
│ └── generated_plugin_registrant.cc
├── lib
├── types
│ ├── enums
│ │ ├── web_socket_codes
│ │ │ └── base.dart
│ │ ├── scene_item_icon.dart
│ │ ├── order.dart
│ │ └── hive_keys.dart
│ ├── exceptions
│ │ └── network.dart
│ ├── classes
│ │ ├── default_filter.dart
│ │ ├── stream
│ │ │ ├── responses
│ │ │ │ ├── get_input_mute.dart
│ │ │ │ ├── get_hotkey_list.dart
│ │ │ │ ├── get_virtual_cam_status.dart
│ │ │ │ ├── get_studio_mode_enabled.dart
│ │ │ │ ├── get_record_directory.dart
│ │ │ │ ├── get_replay_buffer_status.dart
│ │ │ │ ├── get_input_default_settings.dart
│ │ │ │ ├── get_input_list.dart
│ │ │ │ ├── get_input_volume.dart
│ │ │ │ ├── get_input_audio_sync_offset.dart
│ │ │ │ ├── get_source_filter_default_settings.dart
│ │ │ │ ├── get_profile_list.dart
│ │ │ │ ├── get_source_filter_list.dart
│ │ │ │ ├── get_scene_item_list.dart
│ │ │ │ ├── get_scene_collection_list.dart
│ │ │ │ ├── get_group_scene_item_list.dart
│ │ │ │ ├── get_source_screenshot.dart
│ │ │ │ ├── save_source_screenshot.dart
│ │ │ │ ├── get_scene_list.dart
│ │ │ │ ├── get_record_status.dart
│ │ │ │ ├── get_special_inputs.dart
│ │ │ │ ├── get_scene_transition_list.dart
│ │ │ │ ├── get_current_scene_transition.dart
│ │ │ │ ├── get_stream_status.dart
│ │ │ │ └── get_version.dart
│ │ │ ├── events
│ │ │ │ ├── profile_list_changed.dart
│ │ │ │ ├── switch_transition.dart
│ │ │ │ ├── studio_mode_switched.dart
│ │ │ │ ├── current_preview_scene_changed.dart
│ │ │ │ ├── current_program_scene_changed.dart
│ │ │ │ ├── current_profile_changed.dart
│ │ │ │ ├── scene_collection_list_changed.dart
│ │ │ │ ├── transition_duration_changed.dart
│ │ │ │ ├── input_name_changed.dart
│ │ │ │ ├── current_scene_transition_changed.dart
│ │ │ │ ├── input_mute_state_changed.dart
│ │ │ │ ├── stream_state_changed.dart
│ │ │ │ ├── transition_list_changed.dart
│ │ │ │ ├── virtual_cam_state_changed.dart
│ │ │ │ ├── replay_buffer_state_changed.dart
│ │ │ │ ├── input_audio_sync_offset_changed.dart
│ │ │ │ ├── input_volume_changed.dart
│ │ │ │ ├── current_scene_collection_changed.dart
│ │ │ │ ├── source_filter_enable_state_changed.dart
│ │ │ │ ├── record_state_changed.dart
│ │ │ │ ├── scene_item_enable_state_changed.dart
│ │ │ │ ├── scene_item_list_reindexed.dart
│ │ │ │ ├── scene_item_removed.dart
│ │ │ │ ├── scene_item_created.dart
│ │ │ │ ├── current_scene_collection_changing.dart
│ │ │ │ ├── transition_begin.dart
│ │ │ │ ├── base.dart
│ │ │ │ └── input_volume_meters.dart
│ │ │ └── batch_responses
│ │ │ │ ├── filter_list.dart
│ │ │ │ ├── filter_default_settings.dart
│ │ │ │ ├── screenshot.dart
│ │ │ │ ├── stats.dart
│ │ │ │ ├── inputs.dart
│ │ │ │ └── base.dart
│ │ ├── session.dart
│ │ └── api
│ │ │ ├── input_channel.dart
│ │ │ ├── scene.dart
│ │ │ ├── filter.dart
│ │ │ ├── input.dart
│ │ │ ├── scene.g.dart
│ │ │ ├── input_channel.g.dart
│ │ │ ├── filter.g.dart
│ │ │ ├── transition.dart
│ │ │ ├── scene_item_transform.dart
│ │ │ ├── transition.g.dart
│ │ │ ├── record_stats.dart
│ │ │ ├── scene_item.dart
│ │ │ └── input.g.dart
│ ├── extensions
│ │ ├── string.dart
│ │ ├── list.dart
│ │ └── color.dart
│ └── interfaces
│ │ ├── local_persistance.dart
│ │ └── message.dart
├── models
│ ├── hotkey.dart
│ ├── enums
│ │ ├── scene_item_type.dart
│ │ ├── chat_type.dart
│ │ ├── log_level.dart
│ │ ├── scene_item_type.g.dart
│ │ ├── dashboard_element.dart
│ │ ├── log_level.g.dart
│ │ └── chat_type.g.dart
│ ├── purchased_tip.dart
│ ├── type_ids.dart
│ ├── app_log.dart
│ ├── connection.dart
│ ├── hotkey.g.dart
│ ├── hidden_scene.dart
│ └── hidden_scene.g.dart
├── utils
│ ├── icons
│ │ └── custom_flutter_icons.dart
│ ├── general_helper.dart
│ └── authentication_helper.dart
├── shared
│ ├── general
│ │ ├── base
│ │ │ ├── divider.dart
│ │ │ ├── constrained_box.dart
│ │ │ ├── icon_button.dart
│ │ │ └── adaptive_dialog
│ │ │ │ └── adaptive_dialog_action.dart
│ │ ├── themed
│ │ │ ├── cupertino_scaffold.dart
│ │ │ ├── rich_text.dart
│ │ │ ├── cupertino_sliver_navigation_bar.dart
│ │ │ └── cupertino_button.dart
│ │ ├── hive_builder.dart
│ │ ├── custom_sliver_list.dart
│ │ ├── responsive_widget_wrapper.dart
│ │ ├── keyboard_number_header.dart
│ │ ├── formatted_text.dart
│ │ ├── question_mark_tooltip.dart
│ │ └── clean_list_tile.dart
│ └── dialogs
│ │ └── info.dart
├── views
│ ├── dashboard
│ │ └── widgets
│ │ │ ├── dashboard_content
│ │ │ ├── scene_content
│ │ │ │ ├── placeholder_scene_item.dart
│ │ │ │ └── media_inputs
│ │ │ │ │ └── media_inputs.dart
│ │ │ ├── exposed_controls
│ │ │ │ └── hotkeys_control
│ │ │ │ │ └── section_header.dart
│ │ │ ├── scene_preview
│ │ │ │ └── preview_warning_dialog.dart
│ │ │ └── dashboard_content_streaming.dart
│ │ │ └── obs_widgets
│ │ │ └── obs_widgets.dart
│ ├── statistics
│ │ └── widgets
│ │ │ ├── stats_entry_placeholder.dart
│ │ │ ├── card_header
│ │ │ └── sort_filter_panel
│ │ │ │ ├── filter_status.dart
│ │ │ │ ├── statistics_date_range.dart
│ │ │ │ ├── stat_type_control.dart
│ │ │ │ ├── exclude_unnamed_checkbox.dart
│ │ │ │ └── filter_name.dart
│ │ │ └── stats_entry
│ │ │ └── entry_meta_chip.dart
│ ├── settings
│ │ ├── custom_theme
│ │ │ └── widgets
│ │ │ │ ├── color_picker
│ │ │ │ ├── color_label.dart
│ │ │ │ └── color_bubble.dart
│ │ │ │ └── custom_theme_list
│ │ │ │ └── theme_colors_row.dart
│ │ ├── data_management
│ │ │ └── widgets
│ │ │ │ └── data_block.dart
│ │ ├── logs
│ │ │ └── logs.dart
│ │ ├── faq
│ │ │ └── widgets
│ │ │ │ └── faq_block.dart
│ │ └── dashboard_customisation
│ │ │ └── order
│ │ │ └── widgets
│ │ │ ├── profiles_preview.dart
│ │ │ ├── controls_preview.dart
│ │ │ └── scene_buttons_preview.dart
│ ├── home
│ │ └── widgets
│ │ │ ├── connect_box
│ │ │ └── auto_discovery
│ │ │ │ ├── result_entry.dart
│ │ │ │ └── session_tile.dart
│ │ │ └── saved_connections
│ │ │ └── placeholder_connection.dart
│ └── intro
│ │ └── widgets
│ │ └── back_so_selection_wrapper.dart
└── stores
│ ├── views
│ ├── intro.dart
│ └── logs.dart
│ └── shared
│ └── tabs.dart
├── flutterw
├── .gitignore
└── test
└── widget_test.dart
/.github/semantic.yml:
--------------------------------------------------------------------------------
1 | titleOnly: true
--------------------------------------------------------------------------------
/linux/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/title.txt:
--------------------------------------------------------------------------------
1 | OBS Blade
--------------------------------------------------------------------------------
/android/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem "fastlane"
4 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/short_description.txt:
--------------------------------------------------------------------------------
1 | Unofficial OBS controller!
--------------------------------------------------------------------------------
/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/favicon.png
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "disabled"
3 | }
4 |
--------------------------------------------------------------------------------
/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/assets/fonts/JamIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/fonts/JamIcons.ttf
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule ".flutter"]
2 | path = .flutter
3 | url = https://github.com/flutter/flutter
4 |
--------------------------------------------------------------------------------
/assets/icons/app/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/icons/app/splash.png
--------------------------------------------------------------------------------
/assets/images/base_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/base_logo.png
--------------------------------------------------------------------------------
/web/splash/img/dark-1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/dark-1x.png
--------------------------------------------------------------------------------
/web/splash/img/dark-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/dark-2x.png
--------------------------------------------------------------------------------
/web/splash/img/dark-3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/dark-3x.png
--------------------------------------------------------------------------------
/web/splash/img/dark-4x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/dark-4x.png
--------------------------------------------------------------------------------
/web/splash/img/light-1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/light-1x.png
--------------------------------------------------------------------------------
/web/splash/img/light-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/light-2x.png
--------------------------------------------------------------------------------
/web/splash/img/light-3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/light-3x.png
--------------------------------------------------------------------------------
/web/splash/img/light-4x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/splash/img/light-4x.png
--------------------------------------------------------------------------------
/assets/icons/app/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/icons/app/app_logo.png
--------------------------------------------------------------------------------
/android/app/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=..
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/assets/icons/app/app_logo_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/icons/app/app_logo_old.png
--------------------------------------------------------------------------------
/assets/icons/app/splash_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/icons/app/splash_old.png
--------------------------------------------------------------------------------
/assets/images/base_logo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/base_logo_dark.png
--------------------------------------------------------------------------------
/assets/images/base_logo_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/base_logo_old.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/assets/fonts/CustomFlutterIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/fonts/CustomFlutterIcons.ttf
--------------------------------------------------------------------------------
/macos/.gitignore:
--------------------------------------------------------------------------------
1 | # Flutter-related
2 | **/Flutter/ephemeral/
3 | **/Pods/
4 |
5 | # Xcode-related
6 | **/xcuserdata/
7 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Debug.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/assets/icons/app/app_logo_adaptive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/icons/app/app_logo_adaptive.png
--------------------------------------------------------------------------------
/assets/images/flutter_logo_render.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/flutter_logo_render.png
--------------------------------------------------------------------------------
/macos/Runner/Configs/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "../../Flutter/Flutter-Release.xcconfig"
2 | #include "Warnings.xcconfig"
3 |
--------------------------------------------------------------------------------
/windows/runner/resources/app_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/windows/runner/resources/app_icon.ico
--------------------------------------------------------------------------------
/lib/types/enums/web_socket_codes/base.dart:
--------------------------------------------------------------------------------
1 | abstract class BaseWebSocketCode {
2 | int get identifier;
3 | String get message;
4 | }
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable/background.png
--------------------------------------------------------------------------------
/assets/images/intro/intro_obs_websocket_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/intro/intro_obs_websocket_page.png
--------------------------------------------------------------------------------
/assets/images/kounex_logo_ai_no_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/kounex_logo_ai_no_background.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/icon.png
--------------------------------------------------------------------------------
/lib/types/exceptions/network.dart:
--------------------------------------------------------------------------------
1 | class NotInWLANException implements Exception {}
2 |
3 | class NoNetworkException implements Exception {}
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-hdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-mdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-v21/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xxxhdpi/splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/assets/images/intro/intro_obs_websocket_download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/intro/intro_obs_websocket_download.png
--------------------------------------------------------------------------------
/assets/images/intro/intro_obs_websocket_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/assets/images/intro/intro_obs_websocket_settings.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night/background.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-hdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-mdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-v21/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night-v21/background.png
--------------------------------------------------------------------------------
/lib/types/enums/scene_item_icon.dart:
--------------------------------------------------------------------------------
1 | enum SceneItemIcon { AudioInput, AudioOutput, DisplayCapture }
2 |
3 | extension SceneItemIconFunctions on SceneItemIcon {}
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xhdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xxhdpi/android12splash.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/featuredGraphics.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/featuredGraphics.png
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/macos/Flutter/Flutter-Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "ephemeral/Flutter-Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xxxhdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #141C24
4 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-hdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night-hdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-mdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night-mdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-xhdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night-xhdpi/android12splash.png
--------------------------------------------------------------------------------
/flutterw:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euo pipefail
3 |
4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
5 |
6 | export PATH="$DIR/vendor/flutter/bin:$PATH"
7 | exec flutter $@
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
--------------------------------------------------------------------------------
/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kounex/obs_blade/HEAD/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png
--------------------------------------------------------------------------------
/lib/types/classes/default_filter.dart:
--------------------------------------------------------------------------------
1 | class DefaultFilter {
2 | String filterKind;
3 | Map filterSettings;
4 |
5 | DefaultFilter(this.filterKind, this.filterSettings);
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/obs_blade/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.kounex.obsBlade
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/kounex/obs_blade/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.kounex.obs_blade
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
--------------------------------------------------------------------------------
/linux/main.cc:
--------------------------------------------------------------------------------
1 | #include "my_application.h"
2 |
3 | int main(int argc, char** argv) {
4 | g_autoptr(MyApplication) app = my_application_new();
5 | return g_application_run(G_APPLICATION(app), argc, argv);
6 | }
7 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/web/splash/splash.js:
--------------------------------------------------------------------------------
1 | function removeSplashFromWeb() {
2 | document.getElementById("splash")?.remove();
3 | document.getElementById("splash-branding")?.remove();
4 | document.body.style.background = "transparent";
5 | }
6 |
--------------------------------------------------------------------------------
/lib/types/enums/order.dart:
--------------------------------------------------------------------------------
1 | enum Order {
2 | Ascending,
3 | Descending,
4 | }
5 |
6 | extension FilterOrderFunctions on Order {
7 | String get text => const {
8 | Order.Ascending: 'Asc.',
9 | Order.Descending: 'Desc.',
10 | }[this]!;
11 | }
12 |
--------------------------------------------------------------------------------
/lib/models/hotkey.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | import 'type_ids.dart';
4 |
5 | part 'hotkey.g.dart';
6 |
7 | @HiveType(typeId: TypeIDs.Hotkey)
8 | class Hotkey extends HiveObject {
9 | @HiveField(0)
10 | String name;
11 |
12 | Hotkey(this.name);
13 | }
14 |
--------------------------------------------------------------------------------
/macos/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | @NSApplicationMain
5 | class AppDelegate: FlutterAppDelegate {
6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
7 | return true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
7 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
4 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"
--------------------------------------------------------------------------------
/macos/Runner/Release.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_input_mute.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the audio mute state of an input.
4 | class GetInputMuteResponse extends BaseResponse {
5 | GetInputMuteResponse(super.json);
6 |
7 | /// Whether the input is muted
8 | bool get inputMuted => json['inputMuted'];
9 | }
10 |
--------------------------------------------------------------------------------
/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/types/classes/session.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:web_socket_channel/io.dart';
4 |
5 | import '../../models/connection.dart';
6 |
7 | class Session {
8 | IOWebSocketChannel socket;
9 | Stream? socketStream;
10 | Connection connection;
11 |
12 | Session(this.socket, this.connection);
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/profile_list_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The profile list has changed.
4 | class ProfileListChangedEvent extends BaseEvent {
5 | ProfileListChangedEvent(super.json);
6 |
7 | /// Updated list of profiles
8 | List get profiles => List.from(this.json['profiles']);
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_hotkey_list.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets an array of all hotkey names in OBS
4 | class GetHotkeyListResponse extends BaseResponse {
5 | GetHotkeyListResponse(super.json);
6 |
7 | /// Array of hotkey names
8 | List get hotkeys => List.from(this.json['hotkeys']);
9 | }
10 |
--------------------------------------------------------------------------------
/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/switch_transition.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The active transition has been changed
4 | class SwitchTransitionEvent extends BaseEvent {
5 | SwitchTransitionEvent(super.json);
6 |
7 | /// The name of the new active transition
8 | String get transitionName => this.json['transition-name'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_virtual_cam_status.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the status of the virtualcam output.
4 | class GetVirtualCamStatusResponse extends BaseResponse {
5 | GetVirtualCamStatusResponse(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 | }
10 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/changelogs/1.txt:
--------------------------------------------------------------------------------
1 | Hello again! 2023 starting off with new features requested by the community:
2 |
3 | - Volume meters
4 | - Fullscreen preview feature
5 | - Fixed twitch chat
6 | - Performance improvements
7 |
8 | As always: feel free to drop some feature or bug requests via the GitHub page of OBS Blade or contact me via email! :)
9 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_studio_mode_enabled.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets whether studio is enabled.
4 | class GetStudioModeEnabledResponse extends BaseResponse {
5 | GetStudioModeEnabledResponse(super.json);
6 |
7 | /// Whether studio mode is enabled
8 | bool get studioModeEnabled => this.json['studioModeEnabled'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/studio_mode_switched.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Studio mode has been enabled or disabled.
4 | class StudioModeStateChangedEvent extends BaseEvent {
5 | StudioModeStateChangedEvent(super.json);
6 |
7 | /// True == Enabled, False == Disabled
8 | bool get studioModeEnabled => this.json['studioModeEnabled'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_record_directory.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the current directory that the record output is set to.
4 | class GetRecordDirectoryResponse extends BaseResponse {
5 | GetRecordDirectoryResponse(super.json);
6 |
7 | /// Output directory
8 | String get recordDirectory => this.json['recordDirectory'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/current_preview_scene_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The current preview scene has changed.
4 | class CurrentPreviewSceneChangedEvent extends BaseEvent {
5 | CurrentPreviewSceneChangedEvent(super.json);
6 |
7 | /// Name of the scene that was switched to
8 | String get sceneName => this.json['sceneName'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/current_program_scene_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The current program scene has changed.
4 | class CurrentProgramSceneChangedEvent extends BaseEvent {
5 | CurrentProgramSceneChangedEvent(super.json);
6 |
7 | /// Name of the scene that was switched to
8 | String get sceneName => this.json['sceneName'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_replay_buffer_status.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Get the status of the OBS replay buffer
4 | class GetReplayBufferStatusResponse extends BaseResponse {
5 | GetReplayBufferStatusResponse(super.json);
6 |
7 | /// Current recording status
8 | bool? get isReplayBufferActive => this.json['isReplayBufferActive'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/current_profile_changed.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/events/base.dart';
2 |
3 | /// The current profile has changed.
4 | class CurrentProfileChangedEvent extends BaseEvent {
5 | CurrentProfileChangedEvent(super.json);
6 |
7 | /// Name of the new profile
8 | String get profileName => this.json['profileName'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/extensions/string.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | extension StringStuff on String {
4 | Color hexToColor() {
5 | final buffer = StringBuffer();
6 | if (this.length == 6 || this.length == 7) buffer.write('ff');
7 | buffer.write(this.replaceFirst('#', ''));
8 | return Color(int.parse(buffer.toString(), radix: 16));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/android/app/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/windows/.gitignore:
--------------------------------------------------------------------------------
1 | flutter/ephemeral/
2 |
3 | # Visual Studio user-specific files.
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # Visual Studio build-related files.
10 | x64/
11 | x86/
12 |
13 | # Visual Studio cache files
14 | # files ending in .cache can be ignored
15 | *.[Cc]ache
16 | # but keep track of directories ending in .cache
17 | !*.[Cc]ache/
18 |
--------------------------------------------------------------------------------
/lib/utils/icons/custom_flutter_icons.dart:
--------------------------------------------------------------------------------
1 | /// Custom icons made on fluttericon.com
2 | import 'package:flutter/widgets.dart';
3 |
4 | class CustomFlutterIcons {
5 | static const _kFontFam = 'CustomFlutterIcons';
6 | static const String? _kFontPkg = null;
7 |
8 | static const IconData owncast_logo =
9 | IconData(0xe800, fontFamily: _kFontFam, fontPackage: _kFontPkg);
10 | }
11 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | project.evaluationDependsOn(':app')
12 | }
13 |
14 | tasks.register("clean", Delete) {
15 | delete rootProject.buildDir
16 | }
17 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/scene_collection_list_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The scene collection list has changed.
4 | class SceneCollectionListChangedEvent extends BaseEvent {
5 | SceneCollectionListChangedEvent(super.json);
6 |
7 | /// Updated list of scene collections
8 | List get sceneCollections => List.from(this.json['sceneCollections']);
9 | }
10 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_input_default_settings.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the default settings for an input kind.
4 | class GetInputDefaultSettingsResponse extends BaseResponse {
5 | GetInputDefaultSettingsResponse(super.json);
6 |
7 | /// Object of default settings for the input kind
8 | dynamic get defaultInputSettings => this.json['defaultInputSettings'];
9 | }
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/transition_duration_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The current scene transition duration has changed.
4 | class CurrentSceneTransitionDurationChangedEvent extends BaseEvent {
5 | CurrentSceneTransitionDurationChangedEvent(super.json);
6 |
7 | /// Transition duration in milliseconds
8 | int get transitionDuration => this.json['transitionDuration'];
9 | }
10 |
--------------------------------------------------------------------------------
/linux/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void fl_register_plugins(FlPluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void RegisterPlugins(flutter::PluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/input_name_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The name of an input has changed.
4 | class InputNameChangedEvent extends BaseEvent {
5 | InputNameChangedEvent(super.json);
6 |
7 | /// Old name of the input
8 | String get oldInputName => this.json['oldInputName'];
9 |
10 | /// New name of the input
11 | String get inputName => this.json['inputName'];
12 | }
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-night-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/current_scene_transition_changed.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/events/base.dart';
2 |
3 | /// The current scene transition has changed.
4 | class CurrentSceneTransitionChangedEvent extends BaseEvent {
5 | CurrentSceneTransitionChangedEvent(super.json);
6 |
7 | /// Name of the new transition
8 | String get transitionName => this.json['transitionName'];
9 | }
10 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/input_mute_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// An input's mute state has changed.
4 | class InputMuteStateChangedEvent extends BaseEvent {
5 | InputMuteStateChangedEvent(super.json);
6 |
7 | /// Name of the input
8 | String get inputName => this.json['inputName'];
9 |
10 | /// Whether the input is muted
11 | bool get inputMuted => this.json['inputMuted'];
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_input_list.dart:
--------------------------------------------------------------------------------
1 | import '../../api/input.dart';
2 | import 'base.dart';
3 |
4 | /// Gets an array of all inputs in OBS.
5 | class GetInputListResponse extends BaseResponse {
6 | GetInputListResponse(super.json);
7 |
8 | /// Array of inputs
9 | List get inputs => (this.json['inputs'] as List)
10 | .map((input) => Input.fromJson(input))
11 | .toList();
12 | }
13 |
--------------------------------------------------------------------------------
/macos/Runner/DebugProfile.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.cs.allow-jit
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_input_volume.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the current volume setting of an input.
4 | class GetInputVolumeResponse extends BaseResponse {
5 | GetInputVolumeResponse(super.json);
6 |
7 | /// Volume setting in mul
8 | double get inputVolumeMul => this.json['inputVolumeMul'];
9 |
10 | /// Volume setting in dB
11 | double get inputVolumeDb => this.json['inputVolumeDb'];
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/stream_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The state of the stream output has changed.
4 | class StreamStateChangedEvent extends BaseEvent {
5 | StreamStateChangedEvent(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 |
10 | /// The specific state of the output
11 | String get outputState => this.json['outputState'];
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_input_audio_sync_offset.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the audio sync offset of an input.
4 | ///
5 | /// Note: The audio sync offset can be negative too!
6 | class GetInputAudioSyncOffsetResponse extends BaseResponse {
7 | GetInputAudioSyncOffsetResponse(super.json);
8 |
9 | /// Audio sync offset in milliseconds
10 | int get inputAudioSyncOffset => this.json['inputAudioSyncOffset'];
11 | }
12 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_source_filter_default_settings.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the default settings for a filter kind.
4 | class GetSourceFilterDefaultSettingsResponse extends BaseResponse {
5 | GetSourceFilterDefaultSettingsResponse(super.json);
6 |
7 | /// Object of default settings for the filter kind
8 | Map get defaultFilterSettings =>
9 | this.json['defaultFilterSettings'];
10 | }
11 |
--------------------------------------------------------------------------------
/lib/shared/general/base/divider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class BaseDivider extends StatelessWidget {
4 | final double? height;
5 |
6 | const BaseDivider({super.key, this.height});
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Divider(
11 | color: Theme.of(context).dividerColor.withOpacity(0.4),
12 | height: this.height ?? 1.0,
13 | thickness: 0.0,
14 | );
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/transition_list_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The list of available transitions has been modified. Transitions have been added, removed, or renamed
4 | class TransitionListChangedEvent extends BaseEvent {
5 | TransitionListChangedEvent(super.json);
6 |
7 | /// Transitions list
8 | List get transitions => List.from(
9 | this.json['transitions'].map((transition) => transition['name']));
10 | }
11 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_profile_list.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets an array of all profiles
4 | class GetProfileListResponse extends BaseResponse {
5 | GetProfileListResponse(super.json);
6 |
7 | /// The name of the current profile
8 | String get currentProfileName => this.json['currentProfileName'];
9 |
10 | /// Array of all available profiles
11 | List get profiles => List.from(this.json['profiles']);
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/virtual_cam_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The state of the virtualcam output has changed.
4 | class VirtualCamStateChangedEvent extends BaseEvent {
5 | VirtualCamStateChangedEvent(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 |
10 | /// The specific state of the output
11 | String get outputState => this.json['outputState'];
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/replay_buffer_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The state of the replay buffer output has changed.
4 | class ReplayBufferStateChangedEvent extends BaseEvent {
5 | ReplayBufferStateChangedEvent(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 |
10 | /// The specific state of the output
11 | String get outputState => this.json['outputState'];
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_source_filter_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/api/filter.dart';
2 |
3 | import 'base.dart';
4 |
5 | /// Gets an array of all of a source's filters.
6 | class GetSourceFilterListResponse extends BaseResponse {
7 | GetSourceFilterListResponse(super.json);
8 |
9 | /// Array of filters
10 | List get filters =>
11 | List.from(this.json['filters'].map((filter) => Filter.fromJson(filter)));
12 | }
13 |
--------------------------------------------------------------------------------
/linux/my_application.h:
--------------------------------------------------------------------------------
1 | #ifndef FLUTTER_MY_APPLICATION_H_
2 | #define FLUTTER_MY_APPLICATION_H_
3 |
4 | #include
5 |
6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
7 | GtkApplication)
8 |
9 | /**
10 | * my_application_new:
11 | *
12 | * Creates a new Flutter-based application.
13 | *
14 | * Returns: a new #MyApplication.
15 | */
16 | MyApplication* my_application_new();
17 |
18 | #endif // FLUTTER_MY_APPLICATION_H_
19 |
--------------------------------------------------------------------------------
/lib/utils/general_helper.dart:
--------------------------------------------------------------------------------
1 | import '../models/enums/log_level.dart';
2 |
3 | class GeneralHelper {
4 | static void advLog(
5 | Object? obj, {
6 | LogLevel level = LogLevel.Info,
7 | bool includeInLogs = false,
8 | }) {
9 | String inLog = includeInLogs ? '[ON]' : '[OFF]';
10 | // ignore: avoid_print
11 | print(obj == null
12 | ? '${LogLevel.Warning.prefix}$inLog ${obj.runtimeType} is null!'
13 | : '${level.prefix}$inLog $obj');
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/macos/Runner/MainFlutterWindow.swift:
--------------------------------------------------------------------------------
1 | import Cocoa
2 | import FlutterMacOS
3 |
4 | class MainFlutterWindow: NSWindow {
5 | override func awakeFromNib() {
6 | let flutterViewController = FlutterViewController.init()
7 | let windowFrame = self.frame
8 | self.contentViewController = flutterViewController
9 | self.setFrame(windowFrame, display: true)
10 |
11 | RegisterGeneratedPlugins(registry: flutterViewController)
12 |
13 | super.awakeFromNib()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/batch_responses/filter_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/batch_responses/base.dart';
2 | import 'package:obs_blade/types/classes/stream/responses/get_source_filter_list.dart';
3 |
4 | class FilterListBatchResponse extends BaseBatchResponse {
5 | FilterListBatchResponse(super.json);
6 |
7 | Iterable get filterLists => this
8 | .responses
9 | .map((response) => GetSourceFilterListResponse(response.jsonRAW));
10 | }
11 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/input_audio_sync_offset_changed.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/events/base.dart';
2 |
3 | /// The sync offset of an input has changed.
4 | class InputAudioSyncOffsetChangedEvent extends BaseEvent {
5 | InputAudioSyncOffsetChangedEvent(super.json);
6 |
7 | /// Name of the input
8 | String get inputName => this.json['inputName'];
9 |
10 | /// New sync offset in milliseconds
11 | int get inputAudioSyncOffset => this.json['inputAudioSyncOffset'];
12 | }
13 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "background.png",
5 | "idiom" : "universal"
6 | },
7 | {
8 | "appearances" : [
9 | {
10 | "appearance" : "luminosity",
11 | "value" : "dark"
12 | }
13 | ],
14 | "filename" : "darkbackground.png",
15 | "idiom" : "universal"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/windows/runner/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Runner.rc
4 | //
5 | #define IDI_APP_ICON 101
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 102
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1001
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/lib/types/classes/api/input_channel.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'input_channel.freezed.dart';
4 | part 'input_channel.g.dart';
5 |
6 | @freezed
7 | class InputChannel with _$InputChannel {
8 | const factory InputChannel({
9 | required double? current,
10 | required double? average,
11 | required double? potential,
12 | }) = _InputChannel;
13 |
14 | factory InputChannel.fromJson(Map json) =>
15 | _$InputChannelFromJson(json);
16 | }
17 |
--------------------------------------------------------------------------------
/lib/types/classes/api/scene.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'scene.freezed.dart';
4 | part 'scene.g.dart';
5 |
6 | @freezed
7 | class Scene with _$Scene {
8 | const factory Scene({
9 | /// Name of the currently active scene
10 | required String sceneName,
11 |
12 | /// Ordered list of the current scene's source items
13 | required int sceneIndex,
14 | }) = _Scene;
15 |
16 | factory Scene.fromJson(Map json) => _$SceneFromJson(json);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/input_volume_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// An input's volume level has changed.
4 | class InputVolumeChangedEvent extends BaseEvent {
5 | InputVolumeChangedEvent(super.json);
6 |
7 | /// Name of the input
8 | String get inputName => this.json['inputName'];
9 |
10 | /// New volume level in multimap
11 | double get inputVolumeMul => this.json['inputVolumeMul'];
12 |
13 | /// New volume level in dB
14 | double get inputVolumeDb => this.json['inputVolumeDb'];
15 | }
16 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_scene_item_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/api/scene_item.dart';
2 |
3 | import 'base.dart';
4 |
5 | /// Gets a list of all scene items in a scene.
6 | class GetSceneItemListResponse extends BaseResponse {
7 | GetSceneItemListResponse(super.json);
8 |
9 | /// Array of scene items in the scene
10 | Iterable get sceneItems =>
11 | (this.json['sceneItems'] as List)
12 | .map((sceneItem) => SceneItem.fromJson(sceneItem));
13 | }
14 |
--------------------------------------------------------------------------------
/lib/models/enums/scene_item_type.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 | import '../type_ids.dart';
3 |
4 | part 'scene_item_type.g.dart';
5 |
6 | @HiveType(typeId: TypeIDs.SceneItemType)
7 | enum SceneItemType {
8 | /// Source related to items in the "Sources" list of OBS.
9 | /// Basically the "actual" scene items
10 | @HiveField(0)
11 | Source,
12 |
13 | /// Items which result from entries in the "Sources" list but
14 | /// have a designated entry in the "Audio Mixer" list of OBS
15 | @HiveField(1)
16 | Audio,
17 | }
18 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "LaunchImage.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "LaunchImage@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "filename" : "LaunchImage@3x.png",
15 | "idiom" : "universal",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "author" : "xcode",
21 | "version" : 1
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/types/classes/api/filter.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'filter.freezed.dart';
4 | part 'filter.g.dart';
5 |
6 | @freezed
7 | class Filter with _$Filter {
8 | const factory Filter({
9 | required bool filterEnabled,
10 | required int filterIndex,
11 | required String filterKind,
12 | required String filterName,
13 | required Map filterSettings,
14 | }) = _Filter;
15 |
16 | factory Filter.fromJson(Map json) => _$FilterFromJson(json);
17 | }
18 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_scene_collection_list.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets an array of all scene collections
4 | class GetSceneCollectionListResponse extends BaseResponse {
5 | GetSceneCollectionListResponse(super.json);
6 |
7 | /// The name of the current scene collection
8 | String get currentSceneCollectionName =>
9 | this.json['currentSceneCollectionName'];
10 |
11 | /// Array of all available scene collections
12 | List get sceneCollections => List.from(this.json['sceneCollections']);
13 | }
14 |
--------------------------------------------------------------------------------
/.github/workflows/flutter-analyze.yml:
--------------------------------------------------------------------------------
1 | name: flutter analyze
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 | workflow_dispatch:
11 |
12 | jobs:
13 | analyze:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Checkout code
17 | uses: actions/checkout@v2
18 | - name: Set up Flutter
19 | uses: subosito/flutter-action@v1
20 | - run: flutter pub get
21 | - name: Run flutter analyze
22 | uses: invertase/github-action-dart-analyzer@v1
--------------------------------------------------------------------------------
/lib/views/dashboard/widgets/dashboard_content/scene_content/placeholder_scene_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PlaceholderSceneItem extends StatelessWidget {
4 | final String text;
5 |
6 | const PlaceholderSceneItem({
7 | super.key,
8 | required this.text,
9 | });
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Padding(
14 | padding: const EdgeInsets.only(top: 12.0),
15 | child: Center(
16 | child: Text(this.text),
17 | ),
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/types/interfaces/local_persistance.dart:
--------------------------------------------------------------------------------
1 | /// Interface which serves as an abstraction layer for persistance tasks.
2 | /// Every class which implements this has to provide a [load] and [save] function
3 | /// which is what we actually need and do (for now) for this task. The actual
4 | /// implementation can then be handeled individually (right now hive)
5 | abstract class Persistance {
6 | /// Persist [item], should return a bool to indicate if the task was successful
7 | bool save(T item);
8 |
9 | /// Load items as List
10 | List load();
11 | }
12 |
--------------------------------------------------------------------------------
/lib/models/purchased_tip.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 | import 'type_ids.dart';
3 |
4 | part 'purchased_tip.g.dart';
5 |
6 | @HiveType(typeId: TypeIDs.PurchasedTip)
7 | class PurchasedTip extends HiveObject {
8 | @HiveField(0)
9 | int timestampMS;
10 |
11 | @HiveField(1)
12 | String id;
13 |
14 | @HiveField(2)
15 | String name;
16 |
17 | @HiveField(3)
18 | String price;
19 |
20 | @HiveField(4)
21 | String currencySymbol;
22 |
23 | PurchasedTip(
24 | this.timestampMS, this.id, this.name, this.price, this.currencySymbol);
25 | }
26 |
--------------------------------------------------------------------------------
/lib/models/type_ids.dart:
--------------------------------------------------------------------------------
1 | class TypeIDs {
2 | static const int Connection = 0;
3 | static const int PastStreamData = 1;
4 | static const int CustomTheme = 2;
5 | static const int HiddenSceneItem = 3;
6 | static const int ChatType = 4;
7 | static const int SceneItemType = 5;
8 | static const int HiddenScene = 6;
9 | static const int AppLog = 7;
10 | static const int LogLevel = 8;
11 | static const int PurchasedTip = 9;
12 | static const int PastRecordData = 10;
13 | static const int Hotkey = 11;
14 | static const int DashboardElement = 12;
15 | }
16 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/batch_responses/filter_default_settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/batch_responses/base.dart';
2 | import 'package:obs_blade/types/classes/stream/responses/get_source_filter_default_settings.dart';
3 |
4 | class FilterDefaultSettingsResponse extends BaseBatchResponse {
5 | FilterDefaultSettingsResponse(super.json);
6 |
7 | Iterable get defaultSettings =>
8 | this.responses.map((response) =>
9 | GetSourceFilterDefaultSettingsResponse(response.jsonRAW));
10 | }
11 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/current_scene_collection_changed.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/events/base.dart';
2 |
3 | /// The current scene collection has changed.
4 | ///
5 | /// Note: If polling has been paused during CurrentSceneCollectionChanging,
6 | /// this is the que to restart polling.
7 | class CurrentSceneCollectionChangedEvent extends BaseEvent {
8 | CurrentSceneCollectionChangedEvent(super.json);
9 |
10 | /// Name of the current scene collection
11 | String get sceneCollectionName => this.json['sceneCollectionName'];
12 | }
13 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/stats_entry_placeholder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class StatsEntryPlaceholder extends StatelessWidget {
4 | final String text;
5 |
6 | const StatsEntryPlaceholder({super.key, required this.text});
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Center(
11 | child: Padding(
12 | padding: const EdgeInsets.all(24.0),
13 | child: Text(
14 | this.text,
15 | textAlign: TextAlign.center,
16 | ),
17 | ),
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/source_filter_enable_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// A source filter's enable state has changed.
4 | class SourceFilterEnableStateChangedEvent extends BaseEvent {
5 | SourceFilterEnableStateChangedEvent(super.json);
6 |
7 | /// Name of the source the filter is on
8 | String get sourceName => this.json['sourceName'];
9 |
10 | /// Name of the filter
11 | String get filterName => this.json['filterName'];
12 |
13 | /// Whether the filter is enabled
14 | bool get filterEnabled => this.json['filterEnabled'];
15 | }
16 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/record_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// The state of the record output has changed.
4 | class RecordStateChangedEvent extends BaseEvent {
5 | RecordStateChangedEvent(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 |
10 | /// The specific state of the output
11 | String get outputState => this.json['outputState'];
12 |
13 | /// File name for the saved recording, if record stopped. null otherwise
14 | String? get outputPath => this.json['outputPath'];
15 | }
16 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/scene_item_enable_state_changed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// A scene item's enable state has changed.
4 | class SceneItemEnableStateChangedEvent extends BaseEvent {
5 | SceneItemEnableStateChangedEvent(super.json);
6 |
7 | /// Name of the scene the item is in
8 | String get sceneName => this.json['sceneName'];
9 |
10 | /// Numeric ID of the scene item
11 | int get sceneItemId => this.json['sceneItemId'];
12 |
13 | /// Whether the scene item is enabled (visible)
14 | bool get sceneItemEnabled => this.json['sceneItemEnabled'];
15 | }
16 |
--------------------------------------------------------------------------------
/lib/models/app_log.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 | import 'type_ids.dart';
3 |
4 | import 'enums/log_level.dart';
5 |
6 | part 'app_log.g.dart';
7 |
8 | @HiveType(typeId: TypeIDs.AppLog)
9 | class AppLog extends HiveObject {
10 | @HiveField(0)
11 | int timestampMS;
12 |
13 | @HiveField(1)
14 | LogLevel level;
15 |
16 | @HiveField(2)
17 | String entry;
18 |
19 | @HiveField(3)
20 | String? stackTrace;
21 |
22 | @HiveField(4)
23 | bool manually;
24 |
25 | AppLog(this.timestampMS, this.level, this.entry,
26 | [this.stackTrace, this.manually = false]);
27 | }
28 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/batch_responses/screenshot.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/batch_responses/base.dart';
2 | import 'package:obs_blade/types/classes/stream/responses/get_source_screenshot.dart';
3 | import 'package:obs_blade/types/enums/request_type.dart';
4 |
5 | class ScreenshotBatchResponse extends BaseBatchResponse {
6 | ScreenshotBatchResponse(super.json);
7 |
8 | GetSourceScreenshotResponse get getSourceScreenshotResponse => this.response(
9 | RequestType.GetSourceScreenshot,
10 | (jsonRAW) => GetSourceScreenshotResponse(jsonRAW),
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/scene_item_list_reindexed.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/api/scene_item.dart';
2 |
3 | import 'base.dart';
4 |
5 | /// A scene's item list has been reindexed.
6 | class SceneItemListReindexedEvent extends BaseEvent {
7 | SceneItemListReindexedEvent(super.json);
8 |
9 | /// Name of the scene
10 | String get sceneName => this.json['sceneName'];
11 |
12 | /// Array of scene item objects
13 | List get sceneItems => (this.json['sceneItems'] as List)
14 | .map((sceneItem) => SceneItem.fromJson(sceneItem))
15 | .toList();
16 | }
17 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/scene_item_removed.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// A scene item has been removed.
4 | ///
5 | /// This event is not emitted when the scene the item is in is removed.
6 | class SceneItemRemovedEvent extends BaseEvent {
7 | SceneItemRemovedEvent(super.json);
8 |
9 | /// Name of the scene the item was removed from
10 | String get sceneName => this.json['sceneName'];
11 |
12 | /// Name of the underlying source (input/scene)
13 | String get sourceName => this.json['sourceName'];
14 |
15 | /// Numeric ID of the scene item
16 | int get sceneItemId => this.json['sceneItemId'];
17 | }
18 |
--------------------------------------------------------------------------------
/lib/types/extensions/list.dart:
--------------------------------------------------------------------------------
1 | /// utils should be used for generic stuff which can be useful from everywhere in the
2 | /// app. This example is a extension method for the List type so every List object can
3 | /// use this new method. Another examples would be static functions doing stuff you would
4 | /// do on several places, so summarize them in a util class!
5 | extension ListStuff on Iterable {
6 | Iterable mapIndexed(T Function(E element, int index) f) sync* {
7 | var index = 0;
8 |
9 | for (final item in this) {
10 | yield f(item, index);
11 | index = index + 1;
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/scene_item_created.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// A scene item has been created.
4 | class SceneItemAddedEvent extends BaseEvent {
5 | SceneItemAddedEvent(super.json);
6 |
7 | /// Name of the scene the item was added to
8 | String get sceneName => this.json['sceneName'];
9 |
10 | /// Name of the underlying source (input/scene)
11 | String get sourceName => this.json['sourceName'];
12 |
13 | /// Numeric ID of the scene item
14 | int get sceneItemId => this.json['sceneItemId'];
15 |
16 | /// Index position of the item
17 | int get sceneItemIndex => this.json['sceneItemIndex'];
18 | }
19 |
--------------------------------------------------------------------------------
/lib/views/settings/custom_theme/widgets/color_picker/color_label.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ColorLabel extends StatelessWidget {
4 | final String label;
5 | final double width;
6 |
7 | const ColorLabel({
8 | super.key,
9 | required this.label,
10 | this.width = 24.0,
11 | });
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return SizedBox(
16 | width: this.width,
17 | child: Text(
18 | this.label,
19 | textAlign: TextAlign.center,
20 | style: Theme.of(context).textTheme.titleMedium,
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/models/connection.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | import 'type_ids.dart';
4 |
5 | part 'connection.g.dart';
6 |
7 | @HiveType(typeId: TypeIDs.Connection)
8 | class Connection extends HiveObject {
9 | @HiveField(0)
10 | String? name;
11 |
12 | @HiveField(1)
13 | String host;
14 |
15 | @HiveField(2)
16 | String? ssid;
17 |
18 | @HiveField(3)
19 | int? port;
20 |
21 | @HiveField(4)
22 | String? pw;
23 |
24 | @HiveField(5)
25 | bool? isDomain;
26 |
27 | String? challenge;
28 | String? salt;
29 | bool? reachable;
30 |
31 | Connection(this.host, this.port, [this.pw, this.isDomain]);
32 | }
33 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_group_scene_item_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/api/scene_item.dart';
2 |
3 | import 'base.dart';
4 |
5 | /// Basically GetSceneItemList, but for groups.
6 | ///
7 | /// Using groups at all in OBS is discouraged, as they are very broken under the hood.
8 | class GetGroupSceneItemListResponse extends BaseResponse {
9 | GetGroupSceneItemListResponse(super.json);
10 |
11 | /// Array of scene items in the group
12 | List get sceneItems => (this.json['sceneItems'] as List)
13 | .map((sceneItem) => SceneItem.fromJson(sceneItem))
14 | .toList();
15 | }
16 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_source_screenshot.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets a Base64-encoded screenshot of a source.
4 | ///
5 | /// The imageWidth and imageHeight parameters are treated as "scale to inner", meaning the smallest
6 | /// ratio will be used and the aspect ratio of the original resolution is kept. If imageWidth and imageHeight
7 | /// are not specified, the compressed image will use the full resolution of the source.
8 | class GetSourceScreenshotResponse extends BaseResponse {
9 | GetSourceScreenshotResponse(super.json);
10 |
11 | /// Base64-encoded screenshot
12 | String get imageData => this.json['imageData'];
13 | }
14 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/Warnings.xcconfig:
--------------------------------------------------------------------------------
1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
2 | GCC_WARN_UNDECLARED_SELECTOR = YES
3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
6 | CLANG_WARN_PRAGMA_PACK = YES
7 | CLANG_WARN_STRICT_PROTOTYPES = YES
8 | CLANG_WARN_COMMA = YES
9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES
10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
12 | GCC_WARN_SHADOW = YES
13 | CLANG_WARN_UNREACHABLE_CODE = YES
14 |
--------------------------------------------------------------------------------
/windows/runner/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.15)
2 | project(runner LANGUAGES CXX)
3 |
4 | add_executable(${BINARY_NAME} WIN32
5 | "flutter_window.cpp"
6 | "main.cpp"
7 | "utils.cpp"
8 | "win32_window.cpp"
9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
10 | "Runner.rc"
11 | "runner.exe.manifest"
12 | )
13 | apply_standard_settings(${BINARY_NAME})
14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
17 | add_dependencies(${BINARY_NAME} flutter_assemble)
18 |
--------------------------------------------------------------------------------
/lib/shared/general/themed/cupertino_scaffold.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
3 |
4 | class ThemedCupertinoScaffold extends StatelessWidget {
5 | final Widget body;
6 |
7 | const ThemedCupertinoScaffold({
8 | super.key,
9 | required this.body,
10 | });
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return CupertinoScaffold(
15 | transitionBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
16 | body: IconTheme(
17 | data: Theme.of(context).iconTheme,
18 | child: this.body,
19 | ),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/save_source_screenshot.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Saves a screenshot of a source to the filesystem.
4 | /// The imageWidth and imageHeight parameters are treated as "scale to inner",
5 | /// meaning the smallest ratio will be used and the aspect ratio of the
6 | /// original resolution is kept. If imageWidth and imageHeight are not
7 | /// specified, the compressed image will use the full resolution of the source.
8 | class SaveSourceScreenshotResponse extends BaseResponse {
9 | SaveSourceScreenshotResponse(super.json);
10 |
11 | /// Base64-encoded screenshot
12 | String get imageData => this.json['imageData'];
13 | }
14 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/current_scene_collection_changing.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/events/base.dart';
2 |
3 | /// The current scene collection has begun changing.
4 | ///
5 | /// Note: We recommend using this event to trigger a pause of all polling requests,
6 | /// as performing any requests during a scene collection change is considered undefined
7 | /// behavior and can cause crashes!
8 | class CurrentSceneCollectionChangingEvent extends BaseEvent {
9 | CurrentSceneCollectionChangingEvent(super.json);
10 |
11 | /// Name of the current scene collection
12 | String get sceneCollectionName => this.json['sceneCollectionName'];
13 | }
14 |
--------------------------------------------------------------------------------
/macos/Runner/Configs/AppInfo.xcconfig:
--------------------------------------------------------------------------------
1 | // Application-level settings for the Runner target.
2 | //
3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
4 | // future. If not, the values below would default to using the project name when this becomes a
5 | // 'flutter create' template.
6 |
7 | // The application's name. By default this is also the title of the Flutter window.
8 | PRODUCT_NAME = obs_blade
9 |
10 | // The application's bundle identifier
11 | PRODUCT_BUNDLE_IDENTIFIER = com.kounex.obsBlade
12 |
13 | // The copyright displayed in application information
14 | PRODUCT_COPYRIGHT = Copyright © 2021 com.kounex. All rights reserved.
15 |
--------------------------------------------------------------------------------
/lib/types/interfaces/message.dart:
--------------------------------------------------------------------------------
1 | /// The Message interface is used as the abstraction layer for the
2 | /// messages which we receive from the OBS connection (currently via
3 | /// the WebSocket). Makes it easier to handle those messages with this
4 | /// abstraction since it allows me to name this instance instead of
5 | /// writing duplicate code (right now for Response and Event)
6 | abstract class Message {
7 | /// The RAW JSON object received through the WebSocket
8 | late Map jsonRAW;
9 |
10 | /// Will hold the 'responseData' / 'eventData' map directly
11 | /// from the [jsonRAW] - will make it easier to access later
12 | late Map json;
13 | }
14 |
--------------------------------------------------------------------------------
/lib/types/classes/api/input.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:obs_blade/types/classes/api/input_channel.dart';
3 |
4 | part 'input.freezed.dart';
5 | part 'input.g.dart';
6 |
7 | @freezed
8 | class Input with _$Input {
9 | const factory Input({
10 | required String? inputKind,
11 | required String? inputName,
12 | required String? unversionedInputKind,
13 | double? inputVolumeMul,
14 | double? inputVolumeDb,
15 | List? inputLevelsMul,
16 | int? syncOffset,
17 | @Default(false) bool inputMuted,
18 | }) = _Input;
19 |
20 | factory Input.fromJson(Map json) => _$InputFromJson(json);
21 | }
22 |
--------------------------------------------------------------------------------
/lib/shared/general/themed/rich_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ThemedRichText extends StatelessWidget {
4 | final List textSpans;
5 | final TextAlign? textAlign;
6 | final TextStyle? textStyle;
7 |
8 | const ThemedRichText({
9 | super.key,
10 | required this.textSpans,
11 | this.textAlign,
12 | this.textStyle,
13 | });
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return RichText(
18 | textAlign: this.textAlign ?? TextAlign.start,
19 | text: TextSpan(
20 | style: this.textStyle ?? DefaultTextStyle.of(context).style,
21 | children: this.textSpans,
22 | ),
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/types/classes/api/scene.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'scene.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$SceneImpl _$$SceneImplFromJson(Map json) => _$SceneImpl(
10 | sceneName: json['sceneName'] as String,
11 | sceneIndex: (json['sceneIndex'] as num).toInt(),
12 | );
13 |
14 | Map _$$SceneImplToJson(_$SceneImpl instance) =>
15 | {
16 | 'sceneName': instance.sceneName,
17 | 'sceneIndex': instance.sceneIndex,
18 | };
19 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | arguments=--init-script /var/folders/9b/j0bnrfwd6r36z9l64wrjpxpw0000gn/T/db3b08fc4a9ef609cb16b96b200fa13e563f396e9bb1ed0905fdab7bc3bc513b.gradle --init-script /var/folders/9b/j0bnrfwd6r36z9l64wrjpxpw0000gn/T/52cde0cfcf3e28b8b7510e992210d9614505e0911af0c190bd590d7158574963.gradle
2 | auto.sync=false
3 | build.scans.enabled=false
4 | connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
5 | connection.project.dir=
6 | eclipse.preferences.version=1
7 | gradle.user.home=
8 | java.home=/opt/homebrew/Cellar/openjdk/23/libexec/openjdk.jdk/Contents/Home
9 | jvm.arguments=
10 | offline.mode=false
11 | override.workspace.settings=true
12 | show.console.view=true
13 | show.executions.view=true
14 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_scene_list.dart:
--------------------------------------------------------------------------------
1 | import '../../api/scene.dart';
2 | import 'base.dart';
3 |
4 | /// Gets an array of all scenes in OBS.
5 | class GetSceneListResponse extends BaseResponse {
6 | GetSceneListResponse(super.json);
7 |
8 | /// Current program scene
9 | String get currentProgramSceneName => this.json['currentProgramSceneName'];
10 |
11 | /// Current preview scene. null if not in studio mode
12 | String? get currentPreviewSceneName => this.json['currentPreviewSceneName'];
13 |
14 | /// Ordered list of the current profile's scenes (See GetCurrentScene for more information)
15 | Iterable get scenes => (this.json['scenes'] as List)
16 | .map((scene) => Scene.fromJson(scene));
17 | }
18 |
--------------------------------------------------------------------------------
/windows/runner/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_UTILS_H_
2 | #define RUNNER_UTILS_H_
3 |
4 | #include
5 | #include
6 |
7 | // Creates a console for the process, and redirects stdout and stderr to
8 | // it for both the runner and the Flutter library.
9 | void CreateAndAttachConsole();
10 |
11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
12 | // encoded in UTF-8. Returns an empty std::string on failure.
13 | std::string Utf8FromUtf16(const wchar_t* utf16_string);
14 |
15 | // Gets the command line arguments passed in as a std::vector,
16 | // encoded in UTF-8. Returns an empty std::vector on failure.
17 | std::vector GetCommandLineArguments();
18 |
19 | #endif // RUNNER_UTILS_H_
20 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_record_status.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the status of the record output.
4 | class GetRecordStatusResponse extends BaseResponse {
5 | GetRecordStatusResponse(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 |
10 | /// Whether the output is paused
11 | bool? get outputPaused => this.json['outputPaused'];
12 |
13 | /// Current formatted timecode string for the output
14 | String get outputTimecode => this.json['outputTimecode'];
15 |
16 | /// Current duration in milliseconds for the output
17 | int get outputDuration => this.json['outputDuration'];
18 |
19 | /// Number of bytes sent by the output
20 | int get outputBytes => this.json['outputBytes'];
21 | }
22 |
--------------------------------------------------------------------------------
/lib/views/settings/data_management/widgets/data_block.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../../shared/general/base/card.dart';
5 | import '../../../../shared/general/column_separated.dart';
6 | import 'data_entry.dart';
7 |
8 | class DataBlock extends StatelessWidget {
9 | final List dataEntries;
10 |
11 | const DataBlock({
12 | super.key,
13 | required this.dataEntries,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return BaseCard(
19 | bottomPadding: 12.0,
20 | child: ColumnSeparated(
21 | paddingSeparator: const EdgeInsets.symmetric(vertical: 16.0),
22 | children: this.dataEntries,
23 | ),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/shared/general/themed/cupertino_sliver_navigation_bar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:obs_blade/utils/styling_helper.dart';
4 |
5 | class ThemedCupertinoSliverNavigationBar extends StatelessWidget {
6 | final Widget largeTitle;
7 |
8 | const ThemedCupertinoSliverNavigationBar({
9 | super.key,
10 | required this.largeTitle,
11 | });
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return CupertinoSliverNavigationBar(
16 | backgroundColor: StylingHelper.isApple(context)
17 | ? Theme.of(context).appBarTheme.backgroundColor
18 | : Theme.of(context).appBarTheme.backgroundColor!.withOpacity(1.0),
19 | largeTitle: this.largeTitle,
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/transition_begin.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// A transition (other than "cut") has begun
4 | class TransitionBeginEvent extends BaseEvent {
5 | TransitionBeginEvent(super.json);
6 |
7 | /// Transition name
8 | String get name => this.json['name'];
9 |
10 | /// Transition type
11 | String get type => this.json['type'];
12 |
13 | /// transition duration (in milliseconds). Will be -1 for any transition
14 | /// with a fixed duration, such as a Stinger, due to limitations
15 | /// of the OBS API
16 | int get duration => this.json['duration'];
17 |
18 | /// Source scene of the transition
19 | String get fromScene => this.json['from-scene'];
20 |
21 | /// Destination scene of the transition
22 | String get toScene => this.json['to-scene'];
23 | }
24 |
--------------------------------------------------------------------------------
/lib/views/settings/custom_theme/widgets/color_picker/color_bubble.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../utils/styling_helper.dart';
4 |
5 | class ColorBubble extends StatelessWidget {
6 | final Color color;
7 | final double size;
8 |
9 | const ColorBubble({super.key, required this.color, this.size = 24.0});
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Container(
14 | height: this.size,
15 | width: this.size,
16 | decoration: BoxDecoration(
17 | color: this.color,
18 | borderRadius: BorderRadius.circular(this.size / 2),
19 | border: Border.all(
20 | color: StylingHelper.surroundingAwareAccent(context: context),
21 | width: 0.0,
22 | ),
23 | ),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/linux/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 | #include
11 |
12 | void fl_register_plugins(FlPluginRegistry* registry) {
13 | g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
14 | fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
15 | file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
16 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
17 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
18 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
19 | }
20 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_special_inputs.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the names of all special inputs.
4 | class GetSpecialInputsResponse extends BaseResponse {
5 | GetSpecialInputsResponse(super.json);
6 |
7 | /// Name of the Desktop Audio input
8 | String? get desktop1 => this.json['desktop1'];
9 |
10 | /// Name of the Desktop Audio 2 input
11 | String? get desktop2 => this.json['desktop2'];
12 |
13 | /// Name of the Mic/Auxiliary Audio input
14 | String? get mic1 => this.json['mic1'];
15 |
16 | /// Name of the Mic/Auxiliary Audio 2 input
17 | String? get mic2 => this.json['mic2'];
18 |
19 | /// Name of the Mic/Auxiliary Audio 3 input
20 | String? get mic3 => this.json['mic3'];
21 |
22 | /// Name of the Mic/Auxiliary Audio 3 input
23 | String? get mic4 => this.json['mic4'];
24 | }
25 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 | id "com.android.application" version "7.2.1" apply false
22 | id "org.jetbrains.kotlin.android" version "2.0.20" apply false
23 | }
24 |
25 | include ":app"
--------------------------------------------------------------------------------
/lib/views/dashboard/widgets/dashboard_content/exposed_controls/hotkeys_control/section_header.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../../shared/general/base/divider.dart';
4 |
5 | class SectionHeader extends StatelessWidget {
6 | final String title;
7 |
8 | const SectionHeader({
9 | super.key,
10 | required this.title,
11 | });
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Row(
16 | children: [
17 | const Expanded(
18 | child: BaseDivider(),
19 | ),
20 | const SizedBox(width: 24.0),
21 | Text(this.title, style: Theme.of(context).textTheme.labelSmall!),
22 | const SizedBox(width: 24.0),
23 | const Expanded(
24 | child: BaseDivider(),
25 | ),
26 | ],
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_scene_transition_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/api/transition.dart';
2 |
3 | import 'base.dart';
4 |
5 | /// List of all transitions available in the frontend's dropdown menu
6 | class GetSceneTransitionListResponse extends BaseResponse {
7 | GetSceneTransitionListResponse(super.json);
8 |
9 | /// Name of the current scene transition. Can be null
10 | String? get currentSceneTransitionName =>
11 | this.json['currentSceneTransitionName'];
12 |
13 | /// Kind of the current scene transition. Can be null
14 | String? get currentSceneTransitionKind =>
15 | this.json['currentSceneTransitionKind'];
16 |
17 | /// List of transitions
18 | List get transitions => List.from(
19 | json['transitions'].map((transition) => Transition.fromJson(transition)));
20 | }
21 |
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | android
4 | Project android created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.buildship.core.gradleprojectbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.buildship.core.gradleprojectnature
16 |
17 |
18 |
19 | 1667568563408
20 |
21 | 30
22 |
23 | org.eclipse.core.resources.regexFilterMatcher
24 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/lib/types/classes/api/input_channel.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'input_channel.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$InputChannelImpl _$$InputChannelImplFromJson(Map json) =>
10 | _$InputChannelImpl(
11 | current: (json['current'] as num?)?.toDouble(),
12 | average: (json['average'] as num?)?.toDouble(),
13 | potential: (json['potential'] as num?)?.toDouble(),
14 | );
15 |
16 | Map _$$InputChannelImplToJson(_$InputChannelImpl instance) =>
17 | {
18 | 'current': instance.current,
19 | 'average': instance.average,
20 | 'potential': instance.potential,
21 | };
22 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/linux/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | file_selector_linux
7 | url_launcher_linux
8 | )
9 |
10 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
11 | )
12 |
13 | set(PLUGIN_BUNDLED_LIBRARIES)
14 |
15 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
16 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
17 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
20 | endforeach(plugin)
21 |
22 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
23 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
24 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
25 | endforeach(ffi_plugin)
26 |
--------------------------------------------------------------------------------
/lib/models/enums/chat_type.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 | import 'package:hive/hive.dart';
3 | import 'package:obs_blade/utils/icons/custom_flutter_icons.dart';
4 | import '../type_ids.dart';
5 | import '../../utils/icons/jam_icons.dart';
6 |
7 | part 'chat_type.g.dart';
8 |
9 | @HiveType(typeId: TypeIDs.ChatType)
10 | enum ChatType {
11 | @HiveField(0)
12 | Twitch,
13 |
14 | @HiveField(1)
15 | YouTube,
16 |
17 | @HiveField(2)
18 | Owncast,
19 | }
20 |
21 | extension ChatTypeFunctions on ChatType {
22 | String get text => const {
23 | ChatType.Twitch: 'Twitch',
24 | ChatType.YouTube: 'YouTube',
25 | ChatType.Owncast: 'Owncast',
26 | }[this]!;
27 |
28 | IconData get icon => const {
29 | ChatType.Twitch: JamIcons.twitch,
30 | ChatType.YouTube: JamIcons.youtube,
31 | ChatType.Owncast: CustomFlutterIcons.owncast_logo,
32 | }[this]!;
33 | }
34 |
--------------------------------------------------------------------------------
/lib/shared/general/base/constrained_box.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | const double kBaseConstrainedMaxWidth = 640.0;
4 |
5 | class BaseConstrainedBox extends StatelessWidget {
6 | final Widget? child;
7 |
8 | final double maxWidth;
9 |
10 | final bool hasBasePadding;
11 |
12 | final EdgeInsetsGeometry? padding;
13 |
14 | const BaseConstrainedBox({
15 | super.key,
16 | required this.child,
17 | this.maxWidth = kBaseConstrainedMaxWidth,
18 | this.hasBasePadding = false,
19 | this.padding,
20 | });
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return Padding(
25 | padding: this.padding ??
26 | EdgeInsets.symmetric(horizontal: this.hasBasePadding ? 24.0 : 0),
27 | child: ConstrainedBox(
28 | constraints: BoxConstraints(maxWidth: this.maxWidth),
29 | child: this.child,
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/models/enums/log_level.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hive/hive.dart';
3 | import '../type_ids.dart';
4 |
5 | part 'log_level.g.dart';
6 |
7 | @HiveType(typeId: TypeIDs.LogLevel)
8 | enum LogLevel {
9 | @HiveField(0)
10 | Info,
11 |
12 | @HiveField(1)
13 | Warning,
14 |
15 | @HiveField(2)
16 | Error,
17 | }
18 |
19 | extension LogLevelFunctions on LogLevel {
20 | String get name => {
21 | LogLevel.Info: 'Info',
22 | LogLevel.Warning: 'Warning',
23 | LogLevel.Error: 'Error',
24 | }[this]!;
25 |
26 | String get prefix => {
27 | LogLevel.Info: '[INFO]',
28 | LogLevel.Warning: '[WARNING]',
29 | LogLevel.Error: '[ERROR]',
30 | }[this]!;
31 |
32 | Color get color => {
33 | LogLevel.Info: Colors.lightBlueAccent,
34 | LogLevel.Warning: Colors.orangeAccent,
35 | LogLevel.Error: Colors.redAccent,
36 | }[this]!;
37 | }
38 |
--------------------------------------------------------------------------------
/lib/views/settings/logs/logs.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:get_it/get_it.dart';
3 |
4 | import '../../../shared/general/transculent_cupertino_navbar_wrapper.dart';
5 | import '../../../stores/views/logs.dart';
6 | import 'widgets/log_explanation.dart';
7 | import 'widgets/log_filter.dart';
8 | import 'widgets/log_grid/log_list.dart';
9 |
10 | class LogsView extends StatelessWidget {
11 | const LogsView({
12 | super.key,
13 | });
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | GetIt.instance.resetLazySingleton();
18 |
19 | return Scaffold(
20 | body: TransculentCupertinoNavBarWrapper(
21 | previousTitle: 'Settings',
22 | title: 'Logs',
23 | showScrollBar: true,
24 | listViewChildren: const [
25 | LogExplanation(),
26 | LogFilter(),
27 | LogList(),
28 | ],
29 | ),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/card_header/sort_filter_panel/filter_status.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_mobx/flutter_mobx.dart';
3 | import 'package:get_it/get_it.dart';
4 | import 'package:obs_blade/shared/general/tag_box.dart';
5 | import 'package:obs_blade/stores/views/statistics.dart';
6 |
7 | class FilterStatus extends StatelessWidget {
8 | const FilterStatus({
9 | super.key,
10 | });
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | StatisticsStore statisticsStore = GetIt.instance();
15 |
16 | return Observer(
17 | builder: (context) => TagBox(
18 | color: statisticsStore.isFilterSortActive
19 | ? Theme.of(context).colorScheme.secondary
20 | : Theme.of(context).scaffoldBackgroundColor,
21 | label: statisticsStore.isFilterSortActive ? 'ON' : 'OFF',
22 | width: 32.0,
23 | ),
24 | );
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | connectivity_plus
7 | file_selector_windows
8 | share_plus
9 | url_launcher_windows
10 | )
11 |
12 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
13 | )
14 |
15 | set(PLUGIN_BUNDLED_LIBRARIES)
16 |
17 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
18 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
19 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
21 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
22 | endforeach(plugin)
23 |
24 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
25 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
26 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
27 | endforeach(ffi_plugin)
28 |
--------------------------------------------------------------------------------
/windows/runner/runner.exe.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PerMonitorV2
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/lib/views/settings/faq/widgets/faq_block.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class FAQBlock extends StatelessWidget {
4 | final String heading;
5 | final String? text;
6 | final Widget? customBody;
7 |
8 | const FAQBlock({
9 | super.key,
10 | required this.heading,
11 | this.text,
12 | this.customBody,
13 | }) : assert(text != null || customBody != null),
14 | super();
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Column(
19 | crossAxisAlignment: CrossAxisAlignment.start,
20 | children: [
21 | Text(
22 | this.heading,
23 | style: TextStyle(
24 | fontSize: Theme.of(context).textTheme.titleLarge!.fontSize,
25 | fontWeight: FontWeight.bold,
26 | ),
27 | ),
28 | const SizedBox(height: 8.0),
29 | this.text != null ? Text(this.text!) : this.customBody!,
30 | ],
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/windows/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | void RegisterPlugins(flutter::PluginRegistry* registry) {
15 | ConnectivityPlusWindowsPluginRegisterWithRegistrar(
16 | registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
17 | FileSelectorWindowsRegisterWithRegistrar(
18 | registry->GetRegistrarForPlugin("FileSelectorWindows"));
19 | SharePlusWindowsPluginCApiRegisterWithRegistrar(
20 | registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
21 | UrlLauncherWindowsRegisterWithRegistrar(
22 | registry->GetRegistrarForPlugin("UrlLauncherWindows"));
23 | }
24 |
--------------------------------------------------------------------------------
/lib/shared/general/hive_builder.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hive_flutter/hive_flutter.dart';
3 |
4 | import '../../types/enums/hive_keys.dart';
5 | import '../../types/enums/settings_keys.dart';
6 |
7 | class HiveBuilder extends StatelessWidget {
8 | final HiveKeys hiveKey;
9 | final List? rebuildKeys;
10 | final Widget? child;
11 | final Widget Function(BuildContext context, Box box, Widget? child)
12 | builder;
13 |
14 | const HiveBuilder({
15 | super.key,
16 | required this.hiveKey,
17 | this.child,
18 | required this.builder,
19 | this.rebuildKeys,
20 | });
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return ValueListenableBuilder(
25 | valueListenable: Hive.box(this.hiveKey.name).listenable(
26 | keys: this.rebuildKeys?.map((key) => key.name).toList(),
27 | ),
28 | builder: this.builder,
29 | child: this.child,
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/views/home/widgets/connect_box/auto_discovery/result_entry.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../shared/animator/fader.dart';
4 | import '../../../../../shared/general/themed/rich_text.dart';
5 |
6 | class ResultEntry extends StatelessWidget {
7 | final String result;
8 |
9 | const ResultEntry({super.key, required this.result});
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Container(
14 | padding: const EdgeInsets.all(24.0),
15 | alignment: Alignment.center,
16 | child: Fader(
17 | child: ThemedRichText(
18 | textAlign: TextAlign.center,
19 | textSpans: [
20 | TextSpan(text: this.result),
21 | const TextSpan(
22 | text: '\n\nPull down to try again!',
23 | style: TextStyle(
24 | fontWeight: FontWeight.bold,
25 | ),
26 | ),
27 | ],
28 | ),
29 | ),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/shared/general/custom_sliver_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CustomSliverList extends StatelessWidget {
4 | final List children;
5 |
6 | final double? customTopPadding;
7 | final double? customBottomPadding;
8 |
9 | const CustomSliverList({
10 | super.key,
11 | required this.children,
12 | this.customTopPadding,
13 | this.customBottomPadding,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return SliverPadding(
19 | padding: EdgeInsets.only(
20 | top: this.customTopPadding ?? 0.0,
21 | right: MediaQuery.paddingOf(context).right,
22 | bottom: this.customBottomPadding ??
23 | (2 * kBottomNavigationBarHeight +
24 | MediaQuery.paddingOf(context).bottom / 2),
25 | left: MediaQuery.paddingOf(context).left,
26 | ),
27 | sliver: SliverList(
28 | delegate: SliverChildListDelegate(this.children),
29 | ),
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/stores/views/intro.dart:
--------------------------------------------------------------------------------
1 | import 'package:mobx/mobx.dart';
2 |
3 | part 'intro.g.dart';
4 |
5 | enum IntroStage {
6 | GettingStarted,
7 | VersionSelection,
8 | TwentyEightParty,
9 | InstallationSlides,
10 | }
11 |
12 | class IntroStore = _IntroStore with _$IntroStore;
13 |
14 | const int kSecondsToLockSlide = 5;
15 |
16 | abstract class _IntroStore with Store {
17 | @observable
18 | IntroStage stage = IntroStage.GettingStarted;
19 |
20 | @observable
21 | int currentPage = 0;
22 |
23 | @observable
24 | bool lockedOnSlide = false;
25 |
26 | int slideLockSeconds = kSecondsToLockSlide;
27 |
28 | @action
29 | void setStage(IntroStage stage) => this.stage = stage;
30 |
31 | @action
32 | void setCurrentPage(int currentPage) => this.currentPage = currentPage;
33 |
34 | @action
35 | void setLockedOnSlide(bool lockedOnSlide, [int? secondsToLockSlide]) {
36 | this.slideLockSeconds = secondsToLockSlide ?? kSecondsToLockSlide;
37 | this.lockedOnSlide = lockedOnSlide;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/batch_responses/stats.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/batch_responses/base.dart';
2 | import 'package:obs_blade/types/classes/stream/responses/get_stats.dart';
3 | import 'package:obs_blade/types/classes/stream/responses/get_stream_status.dart';
4 | import 'package:obs_blade/types/enums/request_type.dart';
5 |
6 | import '../responses/get_record_status.dart';
7 |
8 | class StatsBatchResponse extends BaseBatchResponse {
9 | StatsBatchResponse(super.json);
10 |
11 | GetStreamStatusResponse get streamStatus => this.response(
12 | RequestType.GetStreamStatus,
13 | (jsonRAW) => GetStreamStatusResponse(jsonRAW),
14 | );
15 |
16 | GetRecordStatusResponse get recordStatus => this.response(
17 | RequestType.GetRecordStatus,
18 | (jsonRAW) => GetRecordStatusResponse(jsonRAW),
19 | );
20 |
21 | GetStatsResponse get stats => this.response(
22 | RequestType.GetStats,
23 | (jsonRAW) => GetStatsResponse(jsonRAW),
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/lib/views/dashboard/widgets/dashboard_content/scene_preview/preview_warning_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../shared/dialogs/confirmation.dart';
4 |
5 | class PreviewWarningDialog extends StatelessWidget {
6 | final void Function(bool) onOk;
7 |
8 | const PreviewWarningDialog({
9 | super.key,
10 | required this.onOk,
11 | });
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return ConfirmationDialog(
16 | title: 'Warning on scene preview',
17 | body:
18 | 'OBS WebSocket is not able to retrieve a video stream of the current scene. This implementation is a workaround. It does not reflect your actual OBS performance.\n\nBeware that this might cause higher battery usage and / or OBS itself (your pc) might suffer performance issues.\n\nUse with caution!',
19 | onOk: (checked) => this.onOk(checked),
20 | enableDontShowAgainOption: true,
21 | okText: 'Ok',
22 | noText: 'Cancel',
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/android/app/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | app
4 | Project app created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
25 | 1667568563458
26 |
27 | 30
28 |
29 | org.eclipse.core.resources.regexFilterMatcher
30 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/lib/types/classes/api/filter.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'filter.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$FilterImpl _$$FilterImplFromJson(Map json) => _$FilterImpl(
10 | filterEnabled: json['filterEnabled'] as bool,
11 | filterIndex: (json['filterIndex'] as num).toInt(),
12 | filterKind: json['filterKind'] as String,
13 | filterName: json['filterName'] as String,
14 | filterSettings: json['filterSettings'] as Map,
15 | );
16 |
17 | Map _$$FilterImplToJson(_$FilterImpl instance) =>
18 | {
19 | 'filterEnabled': instance.filterEnabled,
20 | 'filterIndex': instance.filterIndex,
21 | 'filterKind': instance.filterKind,
22 | 'filterName': instance.filterName,
23 | 'filterSettings': instance.filterSettings,
24 | };
25 |
--------------------------------------------------------------------------------
/lib/types/classes/api/transition.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'transition.freezed.dart';
4 | part 'transition.g.dart';
5 |
6 | @freezed
7 | class Transition with _$Transition {
8 | const factory Transition({
9 | /// Name of the transition
10 | required String transitionName,
11 |
12 | /// Kind of the transition
13 | required String transitionKind,
14 |
15 | /// Whether the transition uses a fixed (unconfigurable) duration
16 | required bool transitionFixed,
17 |
18 | /// Configured transition duration in milliseconds. null if transition is fixed
19 | required int? transitionDuration,
20 |
21 | /// Whether the transition supports being configured
22 | required bool transitionConfigurable,
23 |
24 | /// Object of settings for the transition. null if transition is not configurable
25 | required dynamic transitionSettings,
26 | }) = _Transition;
27 |
28 | factory Transition.fromJson(Map json) =>
29 | _$TransitionFromJson(json);
30 | }
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
48 | # CUSTOM
49 | # Android key file
50 | /android/key.properties
51 |
52 | # Flutter Version Manager (FVM)
53 | .fvm/
--------------------------------------------------------------------------------
/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obs_blade",
3 | "short_name": "obs_blade",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "A new Flutter project.",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | void main() {
9 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async {
10 | // // Build our app and trigger a frame.
11 | // await tester.pumpWidget(const MyApp());
12 |
13 | // // Verify that our counter starts at 0.
14 | // expect(find.text('0'), findsOneWidget);
15 | // expect(find.text('1'), findsNothing);
16 |
17 | // // Tap the '+' icon and trigger a frame.
18 | // await tester.tap(find.byIcon(Icons.add));
19 | // await tester.pump();
20 |
21 | // // Verify that our counter has incremented.
22 | // expect(find.text('0'), findsNothing);
23 | // expect(find.text('1'), findsOneWidget);
24 | // });
25 | }
26 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/card_header/sort_filter_panel/statistics_date_range.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_mobx/flutter_mobx.dart';
3 | import 'package:get_it/get_it.dart';
4 |
5 | import '../../../../../shared/general/date_range/date_range.dart';
6 | import '../../../../../stores/views/statistics.dart';
7 |
8 | class StatisticsDateRange extends StatelessWidget {
9 | const StatisticsDateRange({
10 | super.key,
11 | });
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | StatisticsStore statisticsStore = GetIt.instance();
16 |
17 | return Observer(
18 | builder: (context) => DateRange(
19 | selectedFromDate: statisticsStore.fromDate,
20 | updateFromDate: (date) => statisticsStore.setFromDate(date),
21 | selectedToDate: statisticsStore.toDate,
22 | updateToDate: (date) => statisticsStore.setToDate(date
23 | ?.add(const Duration(days: 1))
24 | .subtract(const Duration(milliseconds: 1))),
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_current_scene_transition.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets information about the current scene transition.
4 | class GetCurrentSceneTransitionResponse extends BaseResponse {
5 | GetCurrentSceneTransitionResponse(super.json);
6 |
7 | /// Name of the transition
8 | String get transitionName => this.json['transitionName'];
9 |
10 | /// Kind of the transition
11 | String get transitionKind => this.json['transitionKind'];
12 |
13 | /// Whether the transition uses a fixed (unconfigurable) duration
14 | bool get transitionFixed => this.json['transitionFixed'];
15 |
16 | /// Configured transition duration in milliseconds. null if transition is fixed
17 | int? get transitionDuration => this.json['transitionDuration'];
18 |
19 | /// Whether the transition supports being configured
20 | bool get transitionConfigurable => this.json['transitionConfigurable'];
21 |
22 | /// Object of settings for the transition. null if transition is not configurable
23 | dynamic get transitionSettings => this.json['transitionSettings'];
24 | }
25 |
--------------------------------------------------------------------------------
/windows/runner/flutter_window.h:
--------------------------------------------------------------------------------
1 | #ifndef RUNNER_FLUTTER_WINDOW_H_
2 | #define RUNNER_FLUTTER_WINDOW_H_
3 |
4 | #include
5 | #include
6 |
7 | #include
8 |
9 | #include "win32_window.h"
10 |
11 | // A window that does nothing but host a Flutter view.
12 | class FlutterWindow : public Win32Window {
13 | public:
14 | // Creates a new FlutterWindow hosting a Flutter view running |project|.
15 | explicit FlutterWindow(const flutter::DartProject& project);
16 | virtual ~FlutterWindow();
17 |
18 | protected:
19 | // Win32Window:
20 | bool OnCreate() override;
21 | void OnDestroy() override;
22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
23 | LPARAM const lparam) noexcept override;
24 |
25 | private:
26 | // The project to run.
27 | flutter::DartProject project_;
28 |
29 | // The Flutter instance hosted by this window.
30 | std::unique_ptr flutter_controller_;
31 | };
32 |
33 | #endif // RUNNER_FLUTTER_WINDOW_H_
34 |
--------------------------------------------------------------------------------
/lib/shared/general/responsive_widget_wrapper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../types/enums/hive_keys.dart';
4 | import '../../types/enums/settings_keys.dart';
5 | import '../../utils/styling_helper.dart';
6 | import 'hive_builder.dart';
7 |
8 | class ResponsiveWidgetWrapper extends StatelessWidget {
9 | final Widget mobileWidget;
10 | final Widget tabletWidget;
11 |
12 | const ResponsiveWidgetWrapper({
13 | super.key,
14 | required this.mobileWidget,
15 | required this.tabletWidget,
16 | });
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return HiveBuilder(
21 | hiveKey: HiveKeys.Settings,
22 | rebuildKeys: const [SettingsKeys.EnforceTabletMode],
23 | builder: (context, settingsBox, child) =>
24 | MediaQuery.sizeOf(context).width > StylingHelper.max_width_mobile ||
25 | settingsBox.get(SettingsKeys.EnforceTabletMode.name,
26 | defaultValue: false)
27 | ? this.tabletWidget
28 | : this.mobileWidget,
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/types/classes/api/scene_item_transform.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 |
3 | part 'scene_item_transform.freezed.dart';
4 | part 'scene_item_transform.g.dart';
5 |
6 | @freezed
7 | class SceneItemTransform with _$SceneItemTransform {
8 | const factory SceneItemTransform({
9 | required int? alignment,
10 | required int? boundsAlignment,
11 | required double? boundsHeight,
12 | required String? boundsType,
13 | required double? boundsWidth,
14 | required int? cropBottom,
15 | required int? cropLeft,
16 | required int? cropRight,
17 | required int? cropTop,
18 | required double? height,
19 | required double? positionX,
20 | required double? positionY,
21 | required double? rotation,
22 | required double? scaleX,
23 | required double? scaleY,
24 | required double? sourceHeight,
25 | required double? sourceWidth,
26 | required double? width,
27 | }) = _SceneItemTransform;
28 |
29 | factory SceneItemTransform.fromJson(Map json) =>
30 | _$SceneItemTransformFromJson(json);
31 | }
32 |
--------------------------------------------------------------------------------
/lib/views/settings/dashboard_customisation/order/widgets/profiles_preview.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../shared/general/base/dropdown.dart';
4 |
5 | class ProfilesPreview extends StatelessWidget {
6 | const ProfilesPreview({super.key});
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Row(
11 | children: [
12 | Expanded(
13 | child: BaseDropdown(
14 | value: '',
15 | items: [
16 | BaseDropdownItem(
17 | value: '',
18 | text: '',
19 | ),
20 | ],
21 | label: 'Profile',
22 | ),
23 | ),
24 | Expanded(
25 | child: BaseDropdown(
26 | value: '',
27 | items: [
28 | BaseDropdownItem(
29 | value: '',
30 | text: '',
31 | ),
32 | ],
33 | label: 'Scene Collection',
34 | ),
35 | ),
36 | ],
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/shared/dialogs/info.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:obs_blade/shared/general/base/adaptive_dialog/adaptive_dialog.dart';
4 |
5 | class InfoDialog extends StatelessWidget {
6 | final String? title;
7 | final String body;
8 |
9 | final bool enableDontShowAgainOption;
10 |
11 | final Function(bool isDontShowAgainChecked)? onPressed;
12 |
13 | const InfoDialog({
14 | super.key,
15 | required this.body,
16 | this.title,
17 | this.enableDontShowAgainOption = false,
18 | this.onPressed,
19 | });
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return BaseAdaptiveDialog(
24 | title: this.title,
25 | body: this.body,
26 | enableDontShowAgainOption: this.enableDontShowAgainOption,
27 | actions: [
28 | DialogActionConfig(
29 | isDefaultAction: false,
30 | onPressed: (isDontShowAgainChecked) =>
31 | this.onPressed?.call(isDontShowAgainChecked),
32 | child: const Text('OK'),
33 | ),
34 | ],
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/web/splash/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%
3 | }
4 |
5 | body {
6 | margin: 0;
7 | min-height: 100%;
8 | background-color: #101823;
9 | background-size: 100% 100%;
10 | }
11 |
12 | .center {
13 | margin: 0;
14 | position: absolute;
15 | top: 50%;
16 | left: 50%;
17 | -ms-transform: translate(-50%, -50%);
18 | transform: translate(-50%, -50%);
19 | }
20 |
21 | .contain {
22 | display:block;
23 | width:100%; height:100%;
24 | object-fit: contain;
25 | }
26 |
27 | .stretch {
28 | display:block;
29 | width:100%; height:100%;
30 | }
31 |
32 | .cover {
33 | display:block;
34 | width:100%; height:100%;
35 | object-fit: cover;
36 | }
37 |
38 | .bottom {
39 | position: absolute;
40 | bottom: 0;
41 | left: 50%;
42 | -ms-transform: translate(-50%, 0);
43 | transform: translate(-50%, 0);
44 | }
45 |
46 | .bottomLeft {
47 | position: absolute;
48 | bottom: 0;
49 | left: 0;
50 | }
51 |
52 | .bottomRight {
53 | position: absolute;
54 | bottom: 0;
55 | right: 0;
56 | }
57 |
58 | @media (prefers-color-scheme: dark) {
59 | body {
60 | background-color: #101823;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/views/settings/dashboard_customisation/order/widgets/controls_preview.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:obs_blade/shared/general/base/button.dart';
3 |
4 | import '../../../../../shared/general/described_box.dart';
5 |
6 | class ControlsPreview extends StatelessWidget {
7 | const ControlsPreview({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return DescribedBox(
12 | label: 'Various Controls',
13 | labelBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
14 | borderColor: Theme.of(context).dividerColor,
15 | child: Column(
16 | children: [
17 | SizedBox(
18 | width: double.infinity,
19 | child: BaseButton(
20 | text: '',
21 | onPressed: () {},
22 | ),
23 | ),
24 | SizedBox(
25 | width: double.infinity,
26 | child: BaseButton(
27 | text: '',
28 | onPressed: () {},
29 | ),
30 | ),
31 | ],
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/shared/general/keyboard_number_header.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:keyboard_actions/keyboard_actions.dart';
3 |
4 | class KeyboardNumberHeader extends StatelessWidget {
5 | final Widget child;
6 | final FocusNode focusNode;
7 |
8 | final void Function()? onDone;
9 |
10 | const KeyboardNumberHeader({
11 | super.key,
12 | required this.child,
13 | required this.focusNode,
14 | this.onDone,
15 | });
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return KeyboardActions(
20 | config: KeyboardActionsConfig(
21 | keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
22 | keyboardBarColor: Theme.of(context).brightness == Brightness.dark
23 | ? const Color.fromRGBO(45, 45, 45, 1.0)
24 | : const Color.fromRGBO(245, 245, 245, 1.0),
25 | nextFocus: false,
26 | actions: [
27 | KeyboardActionsItem(
28 | focusNode: this.focusNode,
29 | onTapAction: this.onDone,
30 | ),
31 | ],
32 | ),
33 | disableScroll: true,
34 | child: this.child,
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/base.dart:
--------------------------------------------------------------------------------
1 | import '../../../enums/event_type.dart';
2 | import '../../../interfaces/message.dart';
3 |
4 | /// Initial Wrapper object for an event which is received from the OBS WebSocket
5 | class BaseEvent implements Message {
6 | @override
7 | Map jsonRAW;
8 |
9 | @override
10 | Map json;
11 |
12 | BaseEvent(Map json)
13 | : jsonRAW = json,
14 | json = json['d']['eventData'] ?? {};
15 |
16 | EventType? get eventType {
17 | try {
18 | return EventType.values.firstWhere(
19 | (eventType) => eventType.name == this.jsonRAW['d']['eventType'],
20 | );
21 | } catch (e) {
22 | return null;
23 | }
24 | }
25 |
26 | /// (Optional): time elapsed between now and stream start (only present if OBS Studio is streaming)
27 | ///
28 | /// OLD, < 4.X
29 | String? get streamTimecodeOld => this.json['stream-timecode'];
30 |
31 | /// (Optional): time elapsed between now and recording start (only present if OBS Studio is recording)
32 | ///
33 | /// OLD, < 4.X
34 | String? get recTimecodeOld => this.json['rec-timecode'];
35 | }
36 |
--------------------------------------------------------------------------------
/lib/models/hotkey.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'hotkey.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class HotkeyAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 11;
12 |
13 | @override
14 | Hotkey read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return Hotkey(
20 | fields[0] as String,
21 | );
22 | }
23 |
24 | @override
25 | void write(BinaryWriter writer, Hotkey obj) {
26 | writer
27 | ..writeByte(1)
28 | ..writeByte(0)
29 | ..write(obj.name);
30 | }
31 |
32 | @override
33 | int get hashCode => typeId.hashCode;
34 |
35 | @override
36 | bool operator ==(Object other) =>
37 | identical(this, other) ||
38 | other is HotkeyAdapter &&
39 | runtimeType == other.runtimeType &&
40 | typeId == other.typeId;
41 | }
42 |
--------------------------------------------------------------------------------
/lib/shared/general/formatted_text.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class FormattedText extends StatelessWidget {
4 | final String label;
5 | final String? text;
6 | final double width;
7 | final String? unit;
8 |
9 | const FormattedText({
10 | super.key,
11 | required this.label,
12 | this.text,
13 | this.width = 50.0,
14 | this.unit,
15 | });
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return SizedBox(
20 | width: this.width,
21 | child: TextField(
22 | controller: TextEditingController(
23 | text:
24 | (this.text ?? '-') + (this.text != null ? (this.unit ?? '') : ''),
25 | ),
26 | style: Theme.of(context).textTheme.bodyMedium!.copyWith(
27 | fontFeatures: [
28 | const FontFeature.tabularFigures(),
29 | ],
30 | ),
31 | decoration: InputDecoration(
32 | isDense: true,
33 | enabled: false,
34 | labelText: this.label,
35 | labelStyle:
36 | Theme.of(context).textTheme.bodyMedium!.copyWith(height: 0.75),
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/card_header/sort_filter_panel/stat_type_control.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter_mobx/flutter_mobx.dart';
3 | import 'package:get_it/get_it.dart';
4 |
5 | import '../../../../../stores/views/statistics.dart';
6 |
7 | class StatTypeControl extends StatelessWidget {
8 | const StatTypeControl({
9 | super.key,
10 | });
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | StatisticsStore statisticsStore = GetIt.instance();
15 |
16 | return Observer(
17 | builder: (context) => SizedBox(
18 | width: double.infinity,
19 | child: CupertinoSlidingSegmentedControl(
20 | groupValue: statisticsStore.statType,
21 | padding: const EdgeInsets.all(0),
22 | children: Map.fromEntries(
23 | StatType.values.map(
24 | (statType) => MapEntry(
25 | statType,
26 | Text(statType.name),
27 | ),
28 | ),
29 | ),
30 | onValueChanged: (statType) => statisticsStore.setStatType(statType!),
31 | ),
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_stream_status.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets the status of the stream output.
4 | class GetStreamStatusResponse extends BaseResponse {
5 | GetStreamStatusResponse(super.json);
6 |
7 | /// Whether the output is active
8 | bool get outputActive => this.json['outputActive'];
9 |
10 | /// Whether the output is currently reconnecting
11 | bool get outputReconnecting => this.json['outputReconnecting'];
12 |
13 | /// Current formatted timecode string for the output
14 | String get outputTimecode => this.json['outputTimecode'];
15 |
16 | /// Current duration in milliseconds for the output
17 | int get outputDuration => this.json['outputDuration'];
18 |
19 | /// Congestion of the output
20 | double get outputCongestion => this.json['outputCongestion'];
21 |
22 | /// Number of bytes sent by the output
23 | int get outputBytes => this.json['outputBytes'];
24 |
25 | /// Number of frames skipped by the output's process
26 | int get outputSkippedFrames => this.json['outputSkippedFrames'];
27 |
28 | /// Total number of frames delivered by the output's process
29 | int get outputTotalFrames => this.json['outputTotalFrames'];
30 | }
31 |
--------------------------------------------------------------------------------
/lib/views/dashboard/widgets/dashboard_content/scene_content/media_inputs/media_inputs.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../../shared/general/nested_list_manager.dart';
4 |
5 | class MediaInputs extends StatefulWidget {
6 | const MediaInputs({
7 | super.key,
8 | });
9 |
10 | @override
11 | State createState() => _MediaInputsState();
12 | }
13 |
14 | class _MediaInputsState extends State
15 | with AutomaticKeepAliveClientMixin {
16 | final ScrollController _controller = ScrollController();
17 |
18 | @override
19 | bool get wantKeepAlive => true;
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | super.build(context);
24 | return NestedScrollManager(
25 | parentScrollController:
26 | ModalRoute.of(context)!.settings.arguments as ScrollController,
27 | child: Scrollbar(
28 | controller: _controller,
29 | thumbVisibility: true,
30 | child: ListView(
31 | controller: _controller,
32 | physics: const ClampingScrollPhysics(),
33 | padding: const EdgeInsets.all(0.0),
34 | children: const [],
35 | ),
36 | ),
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/card_header/sort_filter_panel/exclude_unnamed_checkbox.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:flutter_mobx/flutter_mobx.dart';
4 | import 'package:get_it/get_it.dart';
5 |
6 | import '../../../../../shared/general/base/checkbox.dart';
7 | import '../../../../../stores/views/statistics.dart';
8 |
9 | class ExcludeUnnamedCheckbox extends StatelessWidget {
10 | const ExcludeUnnamedCheckbox({
11 | super.key,
12 | });
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | StatisticsStore statisticsStore = GetIt.instance();
17 |
18 | return Transform.translate(
19 | offset: const Offset(-14.0, 0.0),
20 | child: Observer(
21 | builder: (context) => BaseCheckbox(
22 | value: statisticsStore.excludeUnnamedStats,
23 | text: 'Exclude unnamed entries',
24 | smallText: true,
25 | tristate: true,
26 | onChanged: (excludeUnnamedStats) {
27 | HapticFeedback.lightImpact();
28 |
29 | statisticsStore.setExcludeUnnamedStats(excludeUnnamedStats);
30 | },
31 | ),
32 | ),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/shared/general/question_mark_tooltip.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | import '../../utils/modal_handler.dart';
4 | import '../dialogs/info.dart';
5 |
6 | class QuestionMarkTooltip extends StatelessWidget {
7 | final String message;
8 |
9 | const QuestionMarkTooltip({
10 | super.key,
11 | required this.message,
12 | });
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GestureDetector(
17 | onTap: () => ModalHandler.showBaseDialog(
18 | context: context,
19 | barrierDismissible: true,
20 | dialogWidget: InfoDialog(
21 | body: this.message,
22 | ),
23 | ),
24 | child: const Icon(
25 | CupertinoIcons.question_circle_fill,
26 | ));
27 | // return CustomTooltip(
28 | // message: this.message,
29 | // padding: EdgeInsets.all(12.0),
30 | // margin: EdgeInsets.all(32.0),
31 | // preferBelow: false,
32 | // verticalOffset: 2.0,
33 | // waitDuration: Duration(milliseconds: 0),
34 | // showDuration: Duration(seconds: 5),
35 | // immediately: true,
36 | // child: Icon(StylingHelper.question),
37 | // );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/responses/get_version.dart:
--------------------------------------------------------------------------------
1 | import 'base.dart';
2 |
3 | /// Gets data about the current plugin and RPC version.
4 | class GetVersionResponse extends BaseResponse {
5 | GetVersionResponse(super.json);
6 |
7 | /// Current OBS Studio version
8 | String get obsVersion => this.json['obsVersion'];
9 |
10 | /// Current obs-websocket version
11 | String get obsWebSocketVersion => this.json['obsWebSocketVersion'];
12 |
13 | /// Current latest obs-websocket RPC version
14 | int get rpcVersion => this.json['rpcVersion'];
15 |
16 | /// Array of available RPC requests for the currently negotiated RPC version
17 | List get availableRequests =>
18 | List.from(this.json['availableRequests']);
19 |
20 | /// Image formats available in GetSourceScreenshot and SaveSourceScreenshot requests.
21 | List get supportedImageFormats =>
22 | List.from(this.json['supportedImageFormats']);
23 |
24 | /// Name of the platform. Usually windows, macos, or ubuntu (linux flavor).
25 | /// Not guaranteed to be any of those
26 | String get platform => this.json['platform'];
27 |
28 | /// Description of the platform, like Windows 10 (10.0)
29 | String get platformDescription => this.json['platformDescription'];
30 | }
31 |
--------------------------------------------------------------------------------
/lib/views/home/widgets/connect_box/auto_discovery/session_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 |
3 | import '../../../../../models/connection.dart';
4 | import '../../../../../shared/general/base/divider.dart';
5 | import '../../../../../shared/general/custom_expansion_tile.dart';
6 | import '../connect_form/connect_form.dart';
7 |
8 | class SessionTile extends StatelessWidget {
9 | final Connection connection;
10 |
11 | const SessionTile({super.key, required this.connection});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return CustomExpansionTile(
16 | leadingIcon: CupertinoIcons.desktopcomputer,
17 | headerText: this.connection.host,
18 | headerPadding: const EdgeInsets.symmetric(
19 | horizontal: 24.0,
20 | vertical: 18.0,
21 | ),
22 | expandedBody: Column(
23 | children: [
24 | const BaseDivider(),
25 | Padding(
26 | padding: const EdgeInsets.only(
27 | top: 12.0,
28 | left: 24.0,
29 | right: 24,
30 | bottom: 12.0,
31 | ),
32 | child: ConnectForm(connection: this.connection),
33 | ),
34 | ],
35 | ),
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/views/intro/widgets/back_so_selection_wrapper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:get_it/get_it.dart';
4 | import 'package:obs_blade/stores/views/intro.dart';
5 |
6 | import '../../../shared/general/themed/cupertino_button.dart';
7 |
8 | class BackToSelectionWrapper extends StatelessWidget {
9 | final Widget? child;
10 |
11 | const BackToSelectionWrapper({
12 | super.key,
13 | this.child,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Column(
19 | children: [
20 | SizedBox(height: MediaQuery.paddingOf(context).top),
21 | ThemedCupertinoButton(
22 | child: Row(
23 | children: [
24 | Icon(
25 | CupertinoIcons.chevron_left,
26 | color: Theme.of(context).cupertinoOverrideTheme!.primaryColor,
27 | ),
28 | const Text('Version Selection')
29 | ],
30 | ),
31 | onPressed: () => GetIt.instance()
32 | .setStage(IntroStage.VersionSelection),
33 | ),
34 | Expanded(
35 | child: this.child ?? const SizedBox(),
36 | ),
37 | ],
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/events/input_volume_meters.dart:
--------------------------------------------------------------------------------
1 | import '../../api/input_channel.dart';
2 | import 'base.dart';
3 |
4 | /// A high-volume event providing volume levels of all active inputs every 50 milliseconds.
5 | class InputVolumeMetersEvent extends BaseEvent {
6 | InputVolumeMetersEvent(super.json);
7 |
8 | /// Array of active inputs with their associated volume levels
9 | List get inputs =>
10 | List.from(this.json['inputs'].map((input) => InputLevel.fromJSON(input)));
11 | }
12 |
13 | class InputLevel {
14 | String? inputName;
15 | List? inputLevelsMul;
16 |
17 | InputLevel({
18 | required this.inputName,
19 | required this.inputLevelsMul,
20 | });
21 |
22 | static List _mapToChannel(List channels) =>
23 | List.from(channels.map(
24 | (channel) => InputChannel(
25 | current: channel[1],
26 | average: channel[0],
27 | potential: channel[2],
28 | ),
29 | ));
30 |
31 | static InputLevel fromJSON(Map json) => InputLevel(
32 | inputName: json['inputName'],
33 | inputLevelsMul: json['inputLevelsMul'].isNotEmpty
34 | ? _mapToChannel(json['inputLevelsMul'])
35 | : [],
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/lib/types/classes/api/transition.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'transition.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$TransitionImpl _$$TransitionImplFromJson(Map json) =>
10 | _$TransitionImpl(
11 | transitionName: json['transitionName'] as String,
12 | transitionKind: json['transitionKind'] as String,
13 | transitionFixed: json['transitionFixed'] as bool,
14 | transitionDuration: (json['transitionDuration'] as num?)?.toInt(),
15 | transitionConfigurable: json['transitionConfigurable'] as bool,
16 | transitionSettings: json['transitionSettings'],
17 | );
18 |
19 | Map _$$TransitionImplToJson(_$TransitionImpl instance) =>
20 | {
21 | 'transitionName': instance.transitionName,
22 | 'transitionKind': instance.transitionKind,
23 | 'transitionFixed': instance.transitionFixed,
24 | 'transitionDuration': instance.transitionDuration,
25 | 'transitionConfigurable': instance.transitionConfigurable,
26 | 'transitionSettings': instance.transitionSettings,
27 | };
28 |
--------------------------------------------------------------------------------
/lib/shared/general/clean_list_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class CleanListTile extends StatelessWidget {
4 | final String title;
5 | final String description;
6 |
7 | final Widget? trailing;
8 |
9 | const CleanListTile({
10 | super.key,
11 | required this.title,
12 | required this.description,
13 | this.trailing,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Row(
19 | children: [
20 | Expanded(
21 | child: Column(
22 | crossAxisAlignment: CrossAxisAlignment.start,
23 | children: [
24 | Text(
25 | this.title,
26 | style: Theme.of(context)
27 | .textTheme
28 | .labelLarge!
29 | .copyWith(fontSize: 16),
30 | ),
31 | const SizedBox(height: 4.0),
32 | Text(
33 | this.description,
34 | style: Theme.of(context).textTheme.bodySmall,
35 | ),
36 | ],
37 | ),
38 | ),
39 | if (this.trailing != null) ...[
40 | const SizedBox(width: 32.0),
41 | this.trailing!,
42 | ],
43 | ],
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/lib/views/dashboard/widgets/obs_widgets/obs_widgets.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../shared/general/base/card.dart';
4 | import 'stats/stats.dart';
5 | import 'stream_chat/stream_chat.dart';
6 |
7 | class OBSWidgets extends StatelessWidget {
8 | const OBSWidgets({
9 | super.key,
10 | });
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return const Row(
15 | crossAxisAlignment: CrossAxisAlignment.start,
16 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
17 | children: [
18 | Flexible(
19 | child: BaseCard(
20 | title: 'Chat',
21 | rightPadding: 12.0,
22 | paddingChild: EdgeInsets.all(0),
23 | child: SizedBox(
24 | height: 750.0,
25 | child: StreamChat(
26 | usernameRowPadding: true,
27 | ),
28 | ),
29 | ),
30 | ),
31 | Flexible(
32 | child: BaseCard(
33 | title: 'Stats',
34 | leftPadding: 12.0,
35 | paddingChild: EdgeInsets.all(0),
36 | child: SizedBox(
37 | height: 650.0,
38 | child: Stats(),
39 | ),
40 | ),
41 | ),
42 | ],
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/batch_responses/inputs.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/batch_responses/base.dart';
2 | import 'package:obs_blade/types/classes/stream/responses/get_input_audio_sync_offset.dart';
3 | import 'package:obs_blade/types/classes/stream/responses/get_input_mute.dart';
4 | import 'package:obs_blade/types/classes/stream/responses/get_input_volume.dart';
5 | import 'package:obs_blade/types/enums/request_type.dart';
6 |
7 | class InputsBatchResponse extends BaseBatchResponse {
8 | InputsBatchResponse(super.json);
9 |
10 | Iterable get inputsVolume => this
11 | .responses
12 | .where((response) => response.requestType == RequestType.GetInputVolume)
13 | .map((response) => GetInputVolumeResponse(response.jsonRAW));
14 |
15 | Iterable get inputsMute => this
16 | .responses
17 | .where((response) => response.requestType == RequestType.GetInputMute)
18 | .map((response) => GetInputMuteResponse(response.jsonRAW));
19 |
20 | Iterable get inputsAudioSyncOffset => this
21 | .responses
22 | .where((response) =>
23 | response.requestType == RequestType.GetInputAudioSyncOffset)
24 | .map((response) => GetInputAudioSyncOffsetResponse(response.jsonRAW));
25 | }
26 |
--------------------------------------------------------------------------------
/lib/shared/general/themed/cupertino_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class ThemedCupertinoButton extends StatelessWidget {
5 | final String? text;
6 | final Widget? child;
7 | final EdgeInsetsGeometry? padding;
8 | final bool isDestructive;
9 | final double? minSize;
10 |
11 | final void Function()? onPressed;
12 |
13 | const ThemedCupertinoButton({
14 | super.key,
15 | this.text,
16 | this.child,
17 | this.padding,
18 | this.isDestructive = false,
19 | this.minSize,
20 | this.onPressed,
21 | });
22 |
23 | @override
24 | Widget build(BuildContext context) {
25 | return CupertinoButton(
26 | minSize: this.minSize ?? kMinInteractiveDimensionCupertino,
27 | padding: this.padding,
28 | onPressed: this.onPressed,
29 | child: DefaultTextStyle.merge(
30 | style: TextStyle(
31 | color: this.onPressed != null
32 | ? this.isDestructive
33 | ? CupertinoColors.destructiveRed
34 | : Theme.of(context).cupertinoOverrideTheme!.primaryColor
35 | : null,
36 | ),
37 | child: this.child ??
38 | Text(
39 | this.text ?? '',
40 | ),
41 | ),
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-v31/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/lib/models/enums/scene_item_type.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'scene_item_type.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class SceneItemTypeAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 5;
12 |
13 | @override
14 | SceneItemType read(BinaryReader reader) {
15 | switch (reader.readByte()) {
16 | case 0:
17 | return SceneItemType.Source;
18 | case 1:
19 | return SceneItemType.Audio;
20 | default:
21 | return SceneItemType.Source;
22 | }
23 | }
24 |
25 | @override
26 | void write(BinaryWriter writer, SceneItemType obj) {
27 | switch (obj) {
28 | case SceneItemType.Source:
29 | writer.writeByte(0);
30 | break;
31 | case SceneItemType.Audio:
32 | writer.writeByte(1);
33 | break;
34 | }
35 | }
36 |
37 | @override
38 | int get hashCode => typeId.hashCode;
39 |
40 | @override
41 | bool operator ==(Object other) =>
42 | identical(this, other) ||
43 | other is SceneItemTypeAdapter &&
44 | runtimeType == other.runtimeType &&
45 | typeId == other.typeId;
46 | }
47 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night-v31/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
18 |
21 |
22 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/stats_entry/entry_meta_chip.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class EntryMetaChip extends StatelessWidget {
4 | final String title;
5 | final String content;
6 |
7 | final double? width;
8 |
9 | const EntryMetaChip({
10 | super.key,
11 | required this.title,
12 | required this.content,
13 | this.width,
14 | });
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return SizedBox(
19 | width: this.width ?? double.infinity,
20 | child: Padding(
21 | padding: const EdgeInsets.all(4.0),
22 | child: Chip(
23 | padding: const EdgeInsets.all(4.0),
24 | label: SizedBox(
25 | width: double.infinity,
26 | child: Column(
27 | children: [
28 | Padding(
29 | padding: const EdgeInsets.only(bottom: 4.0),
30 | child: Text(
31 | this.title,
32 | style: Theme.of(context).textTheme.titleSmall!.copyWith(
33 | decoration: TextDecoration.underline,
34 | ),
35 | ),
36 | ),
37 | Text(this.content),
38 | ],
39 | ),
40 | ),
41 | ),
42 | ),
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/lib/shared/general/base/icon_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class BaseIconButton extends StatelessWidget {
4 | final IconData? icon;
5 | final double? iconSize;
6 | final double? buttonSize;
7 | final Color? backgroundColor;
8 | final Color? foregroundColor;
9 |
10 | final void Function()? onTap;
11 |
12 | final Widget? child;
13 |
14 | const BaseIconButton({
15 | super.key,
16 | this.icon,
17 | this.iconSize,
18 | this.buttonSize,
19 | this.backgroundColor,
20 | this.foregroundColor,
21 | this.onTap,
22 | this.child,
23 | }) : assert(icon != null || child != null);
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | return GestureDetector(
28 | onTap: this.onTap,
29 | child: Container(
30 | height: this.buttonSize,
31 | width: this.buttonSize,
32 | padding: const EdgeInsets.all(4.0),
33 | decoration: BoxDecoration(
34 | shape: BoxShape.circle,
35 | color: this.backgroundColor ??
36 | Theme.of(context).buttonTheme.colorScheme!.secondary,
37 | ),
38 | child: this.child ??
39 | Icon(
40 | this.icon,
41 | color: this.foregroundColor,
42 | size: this.iconSize,
43 | ),
44 | ),
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/models/hidden_scene.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 | import 'type_ids.dart';
3 |
4 | part 'hidden_scene.g.dart';
5 |
6 | @HiveType(typeId: TypeIDs.HiddenScene)
7 | class HiddenScene extends HiveObject {
8 | /// Name of the scene (unfortunately OBS WebSocket does not expose something
9 | /// like an ID for scene, just the name) which should be hidden
10 | @HiveField(0)
11 | String sceneName;
12 |
13 | /// Since the scene name itself is not a good enough indicator (different OBS
14 | /// instances could share the same scene name like "Main"), a good indicator
15 | /// (if present since it's not mandatory) would be the connection name because
16 | /// it has to be unique
17 | @HiveField(1)
18 | String? connectionName;
19 |
20 | /// Used as a backup if no connection name is present because it's better than
21 | /// only using the name but might cause false behaviour when the host
22 | /// changes for whatever reasons
23 | @HiveField(2)
24 | String host;
25 |
26 | HiddenScene(this.sceneName, this.connectionName, this.host);
27 |
28 | bool isScene(String sceneName, String? connectionName, String? host) =>
29 | this.sceneName == sceneName &&
30 | (this.connectionName == connectionName ||
31 | (this.connectionName == null &&
32 | connectionName == null &&
33 | this.host == host));
34 | }
35 |
--------------------------------------------------------------------------------
/lib/types/classes/stream/batch_responses/base.dart:
--------------------------------------------------------------------------------
1 | import 'package:obs_blade/types/classes/stream/responses/base.dart';
2 | import 'package:obs_blade/types/enums/request_batch_type.dart';
3 | import 'package:obs_blade/types/interfaces/message.dart';
4 |
5 | import '../../../enums/request_type.dart';
6 |
7 | class BaseBatchResponse implements Message {
8 | @override
9 | Map json;
10 |
11 | @override
12 | Map jsonRAW;
13 |
14 | List responses;
15 |
16 | BaseBatchResponse(this.json)
17 | : jsonRAW = json,
18 | responses = List.from(json['d']['results'])
19 | .map((response) => BaseResponse.d(response))
20 | .toList();
21 |
22 | String get uuid => this.jsonRAW['d']['requestId'];
23 |
24 | RequestBatchType get batchRequestType => RequestBatchType.values.firstWhere(
25 | (type) => type.requestTypes.every(
26 | (requestType) => this.responses.any(
27 | (response) => requestType == response.requestType,
28 | ),
29 | ),
30 | );
31 |
32 | T response(
33 | RequestType requestType, T Function(Map) creation) =>
34 | creation(this
35 | .responses
36 | .firstWhere((response) => response.requestType == requestType)
37 | .jsonRAW);
38 | }
39 |
--------------------------------------------------------------------------------
/lib/models/enums/dashboard_element.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | import '../type_ids.dart';
4 |
5 | part 'dashboard_element.g.dart';
6 |
7 | @HiveType(typeId: TypeIDs.DashboardElement)
8 | enum DashboardElement {
9 | @HiveField(0)
10 | ExposedProfile,
11 |
12 | @HiveField(1)
13 | ExposedControls,
14 |
15 | @HiveField(2)
16 | SceneButtons,
17 |
18 | @HiveField(3)
19 | StudioModeTransition,
20 |
21 | @HiveField(4)
22 | StudioModeConfig,
23 |
24 | @HiveField(5)
25 | ScenePreview,
26 |
27 | @HiveField(6)
28 | SceneItems,
29 |
30 | @HiveField(7)
31 | SceneItemsAudio,
32 |
33 | @HiveField(8)
34 | StreamChat,
35 |
36 | @HiveField(9)
37 | OBSStats;
38 |
39 | String get name => switch (this) {
40 | DashboardElement.ExposedProfile => 'Profiles',
41 | DashboardElement.ExposedControls => 'Controls',
42 | DashboardElement.SceneButtons => 'Scene Buttons',
43 | DashboardElement.StudioModeTransition => 'Studio Mode Transition',
44 | DashboardElement.StudioModeConfig => 'Studio Mode Config',
45 | DashboardElement.ScenePreview => 'Scene Preview',
46 | DashboardElement.SceneItems => 'Scene Items',
47 | DashboardElement.SceneItemsAudio => 'Scene Audio',
48 | DashboardElement.StreamChat => 'Chat',
49 | DashboardElement.OBSStats => 'Stats',
50 | };
51 | }
52 |
--------------------------------------------------------------------------------
/lib/types/classes/api/record_stats.dart:
--------------------------------------------------------------------------------
1 | class RecordStats {
2 | /// Total time (in seconds) since the record started
3 | int totalTime;
4 |
5 | /// Current framerate
6 | double fps;
7 |
8 | /// Amount of data per second (in kilobits) transmitted by the output
9 | int kbitsPerSec;
10 |
11 | /// Number of frames rendered
12 | int renderTotalFrames;
13 |
14 | /// Number of frames skipped due to rendering lag
15 | int renderSkippedFrames;
16 |
17 | /// Number of frames outputted
18 | int outputTotalFrames;
19 |
20 | /// Number of frames skipped due to encoding lag
21 | int outputSkippedFrames;
22 |
23 | /// Average frame time (in milliseconds)
24 | double averageFrameTime;
25 |
26 | /// Current CPU usage (percentage)
27 | double cpuUsage;
28 |
29 | /// Current RAM usage (in megabytes)
30 | double memoryUsage;
31 |
32 | /// Free recording disk space (in megabytes)
33 | double freeDiskSpace;
34 |
35 | RecordStats({
36 | required this.kbitsPerSec,
37 | required this.totalTime,
38 | required this.fps,
39 | required this.renderTotalFrames,
40 | required this.renderSkippedFrames,
41 | required this.outputTotalFrames,
42 | required this.outputSkippedFrames,
43 | required this.averageFrameTime,
44 | required this.cpuUsage,
45 | required this.memoryUsage,
46 | required this.freeDiskSpace,
47 | });
48 | }
49 |
--------------------------------------------------------------------------------
/lib/views/statistics/widgets/card_header/sort_filter_panel/filter_name.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:get_it/get_it.dart';
3 | import 'package:mobx/mobx.dart';
4 |
5 | import '../../../../../stores/views/statistics.dart';
6 |
7 | class FilterName extends StatefulWidget {
8 | const FilterName({
9 | super.key,
10 | });
11 |
12 | @override
13 | State createState() => _FilterNameState();
14 | }
15 |
16 | class _FilterNameState extends State {
17 | final List _d = [];
18 | final TextEditingController _controller = TextEditingController();
19 |
20 | @override
21 | void initState() {
22 | super.initState();
23 |
24 | _d.add(reaction(
25 | (_) => GetIt.instance().triggeredDefault,
26 | (__) => _controller.clear(),
27 | ));
28 | }
29 |
30 | @override
31 | void dispose() {
32 | for (final d in _d) {
33 | d();
34 | }
35 |
36 | super.dispose();
37 | }
38 |
39 | @override
40 | Widget build(BuildContext context) {
41 | return CupertinoTextField(
42 | controller: _controller,
43 | placeholder: 'Filter by name...',
44 | clearButtonMode: OverlayVisibilityMode.always,
45 | onChanged: (name) => GetIt.instance().setFilterName(
46 | name.trim().toLowerCase(),
47 | ),
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/types/classes/api/scene_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:freezed_annotation/freezed_annotation.dart';
2 | import 'package:obs_blade/types/classes/api/filter.dart';
3 |
4 | import 'scene_item_transform.dart';
5 |
6 | part 'scene_item.freezed.dart';
7 | part 'scene_item.g.dart';
8 |
9 | @freezed
10 | class SceneItem with _$SceneItem {
11 | const factory SceneItem({
12 | required String? inputKind,
13 | required bool? isGroup,
14 | required String? sceneItemBlendMode,
15 | required bool? sceneItemEnabled,
16 | required int? sceneItemId,
17 | required int? sceneItemIndex,
18 | required bool? sceneItemLocked,
19 | required SceneItemTransform? sceneItemTransform,
20 | required String? sourceName,
21 | required String? sourceType,
22 | @Default([]) List filters,
23 |
24 | /// OPTIONAL - Name of the item's parent (if this item belongs to a group)
25 | String? parentGroupName,
26 |
27 | /// OPTIONAL - List of children (if this item is a group)
28 | List? groupChildren,
29 |
30 | /// CUSTOM - added myself to handle stuff internally
31 |
32 | /// Indicate whether we want to display the children of this group
33 | /// (if this [SceneItem] is a group)
34 | @Default(false) bool displayGroup,
35 | }) = _SceneItem;
36 |
37 | factory SceneItem.fromJson(Map json) =>
38 | _$SceneItemFromJson(json);
39 | }
40 |
--------------------------------------------------------------------------------
/lib/models/hidden_scene.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'hidden_scene.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class HiddenSceneAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 6;
12 |
13 | @override
14 | HiddenScene read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return HiddenScene(
20 | fields[0] as String,
21 | fields[1] as String?,
22 | fields[2] as String,
23 | );
24 | }
25 |
26 | @override
27 | void write(BinaryWriter writer, HiddenScene obj) {
28 | writer
29 | ..writeByte(3)
30 | ..writeByte(0)
31 | ..write(obj.sceneName)
32 | ..writeByte(1)
33 | ..write(obj.connectionName)
34 | ..writeByte(2)
35 | ..write(obj.host);
36 | }
37 |
38 | @override
39 | int get hashCode => typeId.hashCode;
40 |
41 | @override
42 | bool operator ==(Object other) =>
43 | identical(this, other) ||
44 | other is HiddenSceneAdapter &&
45 | runtimeType == other.runtimeType &&
46 | typeId == other.typeId;
47 | }
48 |
--------------------------------------------------------------------------------
/lib/models/enums/log_level.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'log_level.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class LogLevelAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 8;
12 |
13 | @override
14 | LogLevel read(BinaryReader reader) {
15 | switch (reader.readByte()) {
16 | case 0:
17 | return LogLevel.Info;
18 | case 1:
19 | return LogLevel.Warning;
20 | case 2:
21 | return LogLevel.Error;
22 | default:
23 | return LogLevel.Info;
24 | }
25 | }
26 |
27 | @override
28 | void write(BinaryWriter writer, LogLevel obj) {
29 | switch (obj) {
30 | case LogLevel.Info:
31 | writer.writeByte(0);
32 | break;
33 | case LogLevel.Warning:
34 | writer.writeByte(1);
35 | break;
36 | case LogLevel.Error:
37 | writer.writeByte(2);
38 | break;
39 | }
40 | }
41 |
42 | @override
43 | int get hashCode => typeId.hashCode;
44 |
45 | @override
46 | bool operator ==(Object other) =>
47 | identical(this, other) ||
48 | other is LogLevelAdapter &&
49 | runtimeType == other.runtimeType &&
50 | typeId == other.typeId;
51 | }
52 |
--------------------------------------------------------------------------------
/lib/types/enums/hive_keys.dart:
--------------------------------------------------------------------------------
1 | enum HiveKeys {
2 | /// Returns a List of [Connection]
3 | SavedConnections,
4 |
5 | /// Returns a List of [PastStreamData]
6 | PastStreamData,
7 |
8 | /// Returns a List of [PastRecordData]
9 | PastRecordData,
10 |
11 | /// Returns a List of [CustomTheme]
12 | CustomTheme,
13 |
14 | /// Returns a List of [HiddenSceneItem]
15 | HiddenSceneItem,
16 |
17 | /// Returns a List of [HiddenScene]
18 | HiddenScene,
19 |
20 | /// Returns a List of [AppLog]
21 | AppLog,
22 |
23 | /// Returns a List of [PurchasedTip]
24 | PurchasedTip,
25 |
26 | /// Returns a List of [Hotkey]
27 | Hotkey,
28 |
29 | /// Returns the box containing app settings - refer to [SettingsKeys]
30 | /// to see which key-value pairs are available
31 | Settings,
32 | }
33 |
34 | extension HiveKeysFunctions on HiveKeys {
35 | String get name => const {
36 | HiveKeys.SavedConnections: 'saved-connections',
37 | HiveKeys.PastStreamData: 'past-stream-data',
38 | HiveKeys.PastRecordData: 'past-record-data',
39 | HiveKeys.CustomTheme: 'custom-theme',
40 | HiveKeys.HiddenSceneItem: 'hidden-scene-item',
41 | HiveKeys.HiddenScene: 'hidden-scene',
42 | HiveKeys.AppLog: 'app-log',
43 | HiveKeys.PurchasedTip: 'purchased-tip',
44 | HiveKeys.Hotkey: 'hotkey',
45 | HiveKeys.Settings: 'settings',
46 | }[this]!;
47 | }
48 |
--------------------------------------------------------------------------------
/lib/models/enums/chat_type.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'chat_type.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class ChatTypeAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 4;
12 |
13 | @override
14 | ChatType read(BinaryReader reader) {
15 | switch (reader.readByte()) {
16 | case 0:
17 | return ChatType.Twitch;
18 | case 1:
19 | return ChatType.YouTube;
20 | case 2:
21 | return ChatType.Owncast;
22 | default:
23 | return ChatType.Twitch;
24 | }
25 | }
26 |
27 | @override
28 | void write(BinaryWriter writer, ChatType obj) {
29 | switch (obj) {
30 | case ChatType.Twitch:
31 | writer.writeByte(0);
32 | break;
33 | case ChatType.YouTube:
34 | writer.writeByte(1);
35 | break;
36 | case ChatType.Owncast:
37 | writer.writeByte(2);
38 | break;
39 | }
40 | }
41 |
42 | @override
43 | int get hashCode => typeId.hashCode;
44 |
45 | @override
46 | bool operator ==(Object other) =>
47 | identical(this, other) ||
48 | other is ChatTypeAdapter &&
49 | runtimeType == other.runtimeType &&
50 | typeId == other.typeId;
51 | }
52 |
--------------------------------------------------------------------------------
/lib/views/settings/custom_theme/widgets/custom_theme_list/theme_colors_row.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../../../models/custom_theme.dart';
4 | import '../../../../../types/extensions/string.dart';
5 | import '../../../../../utils/styling_helper.dart';
6 | import '../color_picker/color_bubble.dart';
7 |
8 | class ThemeColorsRow extends StatelessWidget {
9 | final CustomTheme customTheme;
10 |
11 | const ThemeColorsRow({super.key, required this.customTheme});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Wrap(
16 | spacing: 8.0,
17 | runSpacing: 8.0,
18 | children: [
19 | ColorBubble(color: customTheme.cardColorHex.hexToColor()),
20 | ColorBubble(
21 | color: customTheme.cardBorderColorHex?.hexToColor() ??
22 | Colors.transparent),
23 | ColorBubble(
24 | color: customTheme.dividerColorHex?.hexToColor() ??
25 | StylingHelper.light_divider_color),
26 | ColorBubble(color: customTheme.tabBarColorHex.hexToColor()),
27 | ColorBubble(color: customTheme.highlightColorHex.hexToColor()),
28 | ColorBubble(color: customTheme.accentColorHex.hexToColor()),
29 | ColorBubble(color: customTheme.backgroundColorHex.hexToColor()),
30 | // ColorBubble(color: customTheme.textColorHex.hexToColor()),
31 | ],
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/macos/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | $(PRODUCT_COPYRIGHT)
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 | NSLocalNetworkUsageDescription
32 | In order to find running OBS instances automatically.
33 | NSPhotoLibraryUsageDescription
34 | In order to select your custom logo.
35 |
36 |
37 |
--------------------------------------------------------------------------------
/lib/views/home/widgets/saved_connections/placeholder_connection.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../../shared/general/base/card.dart';
5 | import '../../../../shared/overlay/base_result.dart';
6 |
7 | class PlaceholderConnection extends StatelessWidget {
8 | final double height;
9 | final double width;
10 |
11 | const PlaceholderConnection(
12 | {super.key, required this.height, required this.width});
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Center(
17 | child: SizedBox(
18 | width: this.width,
19 | child: BaseCard(
20 | topPadding: 0.0,
21 | rightPadding: 0.0,
22 | bottomPadding: 0.0,
23 | leftPadding: 0.0,
24 | paddingChild: const EdgeInsets.all(0),
25 | child: Padding(
26 | padding: const EdgeInsets.only(left: 24.0, right: 24.0),
27 | child: SizedBox(
28 | height: this.height,
29 | child: const BaseResult(
30 | icon: BaseResultIcon.Missing,
31 | iconSize: 42.0,
32 | text:
33 | 'No saved connections yet...\nNo worries though, once you successfully connected to an OBS instance you can save one for later! :)',
34 | ),
35 | ),
36 | ),
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/windows/runner/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include "flutter_window.h"
6 | #include "utils.h"
7 |
8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
9 | _In_ wchar_t *command_line, _In_ int show_command) {
10 | // Attach to console when present (e.g., 'flutter run') or create a
11 | // new console when running with a debugger.
12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
13 | CreateAndAttachConsole();
14 | }
15 |
16 | // Initialize COM, so that it is available for use in the library and/or
17 | // plugins.
18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
19 |
20 | flutter::DartProject project(L"data");
21 |
22 | std::vector command_line_arguments =
23 | GetCommandLineArguments();
24 |
25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
26 |
27 | FlutterWindow window(project);
28 | Win32Window::Point origin(10, 10);
29 | Win32Window::Size size(1280, 720);
30 | if (!window.CreateAndShow(L"obs_blade", origin, size)) {
31 | return EXIT_FAILURE;
32 | }
33 | window.SetQuitOnClose(true);
34 |
35 | ::MSG msg;
36 | while (::GetMessage(&msg, nullptr, 0, 0)) {
37 | ::TranslateMessage(&msg);
38 | ::DispatchMessage(&msg);
39 | }
40 |
41 | ::CoUninitialize();
42 | return EXIT_SUCCESS;
43 | }
44 |
--------------------------------------------------------------------------------
/lib/views/dashboard/widgets/dashboard_content/dashboard_content_streaming.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/widgets.dart';
4 | import 'package:obs_blade/views/dashboard/widgets/dashboard_content/resizeable_scene_preview.dart';
5 | import 'package:obs_blade/views/dashboard/widgets/dashboard_content/scene_buttons/scene_buttons.dart';
6 | import 'package:obs_blade/views/dashboard/widgets/obs_widgets/stream_chat/stream_chat.dart';
7 |
8 | class DashboardContentStreaming extends StatelessWidget {
9 | const DashboardContentStreaming({
10 | super.key,
11 | });
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return SliverFillRemaining(
16 | child: Column(
17 | mainAxisSize: MainAxisSize.min,
18 | children: [
19 | const ResizeableScenePreview(
20 | resizeable: false,
21 | ),
22 | const SceneButtons(
23 | size: 64,
24 | mode: SceneButtonsMode.horizontalScroll,
25 | ),
26 | const Flexible(
27 | child: StreamChat(
28 | usernameRowExpandable: true,
29 | usernameRowBeneath: true,
30 | usernameRowPadding: true,
31 | ),
32 | ),
33 | SizedBox(
34 | height: MediaQuery.of(context).padding.bottom,
35 | ),
36 | ],
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/types/extensions/color.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Credit to: Natesh bhat
4 | /// Taken from: https://stackoverflow.com/a/61186997/11440449
5 | extension ColorStuff on Color {
6 | /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
7 | String toHex({bool leadingHashSign = false, bool withAlpha = false}) =>
8 | '${leadingHashSign ? '#' : ''}'
9 | '${withAlpha ? alpha.toRadixString(16).padLeft(2, '0') : ''}'
10 | '${red.toRadixString(16).padLeft(2, '0')}'
11 | '${green.toRadixString(16).padLeft(2, '0')}'
12 | '${blue.toRadixString(16).padLeft(2, '0')}';
13 |
14 | /// Darken and lighten from: https://stackoverflow.com/a/60191441
15 | /// Darken a color by [percent] amount (100 = black)
16 | Color darken([int percent = 10]) {
17 | assert(1 <= percent && percent <= 100);
18 | var f = 1 - percent / 100;
19 | return Color.fromARGB(this.alpha, (this.red * f).round(),
20 | (this.green * f).round(), (this.blue * f).round());
21 | }
22 |
23 | /// Lighten a color by [percent] amount (100 = white)
24 | Color lighten([int percent = 10]) {
25 | assert(1 <= percent && percent <= 100);
26 | var p = percent / 100;
27 | return Color.fromARGB(
28 | this.alpha,
29 | this.red + ((255 - this.red) * p).round(),
30 | this.green + ((255 - this.green) * p).round(),
31 | this.blue + ((255 - this.blue) * p).round());
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @main
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 |
11 | /**
12 | Was initially added manually to enable ProMotion (partially)
13 | Is now supported natively by Flutter
14 | */
15 |
16 | // Check whether it's iOS 15.0 or above (since this API is only available there)
17 | // if #available(iOS 15.0, *) {
18 | // // Instance of CADisplayLink which exposes several frame rate related options
19 | // let displayLink = CADisplayLink(target: self, selector: #selector(step))
20 | // // Set the preferred range of frames (in this case "High-impact animations" based on the documentation)
21 | // displayLink.preferredFrameRateRange = CAFrameRateRange(minimum:80, maximum:120, preferred:120)
22 | // // Enable it by adding it to the main runloop
23 | // displayLink.add(to: .current, forMode: .default)
24 | // }
25 |
26 | GeneratedPluginRegistrant.register(with: self)
27 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
28 | }
29 | @objc func step(displaylink: CADisplayLink) {
30 | // Will be called once a frame has been built while matching desired frame rate
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/lib/stores/views/logs.dart:
--------------------------------------------------------------------------------
1 | import 'package:mobx/mobx.dart';
2 | import '../../types/enums/order.dart';
3 |
4 | import '../../models/enums/log_level.dart';
5 |
6 | part 'logs.g.dart';
7 |
8 | enum AmountLogEntries {
9 | Ten,
10 | TwentyFive,
11 | Fifty,
12 | }
13 |
14 | extension AmountLogEntriesFunctions on AmountLogEntries {
15 | int get number => {
16 | AmountLogEntries.Ten: 10,
17 | AmountLogEntries.TwentyFive: 25,
18 | AmountLogEntries.Fifty: 50,
19 | }[this]!;
20 | }
21 |
22 | class LogsStore = _LogsStore with _$LogsStore;
23 |
24 | abstract class _LogsStore with Store {
25 | @observable
26 | LogLevel? logLevel;
27 |
28 | @observable
29 | DateTime? fromDate;
30 |
31 | @observable
32 | DateTime? toDate;
33 |
34 | @observable
35 | AmountLogEntries? amountLogEntries = AmountLogEntries.Ten;
36 |
37 | @observable
38 | Order filterOrder = Order.Descending;
39 |
40 | @action
41 | void setLogLevel(LogLevel? logLevel) => this.logLevel = logLevel;
42 |
43 | @action
44 | void setFromDate(DateTime? fromDate) => this.fromDate = fromDate;
45 |
46 | @action
47 | void setToDate(DateTime? toDate) => this.toDate = toDate;
48 |
49 | @action
50 | void setAmountLogEntries(AmountLogEntries? amountLogEntries) =>
51 | this.amountLogEntries = amountLogEntries;
52 |
53 | @action
54 | void toggleFilterOrder() => this.filterOrder =
55 | this.filterOrder == Order.Ascending ? Order.Descending : Order.Ascending;
56 | }
57 |
--------------------------------------------------------------------------------
/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 | import connectivity_plus
9 | import file_selector_macos
10 | import in_app_purchase_storekit
11 | import network_info_plus
12 | import package_info_plus
13 | import path_provider_foundation
14 | import share_plus
15 | import url_launcher_macos
16 | import wakelock_plus
17 | import webview_flutter_wkwebview
18 |
19 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
20 | ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
21 | FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
22 | InAppPurchasePlugin.register(with: registry.registrar(forPlugin: "InAppPurchasePlugin"))
23 | NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin"))
24 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
25 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
26 | SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
27 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
28 | WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockPlusMacosPlugin"))
29 | FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin"))
30 | }
31 |
--------------------------------------------------------------------------------
/macos/Podfile:
--------------------------------------------------------------------------------
1 | platform :osx, '10.15'
2 |
3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
5 |
6 | project 'Runner', {
7 | 'Debug' => :debug,
8 | 'Profile' => :release,
9 | 'Release' => :release,
10 | }
11 |
12 | def flutter_root
13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
14 | unless File.exist?(generated_xcode_build_settings_path)
15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
16 | end
17 |
18 | File.foreach(generated_xcode_build_settings_path) do |line|
19 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
20 | return matches[1].strip if matches
21 | end
22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
23 | end
24 |
25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
26 |
27 | flutter_macos_podfile_setup
28 |
29 | target 'Runner' do
30 | use_frameworks!
31 | use_modular_headers!
32 |
33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
34 | end
35 |
36 | post_install do |installer|
37 | installer.pods_project.targets.each do |target|
38 | flutter_additional_macos_build_settings(target)
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/utils/authentication_helper.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:crypto/crypto.dart';
4 | import 'package:obs_blade/types/classes/session.dart';
5 |
6 | import '../models/connection.dart';
7 | import '../types/enums/web_socket_codes/web_socket_op_code.dart';
8 |
9 | class AuthenticationHelper {
10 | /// This is the content of the auth field which is needed to correctly
11 | /// authenticate with the OBS WebSocket if a password has been set
12 | static String _getAuthRequestContent(Connection connection) {
13 | String secretString = '${connection.pw}${connection.salt}';
14 | Digest secretHash = sha256.convert(utf8.encode(secretString));
15 | String secret = const Base64Codec().encode(secretHash.bytes);
16 |
17 | String authResponseString = '$secret${connection.challenge}';
18 | Digest authResponseHash = sha256.convert(utf8.encode(authResponseString));
19 | String authResponse = const Base64Codec().encode(authResponseHash.bytes);
20 |
21 | return authResponse;
22 | }
23 |
24 | static void identify(Session activeSession) {
25 | activeSession.socket.sink.add(
26 | jsonEncode(
27 | {
28 | 'op': WebSocketOpCode.Identify.identifier,
29 | 'd': {
30 | 'rpcVersion': 1,
31 | 'authentication': AuthenticationHelper._getAuthRequestContent(
32 | activeSession.connection),
33 | 'eventSubscriptions': 131071
34 | }
35 | },
36 | ),
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/types/classes/api/input.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'input.dart';
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | _$InputImpl _$$InputImplFromJson(Map json) => _$InputImpl(
10 | inputKind: json['inputKind'] as String?,
11 | inputName: json['inputName'] as String?,
12 | unversionedInputKind: json['unversionedInputKind'] as String?,
13 | inputVolumeMul: (json['inputVolumeMul'] as num?)?.toDouble(),
14 | inputVolumeDb: (json['inputVolumeDb'] as num?)?.toDouble(),
15 | inputLevelsMul: (json['inputLevelsMul'] as List?)
16 | ?.map((e) => InputChannel.fromJson(e as Map))
17 | .toList(),
18 | syncOffset: (json['syncOffset'] as num?)?.toInt(),
19 | inputMuted: json['inputMuted'] as bool? ?? false,
20 | );
21 |
22 | Map _$$InputImplToJson(_$InputImpl instance) =>
23 | {
24 | 'inputKind': instance.inputKind,
25 | 'inputName': instance.inputName,
26 | 'unversionedInputKind': instance.unversionedInputKind,
27 | 'inputVolumeMul': instance.inputVolumeMul,
28 | 'inputVolumeDb': instance.inputVolumeDb,
29 | 'inputLevelsMul': instance.inputLevelsMul,
30 | 'syncOffset': instance.syncOffset,
31 | 'inputMuted': instance.inputMuted,
32 | };
33 |
--------------------------------------------------------------------------------
/lib/stores/shared/tabs.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:mobx/mobx.dart';
3 | import '../../utils/routing_helper.dart';
4 |
5 | part 'tabs.g.dart';
6 |
7 | class TabsStore = _TabsStore with _$TabsStore;
8 |
9 | abstract class _TabsStore with Store {
10 | @observable
11 | Tabs activeTab = Tabs.Home;
12 |
13 | Map> navigatorKeys = {};
14 | Map activeRoutePerNavigator = {};
15 |
16 | /// Might later be used as an indicator which says that the user tapped
17 | /// on a tab which is already active and in our views we can react to
18 | /// this (via MobX reaction) to do stuff like scroll up, open search etc.
19 | /// Right now not used since tapping on the current active tab will either
20 | /// pop the route if canPop() returns true or scroll up if its the root view
21 | /// of the tab and it attached the ScrollController (each tab gets a
22 | /// ScrollController as a navigation argument - used or not)
23 | @observable
24 | bool performTabClickAction = false;
25 |
26 | GlobalKey keyForCurrentTab() =>
27 | this.navigatorKeys[this.activeTab]!;
28 |
29 | String activeRouteForCurrentTab() =>
30 | this.activeRoutePerNavigator[this.activeTab]!;
31 |
32 | @action
33 | void setActiveTab(Tabs activeTab) => this.activeTab = activeTab;
34 |
35 | @action
36 | void setPerformTabClickAction(bool performTabClickAction) =>
37 | this.performTabClickAction = performTabClickAction;
38 | }
39 |
--------------------------------------------------------------------------------
/lib/views/settings/dashboard_customisation/order/widgets/scene_buttons_preview.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:obs_blade/shared/animator/selectable_box.dart';
3 |
4 | class SceneButtonsPreview extends StatefulWidget {
5 | const SceneButtonsPreview({super.key});
6 |
7 | @override
8 | State createState() => _SceneButtonsPreviewState();
9 | }
10 |
11 | class _SceneButtonsPreviewState extends State {
12 | List _selected = [true, false, false];
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return FittedBox(
17 | child: Row(
18 | children: [
19 | SelectableBox(
20 | text: '',
21 | selected: _selected[0],
22 | onTap: () => setState(() {
23 | _selected = [true, false, false];
24 | }),
25 | ),
26 | const SizedBox(width: 12.0),
27 | SelectableBox(
28 | text: '',
29 | selected: _selected[1],
30 | onTap: () => setState(() {
31 | _selected = [false, true, false];
32 | }),
33 | ),
34 | const SizedBox(width: 12.0),
35 | SelectableBox(
36 | text: '',
37 | selected: _selected[2],
38 | onTap: () => setState(() {
39 | _selected = [false, false, true];
40 | }),
41 | ),
42 | ],
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | platform :ios, '13.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/shared/general/base/adaptive_dialog/adaptive_dialog_action.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class AdaptiveDialogAction extends StatelessWidget {
5 | final Widget child;
6 |
7 | final bool isDestructiveAction;
8 | final bool isDefaultAction;
9 |
10 | final void Function()? onPressed;
11 |
12 | const AdaptiveDialogAction({
13 | super.key,
14 | required this.child,
15 | this.isDefaultAction = false,
16 | this.isDestructiveAction = false,
17 | this.onPressed,
18 | });
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return switch (Theme.of(context).platform) {
23 | TargetPlatform.iOS || TargetPlatform.macOS => CupertinoDialogAction(
24 | onPressed: this.onPressed,
25 | isDefaultAction: this.isDefaultAction,
26 | isDestructiveAction: this.isDestructiveAction,
27 | child: this.child,
28 | ),
29 | _ => TextButton(
30 | onPressed: this.onPressed,
31 | style: this.isDestructiveAction
32 | ? ButtonStyle(
33 | foregroundColor: const MaterialStatePropertyAll(
34 | CupertinoColors.destructiveRed),
35 | overlayColor: MaterialStatePropertyAll(
36 | CupertinoColors.destructiveRed.withOpacity(0.1),
37 | ),
38 | )
39 | : null,
40 | child: this.child,
41 | ),
42 | };
43 | }
44 | }
45 |
--------------------------------------------------------------------------------